TOON for LLMs:基准性能分析

发布: (2025年12月27日 GMT+8 23:36)
15 min read
原文: Dev.to

I’m happy to translate the article for you, but I’ll need the full text of the post (the content you’d like translated). Could you please paste the article’s body here? Once I have the text, I’ll provide a Simplified‑Chinese translation while preserving the original formatting, markdown, and any code blocks.

每次使用 JSON 的 API 调用都比你想象的更耗费

我使用 Gemini 2.5 Flash 进行真实场景的抽取实验,结果令人震惊:JSON 的输出 token 数始终比 TOON 格式多 30–40 %。在一次测试中,JSON 消耗了 471 个输出 token,而 TOON 只用了 227——降低了 51 %

但有趣的地方在于: TOON 最初的失败率高达 70 %。

经过优化后,我实现了 100 % 的解析成功率,并发现了一个违背直觉的事实——TOON 实际上 整体上为你省钱,因为在需要可靠解析时它使用的提示 token 更少。当我使用 Pydantic 模型测试结构化输出时,JSON 需要 389 个输出 token,而 TOON 的编码方式更为简洁。

隐藏的金矿? 工具/函数调用

这正是 TOON 紧凑格式发挥最大优势的地方,在响应会成为下一个提示的代理工作流中,大幅削减 token 成本。

这并非理论推测。下面展示了实际的提示、解析错误、token 计数以及将 TOON 从 70 % 失败率提升到可投入生产的代码。TOON 是否优于 JSON 取决于你的使用场景——而我拥有的数据可以准确证明何时适用。

让我们拆解这些数字

实验 #1 – 初始 TOON 失败(成功率 70 %)

我从一个直接的测试开始:使用 TOON 而不是 JSON 提取结构化的职位描述数据。

设置

我的提示很简单——让 Gemini 2.5 Flash 提取 角色、技能、经验、地点和职责。对于输出格式,我使用了看似合乎逻辑的方式:展示 TOON 的编码结构,使用实际的输出格式(本质上是直接替换的方法)。

提示

Extract Role, Primary Skills, Secondary Skills,
Minimum Experience, Maximum Experience,
Location, Employment Type, Summary, and Responsibilities

Job Description:

Output in TOON format:

Role: ""
"Primary Skills"[2]: Python,JavaScript
"Secondary Skills"[2]: Responsibility,Communication
"Minimum Experience": ""
"Maximum Experience": ""
Location: ""
"Employment Type": ""
Summary: ""
Responsibilities[2]: Task A,Task B

我的预期: 通过展示带有空字符串和通用占位符的编码格式,模型会理解结构。

现实检查 – 70 % 失败率

错误信息很直观:

  • Error parsing TOON format for JD#2: Expected 10 values, but got 16
  • Error parsing TOON format for JD#5: Missing colon after key

模型对数组感到困惑。有时它会输出 Skills: Python, JavaScript, React 作为平铺字符串;有时它尝试使用方括号,但语法却弄错了。

假设: 只展示空示例是问题所在。模型需要看到真实的数据模式,尤其是数组的写法。

Token 使用情况(失败尝试,成功率 70 %)

Tokens
Prompt729
Output227
Success Rate~30 % 最初,加入两个带填充值的真实示例后提升至 70 %

JSON Token 使用情况(相同测试)

Tokens
Prompt723
Output471

关键洞察

TOON 的紧凑语法非常不宽容。JSON 的冗余({"key":"value"})帮助模型自我纠正。TOON 的 Key: value 格式没有这种安全网,因此模型需要具体示例,而不是抽象模板。

但 70 % 的成功率仍不足以投入生产。是时候彻底解决这个问题了。


实验 #2 – 实现 100 % 解析成功(以及 Token 的权衡)

我需要解决 70 % 的成功率问题。解决方案是什么?停止使用极简的示例。

修订后的提示

Extract Role, Primary Skills, Secondary Skills,
Minimum Experience, Maximum Experience,
Location, Employment Type, Summary, and Responsibilities

Job Description:

Output in TOON format. Example structure:

Role: "Senior Data Scientist"
Primary_Skills:
  [1]: "Machine Learning"
  [2]: "Statistical Analysis"
Secondary_Skills:
  [0]: "Big Data"
  [1]: "Cloud Platforms"
