为什么你的 Agent 演示很惊艳,上线两周就没人用了
我们复盘了失败的落地项目,原因几乎从不是模型能力不够。真正杀死项目的是四件不起眼的事。
只看每百万 token 的单价会严重低估成本。缓存、重试、上下文膨胀和失败请求才是账单上的大头。
The full text of this article is available in Chinese. For an English summary or to discuss anything in it, email hello@plus1compute.com.
选供应商的时候,大家习惯把各家的「每百万 token 单价」列成一张表比较。 这张表几乎总是会得出错误的结论,因为单价只决定了账单的一部分, 而且往往不是最大的那部分。
下面是我们在实际项目里核算成本时会看的六项,按容易被忽略的程度排序。
直觉上大家会关注输出价格,因为输出单价通常是输入的 3–4 倍。 但在企业 RAG 场景里,输入输出的比例经常是 20:1 甚至 50:1—— 你塞进去 8000 token 的检索结果,模型回你 300 token。
假设输入输出比例为 8000:300,输入单价为 a、输出单价为 4a, 则输入成本占总成本约 87%。这意味着优化提示词长度的收益, 通常远大于换一个输出更便宜的模型。
如果你的系统提示词很长(企业场景里两三千 token 很常见), 而且每次请求都重复发送,那么开启前缀缓存后这部分的成本通常能降到 1/5 到 1/10。
关键在于把稳定的内容放在提示词最前面:系统指令、 业务规则、few-shot 示例放前面,用户问题和检索结果放后面。 很多团队顺序写反了,缓存完全命中不了,白白多付几倍的钱。
这是最容易被漏掉的一项。一个配置了「失败重试 3 次」的客户端, 在供应商抖动的时候可能把某些请求的成本乘以 4。更糟的是流式请求中断—— 已经生成的 token 通常照算,但你什么也没拿到。
我们见过一个项目的月账单里有 11% 来自最终失败的请求。
这个数字在没有把 retry_count 记进日志之前,没有人知道。
# 把 usage 记进日志,按场景聚合。没有这一步就没法优化。
r = client.chat.completions.create(model=m, messages=msgs)
log.info("llm_call", extra={
"scene": scene_id, # 哪个业务场景
"model": m,
"prompt_tokens": r.usage.prompt_tokens,
"output_tokens": r.usage.completion_tokens,
"cached_tokens": getattr(r.usage, "prompt_tokens_cached", 0),
"retry_count": attempt, # 重试也要花钱
"latency_ms": elapsed_ms,
})多轮对话场景里,如果你每轮都把完整历史发回去,成本随轮次呈平方级增长。 一个 10 轮的会话,最后一轮要带 9 轮历史,累计发送的 token 是单轮的 55 倍。
常规做法是滑动窗口 + 摘要压缩:保留最近 3–5 轮原文, 更早的内容压成一段摘要。实现成本不高,但在客服这类高频场景里能省掉大半账单。
不是所有请求都需要旗舰模型。一个典型的客服 Agent 里:
我们做过的项目里,做完分级之后总成本降到原来的 30–40% 是常见结果, 而且因为小模型延迟更低,整体响应速度反而变快了。
日终报表、历史数据清洗、批量内容生成这类不要求实时的任务, 走批量接口通常有明显折扣。判断标准很简单:如果几个小时后拿到结果也可以, 就不该按实时价格付费。
把下面这几行填出来,你就能得到一个比「单价表」可靠得多的估算:
我们在方案阶段会用客户的真实日志跑一遍这个模板,给出一个区间而不是一个数字—— 因为用量本身会波动,给单点估算是不负责任的。