← 返回资讯

Agent 为什么这么烧钱:成本结构拆解与六个控制手段

2026-08-07

有个场景我见过太多次了:团队先做了一个「问答式」的功能,跑了一个月,账单在心理预期之内,大家都很满意。然后有人说「这个我们做成 Agent 吧,让它自己查资料、自己调工具、自己修正」。功能确实变强了,两周后财务那边把账单拍过来——同样的业务量,翻了十几倍。

奇怪的是模型没换,单价一分没涨。涨的是调用次数,以及每次调用要塞进去的上下文长度。这两件事一乘,就是数量级的差距。

这篇把 Agent 的成本结构一块一块拆开,讲清楚每块贵在什么机制上、怎么量化,然后按性价比排一遍控制手段。不谈任何厂商的具体价格,所有算例都用假设参数做纯算术演示——你把自己的实际单价乘进去就是自己的账。

如果你还没搞清楚 Agent 到底是怎么跑起来的,建议先看 Agent 应用模式概览工具调用怎么设计,本文默认你知道「一轮 = 一次完整的模型调用」这件事。

一、先把「一次任务」和「一次请求」分开

Agent 成本失控的第一个认知障碍,就是把这两个词混着用。

一问一答里,一次用户请求 = 一次模型调用 = 一笔账。这两者是同一件事,所以你随便看哪个维度都行。

Agent 里,一次用户任务 = N 次模型调用 = N 笔账。而且这 N 笔的单价虽然一样,token 量却是逐轮递增的。你在计费面板上看到的是 N 条便宜的请求记录,每条都不吓人,但它们属于同一个任务。

这就是为什么很多团队明明在看监控,却完全没预警到成本爆炸——监控的聚合维度错了。这一点后面第五节会展开讲,现在先记住:本文说「成本」时,单位永远是「每任务」,不是「每请求」。

二、成本结构拆解:五块,各有各的机制

2.1 轮次膨胀:每一轮都是完整计费

Agent 的基本循环是「思考 → 调工具 → 看结果 → 再思考」。每次「思考」都是一次独立的、完整的模型调用,按当时的输入 token 加输出 token 计费。没有任何折扣是因为「这是同一个任务的第三轮」。

一个查资料类的任务,轮次通常在 3 到 8 之间;一个要改代码、跑测试、看报错、再改的任务,十几轮很常见。轮次是成本的第一个乘数

怎么量化:在你的调用日志里给每次调用打上 task_id,然后统计每个任务的调用条数分布。你要看的不是均值,是 P90 和 P99——均值 4 轮、P99 却是 35 轮的系统,钱是被那个尾巴吃掉的。

2.2 上下文累积:这是最大的一块

这块必须讲透,因为它是 Agent 和普通对话在成本结构上的本质区别

模型调用是无状态的。第三轮要让模型知道前两轮干了什么,唯一的办法是把前两轮的内容重新完整发一遍。所以第 k 轮的输入长度大致是:

输入(k) = 固定前缀 + Σ(前 k-1 轮的模型输出 + 前 k-1 轮的工具返回结果)

注意「工具返回结果」这一项。模型自己每轮输出的 token 通常不多(几十到几百),但工具返回的东西可能非常大:一次搜索返回五条摘要、一次数据库查询返回三十行、一次文件读取返回整个文件。这些观察结果会原封不动地躺在上下文里,被后面每一轮反复重发。

一个 600 token 的搜索结果,如果它出现在第 2 轮,那么第 3、4、5、6 轮的输入里都有它。它被计费了五次。

2.3 工具定义常驻:挂得越多越贵

工具的 schema——名字、描述、每个参数的类型和说明——每一轮都要放在输入里。模型必须看到它才知道自己有什么能力。

单个工具的定义写得规范一点,一两百 token 很正常。挂 5 个工具就是几百到一千 token,挂 20 个就是几千。这几千 token 每一轮都在重发

我见过一种很常见的偷懒做法:把系统里所有能力做成一个大工具包,所有 Agent 都挂全量。写起来是省事了,代价是每个任务的每一轮都在为它永远用不到的十几个工具付钱。

2.4 失败重试与自我纠错:钱花得静悄悄

这一块阴在「它是自动的」。

工具调用失败了,Agent 会自己再试一次。参数格式错了,模型看到报错会自己改参数重来。这些在功能上是优点,在成本上是没有人按下同意按钮的额外支出

而且重试是叠加在上下文累积之上的:失败的那一轮不会从历史里消失,它连同错误信息一起留在上下文里,继续被后面每轮重发。一次失败重试的实际代价,是「多一轮调用」加上「所有后续轮次的输入都变长」。

关于重试本身怎么算账,重试的隐性成本 那篇讲得更细,这里不重复。

2.5 跑偏与死循环:预算杀手

前四块是可以估算的,这一块不能——它是长尾的。