Minimum_Experience: "5 years"
Maximum_Experience: "10 years"
Location: "New York, NY or Remote"
Employment_Type: "Full-time"
Summary: "Lead data science initiatives"
Responsibilities:
  [0]: "Design ML models"
  [1]: "Analyze datasets"

Now provide the extraction in TOON format. Keep the format exactly as shown above.

结果: 100 % 解析成功。再也没有出现错误的数组,也没有缺失冒号的情况。

代价: 提示变得更重。

Token 对比 – TOON 与 JSON(相同的 10 条职位描述)

方法Prompt TokensOutput TokensTotal TokensSuccess Rate
JSON7234711 194100 %
TOON – 初始(70 % 成功)729227 ✅95670 % ❌
TOON – 优化后(100 % 成功)802 ❌ (+11 % vs JSON)455 ✅ (比 JSON 减少 3.4 %)1 257100 % ✅

不舒服的真相

对于基本的提取任务,优化后的 TOON 成本比 JSON 更高

  • 输出略微更紧凑(455 与 471 个 token)。
  • 为了实现 100 % 可靠性所需的冗长提示会抹去任何节省。
  • 实际上,你每次请求要多付 约 5 %

为什么继续测试 TOON?

因为基准比较具有误导性。实际的 LLM 应用并不仅仅提取一次数据——它们会使用结构化输出来:

  • Pydantic 模型验证(原生 SDK 支持)
  • 工具/函数调用(输出成为输入)
  • 多轮代理工作流

在这些场景中,紧凑输出格式带来的 token 节省可能 相当显著,尤其是当相同的结构化数据被反复传递时。


要点

  • JSON 容错性好,且只需最少提示即可正确生成,但会消耗更多输出 token。
  • TOON 能显著减少输出 token,但你必须在提示中投入更多内容(真实示例、明确的数组语法)以实现可靠的解析。
  • 当结构化数据被 重复使用(例如工具调用、代理循环)时,TOON 紧凑格式带来的 token 节省可以抵消额外的提示成本。

欢迎深入查看我使用的代码片段和 token 计数脚本;它们已包含在下面链接的仓库中。祝你 token 优化愉快!

实验 #3:Pydantic 模型 — SDK 完成繁重工作

这里的情况变得有趣起来。现代 LLM SDK 对使用 Pydantic 模型的结构化输出提供了一流的支持。你不需要进行提示工程,只需定义一个模式,让 SDK 自动处理格式化。

关键区别: 你不必在提示中解释输出格式——SDK 会自动从你的 Pydantic 模型中提取它。

环境搭建:Google 的 GenAI SDK

我使用相同的职位提取任务,但这次使用了 Pydantic 模型:

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=prompt,
    config={
        "response_mime_type": "application/json",
        "response_schema": JobModel,
    },
)

注意缺少的内容: 没有输出格式指令、没有示例、没有 “以 JSON 输出并使用这些精确键”。

SDK 在幕后注入了模式。

Become a member – The SDK injects the schema behind the scenes.

令牌比较:Pydantic JSON vs. 手动 TOON

