LLM 应用成本工程:单价低不等于总账低,Agent 的 token 账怎么算
LLM 系列又一篇。前面讲了 Agent、记忆、评测、安全——但有个每张账单都在问的问题没正面讲过:成本。这篇把它算清楚。
一、结论先放这儿
| 提示词缓存 | 输入成本砍 5~10 倍 | 零代码,改 API 参数 |
| 模型分级路由 | 单步成本砍 5~20 倍 | 加一个分诊层 |
| 上下文瘦身 | 输入 token 砍一半 | 接记忆系统的压缩策略 |
| 换便宜模型 | 单价砍 10 倍 | 质量要回归验证 |
一句话记住:单价低 ≠ 总账低,缓存才是 Agent 场景的第一省钱杠杆。
二、成本从哪来:Agent 一次任务的 token 账
Agent 和聊天机器人的成本结构完全不同。聊天一次一发,Agent 一个任务连发十几次甚至几十次请求,而且每次都重发全量上下文:
一次 Agent 任务的典型构成(20 轮循环):
系统提示词 + 工具定义 ~3000 token × 20 次
对话历史 + 中间结果 ~4000 token × 20 次(越滚越大)
模型输出 ~500 token × 20 次
─────────────────────────────────────────
输入 ~14 万 token,输出 ~1 万 token
关键事实:Agent 场景输入 token 是输出的 10 倍以上,而输入是按次重发的。 这意味着两件事:
三、第一杠杆:提示词缓存
主流 API 都支持提示词缓存(prompt caching):相同前缀的内容只算一次全价,命中部分按 1/5 到 1/10 计费。Agent 的系统提示词、工具定义每轮都一样——正是缓存的最佳命中区:
# Anthropic 风格:显式标记缓存断点
system = [{
"type": "text",
"text": SYSTEM_PROMPT + TOOLS_DEFINITION,
"cache_control": {"type": "ephemeral"}, # 这段进缓存
}]
# 对话历史追加时,前缀不变 → 第 2 轮起命中
messages.append({"role": "user", "content": user_input})
验证缓存命中,看返回里的 usage 字段:
usage = resp.usage
print(usage.cache_read_input_tokens) # 命中了多少 token(按 1/10 价计)
print(usage.cache_creation_input_tokens) # 第一次写入缓存的 token
怎么实测缓存命中率:构造 1 万 token 的固定 system 上下文,一字不差连发 5 轮,记录每轮 cache_read_input_tokens。有的模型第二轮起稳定命中 98%,有的间歇命中,有的全程 0%——同一个网关下不同模型缓存表现天差地别,跑 Agent 前先测这个。测试方法照抄系列评测篇:固定输入、多轮、只信 usage 字段的数。
四、第二杠杆:模型分级路由
不是每一步都值得用旗舰模型。Agent 循环里的步骤分三六九等:
| 意图分诊、分类、判断 | 小模型/专门模型 | 系列第十篇 Jev 就是干这个的,输出免费 |
| 工具参数生成、格式化 | 中档模型 | 格式类任务便宜模型够用 |
| 复杂推理、代码生成 | 旗舰模型 | 只有这步配得上单价 |
def route(task_type: str, prompt: str) -> str:
if task_type in ("classify", "triage", "check"):
return call_cheap_model(prompt) # 分诊类:便宜模型
if task_type in ("codegen", "reason"):
return call_flagship_model(prompt) # 硬活:旗舰
return call_mid_model(prompt)
分诊判断这类步骤占 Agent 调用量的 60% 以上,把它们挪到便宜模型上,总账直接砍一半起步。具体路由实现(Jev 的 Choice/Noul、或小模型分类)系列第十篇讲过,原样接上。
五、第三杠杆:上下文瘦身
输入 token 越滚越大是 Agent 的常态,瘦身三板斧(都是系列讲过的零件):
这三件事就是系列第六篇 Harness「上下文管理」部件的完整版——Harness 管的不只是权限,还有 token 预算。
六、算账:一个可抄的成本核算脚本
def task_cost(rounds: int, ctx_tokens: int, out_tokens: int,
price_in: float, price_out: float,
cache_hit_ratio: float = 0.0, cache_discount: float = 0.1) -> dict:
"""算一次 Agent 任务的 token 账(单位:元/百万token)"""
hit, miss = cache_hit_ratio, 1 – cache_hit_ratio
in_cost = rounds * ctx_tokens * (hit * price_in * cache_discount
+ miss * price_in) / 1e6
out_cost = rounds * out_tokens * price_out / 1e6
return {"input": round(in_cost, 4), "output": round(out_cost, 4),
"total": round(in_cost + out_cost, 4)}
# 同一个任务:20 轮,每轮重发 7000 token 上下文
no_cache = task_cost(20, 7000, 500, 8, 28) # 不走缓存
with_cache = task_cost(20, 7000, 500, 8, 28, cache_hit_ratio=0.95)
print(no_cache, with_cache)
# {'input': 1.12, …} → 缓存后输入 0.19:同一任务差 5 倍
把 price_in/out 换成你所用模型的实际定价、cache_hit_ratio 换成实测命中率(第三节的方法),每个任务跑之前先算账,跑完对账单——没有账本的优化和没有评测集的调优一样,都是玄学(系列评测篇的老话)。
七、别省过头:成本回归
省钱优化的最大风险是把质量省没了:换便宜模型、砍上下文、压缩历史,每一步都可能劣化。所以成本优化和功能开发一样要回归验证:
八、踩坑提醒
总结
| 成本结构 | Agent 输入是输出的 10 倍+,省钱主战场在输入侧 |
| 第一杠杆 | 提示词缓存,输入砍 5~10 倍,零代码 |
| 第二杠杆 | 模型分级路由,分诊类步骤占 6 成调用量 |
| 第三杠杆 | 上下文瘦身:压缩历史、裁剪工具结果 |
| 铁律 | 单价低 ≠ 总账低,先算账再优化,改完跑回归 |
至此系列的隐形主线浮出水面:Harness 管预算、记忆管压缩、Jev 管分诊、评测管回归——全都是成本工程的零件。 大模型应用的胜负手从来不只是效果,还有每百万 token 的账。觉得有用点个关注。
网硕互联帮助中心




评论前必须登录!
注册