典型的失控形态有几种:模型陷入「调工具 → 结果不满意 → 换个参数再调」的循环;模型判断任务已完成又不放心,再调一次工具「确认一下」;两个动作互相触发形成振荡。

没有硬性上限的 Agent,理论上可以一直跑到上下文塞满为止。而上下文快塞满的时候,恰恰是每一轮最贵的时候。一个跑飞的任务,成本可能是正常任务的几十倍,而且它通常是在半夜、在你没盯着的时候发生的。

三、算一遍:一问一答 vs 5 轮 Agent

下面全部是假设参数,用于演示算术结构,不代表任何真实系统的实测值。请把它当成一个可以套自己数字的模板。

假设参数:

项目假设值
系统提示500 token
工具定义(8 个 × 150)1200 token
用户任务描述300 token
每轮模型输出(思考 + 工具调用参数)150 token
每轮工具返回的观察结果600 token
最终答案输出500 token

场景 A:一问一答

输入 = 系统提示 500 + 用户问题 300 = 800 token 输出 = 500 token

场景 B:5 轮 Agent

固定前缀 = 500 + 1200 + 300 = 2000 token 每轮结束后,历史增长 = 150(模型输出)+ 600(观察结果)= 750 token

轮次输入 token说明
第 1 轮2000只有固定前缀
第 2 轮2750+1 轮历史
第 3 轮3500+2 轮历史
第 4 轮4250+3 轮历史
第 5 轮5000+4 轮历史
合计17500

输出 = 前 4 轮各 150 + 最后一轮最终答案 500 = 600 + 500 = 1100 token

对比:

  • 输入:17500 ÷ 800 = 21.9 倍
  • 输出:1100 ÷ 500 = 2.2 倍
  • 若输入输出单价相同(只是为了得到一个总量比值):(17500 + 1100) ÷ (800 + 500) = 18600 ÷ 1300 ≈ 14.3 倍

实际单价通常输出高于输入,具体倍率以你所用服务的官方价格表为准。但结论方向不变:膨胀主要发生在输入侧

把「重复重发」那部分单独拎出来

17500 token 的输入里,真正的「新信息」只有多少?

  • 第 1 轮的固定前缀:2000(首次,必须付)
  • 后 4 轮各新产生 750 的历史:750 × 4 = 3000(这些内容首次进入上下文,也必须付)
  • 新信息合计 = 5000

剩下的 17500 − 5000 = 12500 token 是重复重发,占总输入的 12500 ÷ 17500 ≈ 71.4%

拆得更细一点:固定前缀被完整重发了 4 次 = 2000 × 4 = 8000;历史内容的重复重发 = 4500。这 8000 是有办法用缓存前缀打掉的,后面会讲。

为什么是二次方趋势

把轮次记为 N,固定前缀记为 F,每轮历史增量记为 G,总输入是:

总输入 = N × F + G × N(N-1)/2

第一项随 N 线性增长,第二项里的 N(N-1)/2N 的二次项。轮次一多,第二项就会盖过第一项。

代入验证:N=5 时,5 × 2000 + 750 × 10 = 10000 + 7500 = 17500 ✓

看看轮次翻倍会发生什么。N=10:

10 × 2000 + 750 × (10 × 9 ÷ 2) = 20000 + 750 × 45 = 20000 + 33750 = 53750

轮次从 5 到 10 涨了 2 倍,输入从 17500 到 53750 涨了约 3.07 倍

再看一个失控的例子,N=40(比如你设了一个很宽松的上限,或者根本没设):

40 × 2000 + 750 × (40 × 39 ÷ 2) = 80000 + 750 × 780 = 80000 + 585000 = 665000

轮次从 5 到 40 是 8 倍,输入从 17500 到 665000 是 38 倍。相对最初那个一问一答的 800 token 输入,是 831 倍

这就是为什么下一节里,硬性上限排在第一位。它不是「一个不错的优化」,它是止损开关。

四、控制手段,按性价比排序

排序依据是「投入的工程量」除以「省下的钱 + 避免的尾部风险」。前两条几乎是白送的,越往后成本收益比越低、实现越麻烦。

4.1 硬性上限:没有它其他都白搭

三个维度都要设,缺一不可:

  • 最大轮次:单个任务允许的模型调用次数上限,到了就强制终止并返回当前最好的结果
  • 最大总 token:单任务累计输入 + 输出的天花板,防止「轮次不多但每轮巨大」的情况
  • 单任务预算:折算成金额的上限,这是给业务方看的那个数

为什么它排第一:前面算过,失控任务的成本是长尾的,一个跑飞的任务能吃掉几百个正常任务的预算。其他所有优化手段都是在优化正常任务的成本,只有硬上限管得住不正常的那部分。而正常任务优化个 30% 是加法,失控任务是乘法。