指标Pydantic + JSON(SDK 管理)手动 TOON(实验 #2)
提示令牌647 ✅(比优化后的 TOON 少 19.3%)802 ❌
输出令牌389 ✅(比优化后的 TOON 少 14.5%)455 ❌
成功率100 % ✅100 % ✅
解析方式原生(SDK 返回已类型化的 Python 对象)自定义(需要自行编写解析器)

残酷的结论

对于拥有强大 SDK 支持的结构化提取,Pydantic 表现出色。原生的 Pydantic 集成带来:

  • ✅ 更简洁的提示(约减少 155 条提示令牌)
  • ✅ 更小的输出(约减少 66 条输出令牌)
  • ✅ 无需自定义解析逻辑
  • ✅ 内置类型校验
  • ✅ 直接返回已解析的对象,随时可用
  • ✅ 开发者体验大幅提升

正因为如此,我将越来越多地依赖 Pydantic 和原生解析支持来进行结构化提取。相较于手动处理解析和校验,它更可靠、更易维护。

注意: 有一种情况会让 JSON 的冗长成为真正的负担:在代理工作流中的工具调用。这时 TOON 才最终显示出它的价值。

Source:

实验 #4:工具调用 — TOON 最终获胜之处

在具备代理能力的工作流中,LLM 不仅仅一次性提取数据——它会调用工具,接收结果,并利用这些结果进一步推理。工具的响应会成为下一个提示的一部分。如果该响应被冗余的 JSON 语法所膨胀,你实际上是为同一信息付了两次费用:一次作为输出,一次作为输入。

洞察: 工具结果本质上是纯粹的 token 浪费。模型不需要 {"key": "value"} 这种仪式感——它只需要数据,并且要高效编码。

设置:带函数调用的天气代理

我构建了一个简单的代理,它调用 get_current_weather 函数。用户询问天气,模型调用工具,函数返回数据,模型再合成回复。

版本 A:JSON 工具响应

data = {
    "location": location,
    "current": {
        "temperature": "72 F",
        "condition": "sunny",
    },
    "forecast": forecast,
}

return json.dumps(data)   # Returns JSON string

版本 B:TOON 工具响应

data = {
    "location": location,
    "current": {
        "temperature": "72 F",
        "condition": "sunny",
    },
    "forecast": forecast,
}

return encode(data)        # Returns TOON‑encoded string

主代码

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="What is the weather like in New York? Share next 15 days forecast as well.",
    config=types.GenerateContentConfig(
        tools=[get_current_weather],
    ),
)

结果 Token 使用情况

  • 初始提示 token: 152(用户消息 + 工具定义)
  • 工具响应 token(作为输入): 480 ✅(降低 24 %)
  • 模型最终输出: 384(略长,但仍在合理范围)
  • 总 token: 1,016 ✅(整体降低 11.5 %)

为什么 TOON 在代理工作流中占优势

单次工具调用

方法工具结果所需 token
JSON632
TOON480
节省152 token(24 %)

多轮代理(5 次工具调用)

  • JSON:632 × 5 = 3,160 token
  • TOON:480 × 5 = 2,400 token

节省量: 760 token(24 %)

复利效应

  • 工具结果是纯粹的输入 token — 每次都要为它们付费。
  • 冗余会成倍放大 — JSON 的 {}:, 语法会为嵌套数据额外增加 20‑30 % 的开销。
  • 无解析惩罚 — 模型同样轻松消费 TOON(在后续测试中已验证)。
  • 随代理复杂度线性扩展 — 工具越多,节省越多。

底线

在测试了四种不同情景后,数据告诉我们:

  • TOON 在单次抽取时表现不佳。 无论是手动提示还是使用 Pydantic 模型,带有 SDK 支持的 JSON 更简洁、更便宜且更可靠。原生模式集成带来的 17.6 % 令牌节省始终胜过 TOON 的手动方式。

  • TOON 在对代理有意义的场景中占优势:工具调用工作流。 当 LLM 的输出成为下一个提示——即数据在模型与函数之间反复循环时,TOON 每次工具调用约 24 % 的减少会从好奇心转变为实际的成本节约优势。

简而言之:对直接的结构化抽取使用 Pydantic/JSON,而在任何需要模型反复消费自身工具输出的代理式、工具调用管道中切换到 TOON

# king 20 tool calls saves 3,040 tokens per session

**The decision matrix is simple:**

- **Building a chatbot that extracts structured data?**  
  Use **JSON + Pydantic**.

- **Building an agent that calls tools 10+ times per session?**  
  Test **TOON**.

- **Building anything else?**  
  Profile first, optimize later.

亲自尝试

我已开源所有实验、提示词和 token 测量:
View complete code and results on GitHub Gist

该仓库包括:

  • ✅ 包含所有四个实验设置及实际提示词
  • ✅ 每个测试案例的 token 使用日志
  • ✅ 并排比较脚本
  • ✅ 我用于测试的职位描述

TOON 不是魔法——它是数学。 只有在 token 效率真正重要时,这套数学才有效。对于大多数应用来说,JSON 生态系统的优势超过了节省的 token,但对于 token 消耗巨大的智能体工作流,TOON 可能恰好能抵消其成本。

Back to Blog

相关文章

阅读更多 »

FACET的历史与原理

本文件的目的 本文件记录了 FACET 的历史背景、架构动机以及设计决策背后的理由。它的存在…