如何使用 TOON 格式将 LLM Token 消耗降低 40-50%,显著降低 AI 成本
背景
你是否注意到每个月的 LLM 账单越来越高?
当你开始使用简单的用例时,JSON 似乎完美无缺,但随后账单就出现了。如果你正在发送结构化参考数据(如产品目录、实体列表、参考库),你可能正在支付比实际需要多 40-60% 的费用。
问题的根源在于:JSON 重复字段名称。
问题:JSON 是 Token 杀手
示例:KPI 参考库
假设你需要从可持续发展报告中提取 KPI,需要发送一个参考库让 LLM 知道要查找什么。
标准 JSON 格式:
json
{
"kpi_categories": {
"Environment": [
{
"name": "Total GHG Emissions",
"keywords": ["emissions", "carbon", "CO2"]
},
{
"name": "Renewable Energy Percentage",
"keywords": ["renewable", "clean energy", "solar"]
},
{
"name": "Water Consumption",
"keywords": ["water usage", "consumption", "m3"]
}
]
}
}
Token 消耗:127 tokens(Claude tokenizer)
解决方案:TOON 格式
什么是 TOON?
TOON (Token-Oriented Object Notation) 结合了 YAML 的可读性和 CSV 的紧凑性。
核心创新:数组中的对象只声明一次字段名称,然后逐行列出值。
TOON 格式:
css
kpi_categories:
Environment[3]{name,keywords}:
Total GHG Emissions,"emissions,carbon,CO2"
Renewable Energy Percentage,"renewable,clean energy,solar"
Water Consumption,"water usage,consumption,m3"
Token 消耗:61 tokens
节省 :约 50%!
TOON 语法要点
1. 对象:YAML 风格
yaml
user:
name: Alice
role: admin
2. 原始数组
css
tags[3]:
javascript,python,rust
3. 对象数组(TOON 的亮点)
arduino
products[2]{id,name,price,stock}:
101,Laptop,1200,true
102,Mouse,25,false
TOON 语法解读
products- 字段名[2]- 数组长度(帮助检测截断){id,name,price,stock}- 字段顺序
TOON vs JSON 对比
输入数据
| 格式 | 示例 | Token | 节省 |
|---|---|---|---|
| JSON | [{"id":1,"name":"Alice","role":"admin"}] |
127 | - |
| TOON | users[1]{id,name,role}:1,Alice,admin |
61 | 52% |
输出数据
| 格式 | 10个KPI的Token | 节省 |
|---|---|---|
| JSON | 1,500 | - |
| TOON | 900 | 40% |
💡 提示:输出 Token 通常比输入更贵,所以输出节省同样重要!
实战代码
JSON 转 TOON
python
def to_toon(data):
lines = []
for key, value in data.items():
if isinstance(value, list) and value and isinstance(value[0], dict):
# 对象数组 - 表格形式
fields = ",".join(value[0].keys())
lines.append(f"{key}[{len(value)}]{{{fields}}}:")
for obj in value:
vals = []
for v in obj.values():
if "," in str(v):
vals.append(f'"{v}"')
else:
vals.append(str(v))
lines.append(f" {','.join(vals)}")
else:
lines.append(f"{key}: {value}")
return "\n".join(lines)
TOON 转 JSON
python
def from_toon(toon_str):
lines = toon_str.strip().split("\n")
header = lines[0]
# 从 header 提取字段
fields_start = header.index("{") + 1
fields_end = header.index("}")
fields = header[fields_start:fields_end].split(",")
# 解析行
result = []
for line in lines[1:]:
if line.strip():
values = line.strip().split(",")
obj = {f: v.strip('"') for f, v in zip(fields, values)}
result.append(obj)
return result
使用策略
关键原则
javascript
数据库 → 始终使用 JSON
LLM 提示 → 使用 TOON
LLM 输出 → 使用 TOON
业务逻辑 → 使用 JSON
正确做法
python
def process_with_llm(data_json):
# 业务逻辑使用 JSON
processed = transform(data_json)
# LLM 边界使用 TOON
toon = to_toon(processed)
prompt = f"参考数据:\n{toon}\n请提取..."
# 调用 LLM
response = call_llm(prompt)
# 立即转回 JSON 存储
return from_toon(response)
何时使用 TOON
✅ 使用 TOON
- 向 LLM 发送参考数据集(目录、库、实体列表)
- 从 LLM 接收结构化数组
- 数据均匀且有 4+ 对象和 4+ 字段
- Token 成本高(大量使用、大上下文)
❌ 使用 JSON
- 存储在数据库或调用 API
- 数据深度嵌套或不均匀
- 数组很小(< 3 个对象)
性能基准
| 数据类型 | 节省比例 |
|---|---|
| 混合结构 | 22% |
| 表格数据 | 40-50% |
| 嵌套配置 | 30-35% |
实践建议
1. 用代码块包裹 TOON
python
prompt = f"""
参考表格数据:
{toon_data}
erlang
你的任务是...
"""
2. 展示示例,不要只描述
python
prompt = f"""
返回格式:
```toon
results[N]{{id,name,score}}:
1,Alice,95
2,Bob,87
你的任务:使用上述格式返回结果 """
scss
### 3. 验证结构
```python
def validate(response):
header = response.split("\n")[0]
count = int(header[header.index("[")+1:header.index("]")])
rows = [l for l in response.split("\n")[1:] if l.strip()]
assert len(rows) == count, "输出被截断!"
总结
| 方面 | 说明 |
|---|---|
| 节省 | 40-50% Token 消耗 |
| 原理 | 字段名只声明一次 |
| 适用 | 结构化数据、数组对象 |
| 边界 | JSON 存储,TOON 用于 LLM |
TOON 解决了一个特定问题:JSON 在 LLM 提示中对结构化数据的 Token 效率低下。
更重要的是,TOON 的显式结构通常可以提高 LLM 响应可靠性------更少的格式错误输出,更容易验证。
参考资料
💡 行动建议:如果你在 LLM 上花费大量资金并且处理结构化数据,先从最高成本的提示开始测试 TOON!