实现上要注意:上限触发时不要静默失败。要记日志、要报警、要能回溯是哪个任务、卡在哪一步。上限被频繁触发本身就是一个信号——要么任务太难,要么 Agent 的终止判据有问题。

具体的预算折算方法,可以配合 月度预算怎么估 那篇里的口径一起做。

4.2 精简工具集:按任务类型挂载

沿用前面的假设。把工具从 8 个减到 3 个,工具定义从 1200 降到 450,固定前缀从 2000 降到 1250。

N=5 时:5 × 1250 + 7500 = 6250 + 7500 = 13750

相比 17500 省了 3750,降幅 3750 ÷ 17500 ≈ 21.4%

改一行配置换 20% 出头的输入降幅,这是全场性价比最高的一条。做法是按任务类型分组挂载:查询类任务只给检索工具,写入类任务才给写工具。如果你的系统只有一个入口不好分类,退一步的做法是让第一轮先做一次意图判断再决定挂载哪组——多花一轮,但如果工具池很大,这一轮是划算的。

顺带一个副作用:工具少了,模型选错工具的概率也低了,间接减少了重试轮次。

4.3 压缩历史:只留结论,不留全文

把每轮 600 token 的观察结果,摘要成 150 token 的关键结论再放进历史。每轮历史增量从 750 降到 300。

N=5 时:5 × 2000 + 300 × (5 × 4 ÷ 2) = 10000 + 300 × 10 = 10000 + 3000 = 13000

相比 17500 省 4500,降幅 ≈ 25.7%

和 4.2 叠加使用: 5 × 1250 + 3000 = 6250 + 3000 = 9250,相比 17500 降幅 ≈ 47.1%。两条加起来,输入砍掉将近一半。

实操上有几个要点:

  • 长结果先摘要再入上下文,别等到上下文满了再回头压缩。压缩本身是要花钱的(多一次调用),但摘要用小模型做就行
  • 摘要要保留「可追溯的引用」,比如结果 ID、行号、URL,让 Agent 需要时能重新取回全文,而不是把信息彻底丢了
  • 有些内容不能压:任务目标、硬性约束、上一轮的错误信息。这些压掉会直接导致 Agent 重复犯错,省下的钱又以重试的形式还回去
  • 轮次越多,这条越值钱。3 轮以内的任务基本不用做

关于历史怎么存、怎么取,多轮对话怎么管上下文 那篇有更细的结构设计。

4.4 分层模型:规划用强的,执行用小的

Agent 的轮次里,真正需要强推理能力的通常只有两类:最开始的任务分解,和最后的结果综合。中间那些「拿到工具结果、判断够不够、决定下一步调哪个」的轮次,往往是格式化程度很高的判断,小模型足够。

按前面的假设,第 1 轮(2000)和第 5 轮(5000)用强模型 = 7000 token,占总输入的 40%;中间三轮 2750 + 3500 + 4250 = 10500,占 60%,交给小模型。

省多少完全取决于两档模型的价差,这个以你所用服务的官方价格表为准,我不给数字。但结构上你能看出来:60% 的输入量可以降档

代价是工程复杂度上来了:要维护两套提示、要处理两个模型输出格式的差异、要在切换点上做兼容。如果你的 Agent 还在快速迭代,这条建议往后放。

4.5 缓存前缀:把稳定部分放在最前面

前面算过,固定前缀在 5 轮里被完整重发了 4 次,共 8000 token,占总输入的 8000 ÷ 17500 ≈ 45.7%。这部分内容每一轮都完全一样,是缓存的理想对象。

要吃到缓存价,有一条铁的排布原则:按「稳定性从高到低」组织上下文

[ 系统提示 ]        ← 几乎不变,最前
[ 工具定义 ]        ← 同一类任务内不变
[ 任务描述 ]        ← 单个任务内不变
[ 历史动作与观察 ]  ← 每轮增长,最后

前缀缓存的匹配是从头开始的,中间任何一个 token 变了,它后面的全部失效。所以最常见的翻车是往系统提示里塞了当前时间戳、随机 ID、或者动态拼进去的用户名——这一下就把整个缓存前缀作废了,你以为在省钱,实际一次都没命中。

另一个坑是工具定义的顺序。如果你的工具是从一个哈希表里遍历出来再序列化的,顺序可能每次都不一样,同样会破坏缓存。序列化前排个序

折扣比例、缓存有效期、最小可缓存长度这些数值,各家不一样,以官方文档和控制台为准。你要做的是先把排布做对,让缓存有命中的机会,然后从账单上验证命中率是不是真的上去了。

4.6 提前终止:明确成功判据

「再确认一下」是 Agent 最贵的口头禅。

