TOON 教程:把 JSON 数据喂给模型前,先把 token 账算明白

作者:Administrator 发布时间: 2026-04-29 阅读量:4 评论数:0

很多人做 AI 应用时,第一反应是把业务数据直接塞成 JSON。简单、通用、模型也能读,看起来没毛病。

但一到真实场景,账就不太对了。订单列表、埋点日志、知识库片段、搜索结果、用户画像、商品属性,全都用 JSON 喂给模型,会浪费大量字段名、括号和重复结构。上下文窗口再大,也不能这么霍霍。

TOON 的价值就在这里。它不是要替代 JSON 做系统接口,而是专门面向 LLM prompt 的数据表达方式:保留结构、减少重复、让模型更容易看懂表格型数据。

先看一个最小例子

假设你要让模型根据三条订单判断异常,传统 JSON 可能长这样:

{
  "orders": [
    {
      "id": "o_1001",
      "user": "alice",
      "amount": 89.5,
      "country": "SG",
      "risk": "low"
    },
    {
      "id": "o_1002",
      "user": "bob",
      "amount": 1240.0,
      "country": "BR",
      "risk": "unknown"
    },
    {
      "id": "o_1003",
      "user": "alice",
      "amount": 92.0,
      "country": "SG",
      "risk": "low"
    }
  ]
}

TOON 更像把 schema 和 rows 拆开:

orders[3]{id,user,amount,country,risk}:
  o_1001,alice,89.5,SG,low
  o_1002,bob,1240.0,BR,unknown
  o_1003,alice,92.0,SG,low

模型看到的是同一批字段和多行记录。字段名只出现一次,行数据保持顺序,结构仍然清楚。对订单、日志、用户列表、检索结果这类半表格数据,效果很直接。

什么时候值得用 TOON

不是所有数据都适合 TOON。它最适合三类场景。

第一类是多行同构数据。比如 20 条搜索结果、100 条事件日志、50 个商品属性。字段基本一致,JSON 会反复写字段名,TOON 能省掉很多重复。

第二类是给模型做推理前的候选集。比如让模型从候选客户里找风险最高的 5 个,或者从候选文档片段里挑最相关的。候选集本身需要被阅读,但不需要作为 API 原样传输。

第三类是 Agent 工具返回结果。工具调用得到的是结构化输出,模型下一步只需要理解结果,不一定需要完整 JSON 语法。

不适合的场景也要说清楚。深度嵌套、字段极不稳定、值里大量逗号换行、需要机器严格解析回原对象时,JSON 仍然更稳。TOON 是 prompt 表达层,不是系统数据交换层。

在项目里怎么落地

比较稳的做法是保留内部 JSON,在进入模型前做一次格式转换。这样业务系统、日志、缓存、接口仍然用标准 JSON,只有 prompt payload 变成 TOON。

一个常见流程是:

业务数据库或 API
  -> 标准 JSON 对象
  -> 字段筛选和脱敏
  -> 转换成 TOON
  -> 拼进 prompt
  -> 模型输出结构化答案

注意,中间一定要先做字段筛选。不要把所有字段都丢进去再指望 TOON 省钱。最省 token 的字段,是一开始就不传的字段。

可以先做一个小转换函数:

def orders_to_toon(orders):
    fields = ["id", "user", "amount", "country", "risk"]
    header = "orders[" + str(len(orders)) + "]{" + ",".join(fields) + "}:"
    lines = [header]
    for item in orders:
        row = [str(item.get(field, "")) for field in fields]
        lines.append("  " + ",".join(row))
    return "\n".join(lines)

这段代码不追求覆盖所有边界,只说明原则:字段固定、顺序固定、输出可读。生产环境要补三件事:转义逗号和换行,空值统一表示,字段 schema 写测试。

Prompt 里怎么写更稳

不要只把 TOON 片段孤零零扔给模型。最好在前面加一句数据说明,明确字段含义和任务。

下面是订单候选集,采用 TOON 格式。orders 每行字段依次为 id、user、amount、country、risk。请找出最需要人工复核的订单,并给出理由。

orders[3]{id,user,amount,country,risk}:
  o_1001,alice,89.5,SG,low
  o_1002,bob,1240.0,BR,unknown
  o_1003,alice,92.0,SG,low

这一步很重要。模型不会天然知道你的字段语义,省 token 不能省掉任务解释。

上线前要做一次对照测试

别看到 token 省了就直接上生产。建议拿 30 到 100 条真实样本做 A/B。

一组用 JSON,一组用 TOON。比较四个指标:总 token、答案准确率、字段遗漏率、模型是否误解列顺序。如果准确率不降、token 明显下降,再扩大使用范围。

尤其是金融、法务、医疗、风控这类严肃场景,不要只看一次 demo。字段顺序错一次,可能就把风险判断带歪。

最值得记住的一句话

TOON 不是让你抛弃 JSON,而是提醒你:给模型看的数据,不一定要长得像给机器接口看的数据。

AI 工程里很多成本,不在模型本身,而在上下文组织。把一堆重复 JSON 直接塞进 prompt,看似省事,实际是在拿钱换懒。TOON 这类格式的意义,就是把数据表达这层重新整明白。

字段顺序要写进测试

TOON 依赖列顺序。字段顺序一乱,模型就可能把金额当国家,把风险等级当用户名。

所以转换函数必须有测试。

def test_orders_to_toon_header():
    orders = [{"id": "o1", "user": "ann", "amount": 10, "country": "CN", "risk": "low"}]
    text = orders_to_toon(orders)
    assert text.splitlines()[0] == "orders[1]{id,user,amount,country,risk}:"

不要觉得这是小题大做。省 token 的格式通常更依赖约定,约定就要靠测试守住。

值里有逗号和换行怎么办

真实数据里经常有逗号、引号、换行。简单 join 会出问题。生产环境要做转义或换分隔符。一个保守策略是:对复杂字段先做短摘要,或者把复杂字段单独放成文本块。

tickets[2]{id,type,summary}:
  t_01,bug,login fails after password reset
  t_02,refund,user asks for manual refund review

ticket_details:
  t_01: full error trace stored in evidence block A
  t_02: payment records stored in evidence block B

别为了省 token,把数据搞到不可验证。紧凑和清楚要同时成立。

输入可以紧凑,输出要严格

TOON 适合输入,模型输出仍建议用 JSON Schema 约束。进模型时可以紧凑,出模型时要严格。这样既省 token,又方便后端校验。

评论