对策不是在提示里写「不要重复确认」——模型不一定听。对策是把成功判据做成可判定的条件,由代码来判,不由模型来判:

  • 任务要求「找到某个配置项的值」→ 判据是「已经拿到一个非空的值」,拿到就返回,不再进循环
  • 任务要求「修改并验证」→ 判据是「验证步骤返回通过」,通过就停
  • 拿不到明确判据的任务,退而求其次:连续两轮没有产生新信息就终止(比如两轮工具调用返回了相同的结果)

这条实现起来不难,收益主要在于砍掉那些「已经完成但还在空转」的尾巴轮次。而尾巴轮次恰好是最贵的——因为它们的上下文最长。

五、可观测性:必须按「任务」聚合

这一节短,但它决定了前面四节能不能落地。

要记的字段,粒度是任务:

字段说明
task_id贯穿一个任务所有调用的唯一 ID,这是一切的前提
总输入 token该任务所有轮次输入之和
总输出 token同上
轮次数模型调用次数
工具调用次数分工具名统计,用来发现哪个工具在被反复调
重试次数因失败或格式错误触发的额外轮次
终止原因正常完成 / 触发轮次上限 / 触发 token 上限 / 出错
任务类型用来做分组对比

为什么必须按任务聚合,不能按请求:

按请求看,你的面板上是几万条调用,每条 2000 到 5000 token,平均值很好看,没有任何一条触发告警阈值。按任务看,你会立刻发现有 3% 的任务消耗了 40% 的 token。

这两个视图看的是同一批数据,结论完全不同。 按请求聚合的面板永远发现不了「一个任务跑了 60 轮」这件事,因为那 60 轮里每一轮单独看都是正常的。

有了任务维度的数据,你才能算出真正有用的三个数:

  • 单任务平均成本:拿来跟业务价值对比,判断这个功能划不划算
  • 单任务成本的 P99:拿来设硬性上限,上限一般设在 P99 往上一点
  • 成本 ÷ 成功率:一个失败率 30% 的 Agent,它的有效单位成本是平均成本除以 0.7,也就是高出约 43%。这个数才是你该拿去做决策的数

最后一个数经常被忽略。失败的任务同样在烧钱,而且失败前往往烧得更多——它通常是触发了上限才终止的。

六、什么时候压根不该用 Agent

Agent 的溢价买的是运行时的决策自由度。如果你的任务不需要这个自由度,这笔钱就是白花的。

判断标准,满足两条以上就别做 Agent:

  1. 步骤是固定的。你能提前画出完整流程图,没有分支或者只有两三个可枚举的分支
  2. 工具调用顺序是确定的。永远是先查 A 再查 B 再汇总,不存在「看了 A 的结果再决定要不要查 B」
  3. 失败处理是确定的。哪一步失败了该怎么办,规则写得出来
  4. 输入格式稳定。不需要模型去理解一段自由文本才知道该干什么

满足这些条件的任务,写成固定流水线不仅便宜,而且更稳

维度固定流水线Agent
模型调用次数确定,通常 1 到 2 次不确定,3 到几十次
每次输入只带该步骤需要的内容带全量历史
成本可预测性可以精确算出来只能给分布
失败可复现部分不可复现
出问题排查定位到具体步骤要读完整轨迹

有一个折中形态值得考虑:主干用固定流水线,只在真正需要判断的那一步嵌一次模型调用。比如整个流程是固定的七步,其中第三步需要「根据用户描述判断属于哪个类别」,那就只有这一步调模型,其余六步是普通代码。这种做法的成本接近一问一答,能力接近 Agent,是我最常推荐的形态。

反过来说,什么时候 Agent 是值的:任务的步骤数和顺序事先真的不知道,需要根据中间结果动态决定;或者输入的形态太发散,穷举不出流程。这两种情况下,Agent 省下的是工程师的时间,那笔钱花得不冤。

上线前自查清单

  1. 每个任务有唯一 task_id,所有调用日志都带上它,成本面板按任务聚合而不是按请求聚合
  2. 最大轮次、最大总 token、单任务预算三个硬上限全部设置并生效,触发时有日志和报警
  3. 工具按任务类型分组挂载,确认没有出现「所有任务挂全量工具池」的情况
  4. 上下文按「系统提示 → 工具定义 → 任务描述 → 历史」的稳定性顺序排布,系统提示里没有时间戳、随机 ID 等破坏缓存的动态内容,工具定义序列化前已排序
  5. 超长的工具返回结果先摘要再入上下文,摘要保留可追溯的引用,任务目标和错误信息不压缩
  6. 有可由代码判定的成功判据,达成即终止;没有明确判据的任务,配置了「连续两轮无新信息则停」的兜底
  7. 用自己的实际单价套一遍第三节的算术模板,算出单任务平均成本、P99 成本,以及「成本 ÷ 成功率」的有效单位成本
  8. 重新过一遍第六节的四条标准,确认这个任务确实需要运行时决策自由度,而不是一条能画出流程图的固定流水线

相关阅读