周六凌晨两点半,手机连着震了七八下。我从床上爬起来看了一眼告警群,第一反应是“哪个用户这么能跑”。云厂商账单里,我们那个 Agent 平台的推理成本曲线在 40 分钟内从日常谷底直接拉满,换算下来每分钟烧掉 10 块钱。说实话,那一刻我脑子里闪过的是一整年的预算被抹掉,然后才强迫自己冷静下来,打开监控后台。
那个平台是我和团队用 LangGraph 加 Dify 搭起来的企业内部 Agent 平台,接了两个主力模型,带 web 搜索、数据库查询、内部 API 调用几类工具,上线两个多月一直很稳。日常成本不算高,几十块钱一天,所以我也一直没对“Agent 会烧钱”这件事太紧张。但那次告警之后,我把过去两个月的账单翻了个底朝天,越看越后怕,很多钱全都花在了完全看不到的地方。这篇复盘不打算讲什么宏大叙事,就是把你可能也会踩到的坑一条条摆出来,顺带给出我现在认为最靠谱的止血和预防方法。
1. 事故现场:凌晨两点的成本告警,40 分钟烧掉四百多块
1.1 不是用户流量爆发,是平台自己在“自言自语”
告警卡片上写的指标是我们自己定的每日推理成本环比阈值。我进控制台第一件事是看请求量曲线,以为是有营销活动或者外部爬虫在打接口。结果很反常:网关层的用户请求量几乎没有波动,凌晨本来就是低峰期,QVPS 只有平时的十分之一。可模型推理服务的 Token 消耗却在飙升,而且是那种阶梯式往上跳的图形,每隔几分钟就多一个尖峰。
当时我第一反应是模型 Key 是不是泄露了,被人拿去乱用。马上查了调用来源,发现所有异常调用都指向同一个内部服务账号。那个账号属于一个定时报表 Agent,任务是每半小时拉一次销售数据,汇总成简报发到飞书群。它怎么会在凌晨突然开始疯狂烧钱?带着疑问打开了这个 Agent 的执行日志,才发现它已经在后台“自言自语”了将近 40 分钟。
1.2 定时任务为什么变成吞金兽
问题出在它的一个工具调用上:拉销售数据时,它会先请求内部财务系统的接口。那个接口当时正好在发版,偶发返回 500。Agent 平台默认的重试机制看到失败就自动重试,重试失败再重试,而且每次重试都不是简单地把同一个 HTTP 请求再发一遍,而是把“当前对话的完整上下文”重新交给大模型,让模型重新理解、重新决策、重新生成工具参数。
也就是说,一次失败的工具调用会触发一整轮模型推理。如果上下文里已经累积了十几轮历史纪要、几段工具返回结果、系统提示词和工具定义,这一轮推理的输入 Token 可能上万。重试 5 次,等于同一段话在模型里跑 5 遍,而且每一遍模型输出结果还被当作“新的观察”追加到对话里,下一次的上下文更肥。到后面,那条 Agent 的上下文窗口里塞了几万 Token 的“错误信息——重试——错误信息——重试”的死循环记录。
1.3 第一次确认“看不见的钱”藏在哪
我们当时的监控面板只做了请求数和总 Token 聚合,没有按任务维度拆成本。所以这类后台任务烧了多少钱,在账单上只会混在“模型推理费用”一个大项里,不仔细看根本发现不了。真正让我后背发凉的,是这类定时任务从周一到周五每半小时就启动一轮,平时大概率也有失败重试发生,只是单次规模没这次大。换句话说,它可能已经偷偷烧了两个月,我一点感觉都没有。
那晚我把那个定时 Agent 停掉之后,账单曲线立刻平了。但这件事引出的问题远比一次告警严重:平台上还有二十多个 Agent,如果每一个都有类似的默认重试、默认拼上下文、默认调最强模型的行为,那我们的成本根本不是“可控”的,而是“看运气”。
2. 钱花在哪了:把 Agent 平台的账单拆开逐项看
2.1 你以为的成本=模型输出Token,实际所有链路都算钱
大多数人计算 Agent 成本时,脑子里默认的是“用户问一句,模型答一句,答了越多字越贵”。这个直觉在大模型聊天时代大致成立,但放在 Agent 平台上完全不够用。Agent 平台的计费表面上是按输入 Token 加输出 Token 收费,可实际上输入 Token 的膨胀速度远远超过用户看到的输出。
我给你算一笔当时的真实情况。用户说“帮我查一下华东区昨天的回款”,Agent 最终生成给用户看的那段回复,假设只有 300 个输出 Token。但为了得出这 300 个 Token,模型需要先读一遍系统提示词、工具定义、历史对话、上一轮规划,再分析该用哪个工具、生成工具参数、等工具返回结果后重新阅读结果,最后才生成回复。这些全是输入 Token,而且每一次模型调用都会把之前累积的内容重新读一遍。
所以用户看到的是一个轻量请求,模型内部却负重跑了好几趟。成本的大头根本不在最后那段回答上,而在每一轮“看不见”的输入消耗上。这也是为什么我们常说,Agent 平台比普通 Chat Bot 烧钱,不是模型贵了,而是链路长了。
2.2 工具调用是隐形成本大户:一次“查天气”背后可能烧了 5 次模型调用
工具调用是 Agent 平台区别于普通聊天机器人的核心能力,但也是成本失控的第一大来源。拿查天气这个最朴素的场景举例:用户说“明天北京适合出门吗”,Agent 内部可能先调用一次模型判断意图,再调用一次模型决定需不需要用工具,决定用之后又调用一次模型生成结构化参数,工具返回数据后,再调用一次模型解读天气结果,最后再调用一次模型组织自然语言回答。为了一个本来可以直接调 API 的问题,模型被连续唤起了四到五次。
如果工具调用过程中出现一次返回格式不合法,或者 Agent 认为结果不够充分,它还可能继续发起搜索、继续调用其他工具、继续追问。每一次工具调用都会把前面积累的所有上下文重新喂给模型。工具越多,链路越长,上下文越膨胀,成本就越像一个滚雪球。
2.3 记忆与向量化:每个对话结束后的“固定税”
很多人会把注意力放在模型调用上,忽略记忆服务。我们的 Agent 平台给每个会话做了长期记忆,对话结束后会把关键信息写入向量库,下一次用户再来时先检索相关记忆再送进 Prompt。听起来很合理,但这里面有两次隐形消耗:写入记忆需要调用 Embedding 模型,检索记忆也要调用 Embedding 模型,而检索出来的相关片段还会作为上下文输入给主模型。
对于一个高频使用的 Agent,每个用户每轮对话都会产生至少一次 Embedding 调用和一次向量检索。单次费用不高,但如果一天跑几万次,这块费用积累起来就非常可观。而且记忆内容如果没有清理策略,越攒越多,检索结果一次比一次长,Prompt 也跟着膨胀。它不是一次性成本,而是只要平台在跑,就要一直交的“固定税”。
2.4 成本分层参考表
为了让自己团队快速对齐问题,我把 Agent 平台的成本拆成了下面几个层级。我建议任何接手 Agent 平台的人都先建这么一张表,别只盯着模型单价。
| 成本类型 | 典型消耗 | 为什么最容易忽略 |
|---|---|---|
| 模型输入 Token | 每次调用携带的 Prompt、历史、工具定义 | 和“回答字数”完全不成正比,膨胀极快 |
| 模型输出 Token | 最终回复、中间规划、工具参数生成 | 中间输出也会计费,且往往不止一段 |
| 工具调用链 | 每个环节触发一次模型推理 | 链路越长,模型被唤起的次数越多 |
| 重试机制 | 失败后重新送完整上下文 | 重试等于把整段开销复制多份 |
| 记忆与向量化 | Embedding 写入/检索、相关片段入 Prompt | 单次便宜,高频累积后非常可观 |
| 日志与观测 | 全量 Prompt/Response 落库、链路追踪 | 存储便宜,但入库前的清洗和检索也是资源消耗 |
做完这张表之后,我们才真正明白:出事的不是一个模型调用,是整条链路里每一个默认行为都在叠加成本。
3. 顺着链路查:一次带工具调用的 Agent 任务到底烧了多少 Token
3.1 给请求加上 trace 标记,把黑盒变白盒
复盘进行到这一步,光看聚合账单已经不够了。我需要知道“一次普通的 Agent 交互,内部到底发生了多少次模型调用,每一次用了多少 Token”。好在主流 agent 框架多少都自带一些追踪能力,LangChain 系列可以用 LangSmith,Dify 也有自己的日志中心。我们当时用的是 Langfuse,一个开源的 LLM 可观测平台,接入非常简单:给每次 Agent 运行指定一个 trace_id,再给内部每一次模型调用和工具调用挂到同一个 trace 下面。
接入半小时后,我们就能在 Langfuse 里点开任意一次用户会话,看到完整的模型调用瀑布流。那一刻,“黑盒”才算真正打开了。每一条调用都记录着 input tokens、output tokens、模型名称、延迟时间,甚至 Prompt 内容。有了这些数据,我们才敢开始算真实的成本账。
3.2 一个看似简单的请求,背后是 7 次模型交互
我拿当天一笔很普通的用户会话做样本,把它拆开看。用户问的是:“这个月华东区哪个客户回款异常?”平台内部实际跑了一遍这个流程:
- 第一次模型调用:意图识别,判断用户问的是“销售数据”还是“客户回款”。
- 第二次模型调用:规划,决定需要查数据库,并且要调用“客户回款查询”工具。
- 第三次模型调用:参数生成,把用户问题转成结构化查询条件。
- 工具执行:这步不消耗模型 Token,但会花时间去查库。
- 第四次模型调用:阅读理解工具返回结果,生成一段中间小结。
- 第五次模型调用:判断返回结果是否满足用户意图,决定是否继续追问或换工具。
- 第六次模型调用:生成最终回复给用户。
如果这轮对话最后触发记忆写入,还会有第七次模型调用来做内容提炼。实际跑出来七次模型调用,而用户全程只看到一个回答。每一轮调用都带着前面所有轮次拼出来的上下文,尤其是第三次调用时,系统提示词、工具定义、用户问题、计划内容已经全部塞进去了。
3.3 最关键的发现:上下文每过一跳就翻倍
我统计了这个样本每一轮的实际 Token 数,发现一个特别明显的规律:随着链路推进,Prompt 长度几乎是线性往上猛涨。第一次意图识别可能只需要 4000 Token;到了第三次参数生成,因为要把前两轮的结果全部带上,直接涨到 9500 Token;到第四次阅读工具结果时,工具返回的那张几十行数据表又被完整塞进 Prompt,一下子冲到 21000 Token。最后一次生成回复,光是输入就有 25000 Token。
这是最容易被忽略的放大效应。Agent 的每一步“思考”都在消费前一步的结果,而框架默认会把所有中间结果都追加到对话历史里。对于一次只有两三个工具调用的简单任务,最后上下文可能膨胀到几万 Token。工具调用越多,历史越长,越往后 Prompt 越长,成本曲线也就越陡。
3.4 实测数据:单次对话平均消耗是用户可见内容的 8 倍
我们把当天一百多个正常会话样本全部拉出来统计,算出一个让人印象深刻的比值:用户每秒看到的输出 Token 数,只占整条链路总 Token 消耗的 12% 左右。换句话说,模型实际处理的内容,是用户可见内容的 8 倍以上。
按我们当时接的模型内部价折算,这种单次会话平均要烧掉 1 块多钱。如果平台同时有几十个用户在线,每人每分钟触发几次工具调用,每分钟烧掉 10 块钱完全是现实数字。对照标题那个“每分钟烧 10 块”的告警,这才是它真正的来源——不是单个用户太狠,而是每个普通请求都在长期超支。
4. 藏在默认配置里的吞金兽:平台设置是怎么反复放大成本的
4.1 无节制重试:同一个错误被反复全量计算
事故的直接导火索就是重试机制。很多 agent 框架和编排平台的重试参数默认都是“失败就重试”,不限制次数,或者限制得非常宽松。重试本身是必要的,真正的问题在于:Agent 的重试不是重新执行工具调用,而是把整个上下文再让模型推演一遍。
一个上下文已经累积到 20000 Token 的任务,重试一次等于额外付 20000 Token 的输入费用。如果接口不稳,连续失败五次,那就是 100000 Token 白烧。这还不算重试期间模型生成的中间输出。更糟的是,这类定时任务往往无人值守,失败重试循环可能一直持续到预算耗尽。在我这次事故里,那个销售报表 Agent 就是每半小时启动一轮,每轮撞上接口 500 就疯狂重试,一个晚上烧掉的钱比前一周加起来还多。
4.2 记忆机制把上下文窗口当垃圾桶
第二个大问题是记忆的“无脑追加”。我们的平台早期设计很简单,为了不丢信息,把所有历史对话、工具调用记录、中间结果全部塞进上下文窗口。最初看起来没问题,因为测试时只有一两轮对话。但真实业务跑几天后,一个账号的上下文可能包含上百轮历史,甚至跨天累积。
我抽查过一个跑了两周的客服 Agent,它的 Prompt 里光历史对话就占了 18000 Token,而真正跟当前问题相关的可能不到 15%。每次请求都要为这堆历史付钱,还额外拖慢响应速度。更夸张的是,如果记忆系统把向量检索结果也原样追加,不加任何裁剪,那每轮请求都会背上越来越重的包袱。
4.3 模型选型“就高不就低”:所有子任务都跑最强模型
第三个隐性成本来自模型选择。项目初期,团队比较谨慎,为了效果,把平台上所有 Agent 的所有内部步骤全部配置成了当时最强的模型。意图识别用大模型,参数生成用大模型,连给中间结果写一句摘要也用大模型。这在功能层面很稳妥,但成本层面非常奢侈。
实际上,Agent 内部很多步骤根本不需要顶级推理能力。判断用户是不是想查天气、把一段工具返回结果压缩成三句话,这些任务用轻量模型完全能胜任,成本可能只要大模型的十分之一。我们的问题不是“该不该用大模型”,而是“没有区分场景,一刀切全用了大模型”,等于每个环节都在用最高价买单。
4.4 编排框架的循环边界没设置
第四个问题出在框架的编排参数上。用 LangGraph、Dify、CrewAI 搭过 Agent 的人都知道,这些框架允许你定义 Agent 在工具间进行多轮迭代,但“最多迭代多少次”这个参数默认值往往很宽松。有的框架甚至允许 Agent 一直循环到没有可用工具为止,或者一直重试到 Token 耗尽。
如果在设计阶段没有显式设置最大迭代次数,一个稍微复杂的任务就可能让 Agent 反复执行“思考—调用工具—观察结果—再思考”的循环。每次循环都在消耗 Token。尤其当 Agent 拿到的工具返回结果不如预期时,它会尝试换一个工具继续试,直到把能试的全部试一遍。几十次迭代下来,成本自然就爆了。这也是 agent 开发里最常见的“跑通了但不敢上生产”的原因之一。
4.5 观测本身也在烧钱:日志全量采集+无用 trace 的隐性开销
最后我要提一个平时特别容易被忽略的点:观测系统的成本。为了排查问题,我们早期把所有请求的完整 Prompt 和 Response 都存进日志平台,每个 trace 也都保留全量上下文。这当然方便 debug,但同时也意味着每一轮模型调用的上下文都被复制了一份储存在日志、链路追踪和向量库里。存储成本本身不高,但当日志量起来之后,检索、清洗、建立索引的资源和人力成本都是实打实的。
这不是说不要观测,而是说观测也要设计。全量保真只适合测试环境,生产环境应该做有损采样或者只保留关键元数据,否则你为了看成本花了更多的成本,就成黑色幽默了。
5. 止血实操:从模型降级、缓存到编排收缩的修复清单
5.1 给 Agent 加硬预算:请求级熔断与全局配额
复盘结束后,我们做的第一件事不是调 Prompt,而是加“硬性限制”。现在所有 Agent 任务在启动前都会先读取预算配置,配置里包含四个关键值:单次任务最大 Token 预算、最大工具调用次数、最大重试次数、最大运行时长。只要任何一个阈值被触发,任务立刻终止并返回带错误码的响应,同时向告警群发一条消息。
代码层面其实非常简单,核心逻辑就是在 Agent 的每次循环里检查累计消耗。比如伪代码是这样的:
total_tokens = 0 max_tokens = 500_000 max_retries = 2 while current_step < max_steps: response = model.call(prompt) total_tokens += response.usage.total_tokens if total_tokens > max_tokens: raise BudgetExceededError(f"tokens={total_tokens}") if tool_calls_failed and current_retry >= max_retries: raise TooManyRetriesError()这套机制上线当天就拦下了好几个本来要失控的任务。重点是它必须在平台层强制生效,而不是依赖每个 Agent 开发者自觉。
5.2 上下文裁剪与 Prompt 瘦身
第二步是给 Prompt 减肥。我们设计了一套分层上下文策略:每轮工具调用结束后,只保留“该轮的工具名称、执行状态、结果摘要”三项内容,丢弃完整的原始工具返回值;历史对话只保留最近 3 轮,更早的内容交给一个轻量模型做定期摘要;系统提示词里的工具定义全部精简,只保留参数结构,不保留长篇说明。
这套策略上线后,我们统计了同一样本请求的输入 Token,从原来的平均 25000 左右降到了 9000 左右,降幅超过 60%。而且因为上下文变短,模型响应延迟也下降了,用户体验反而更好了。可见“为了完整而完整”的上下文保留,很多时候只是自我安慰。
5.3 工具调用的预检查与缓存
第三步是工具侧优化。我们发现很多工具调用是重复且不必要的,比如同一个销售数据查询,五分钟内不同用户问的其实是同一件事。于是给工具调用层加了两层防护:第一层是“预检查”,在模型决定调用工具之前,先做一个成本很低的规则匹配,如果用户问题和最近某次成功调用高度相似,直接复用上次结果;第二层是 TTL 缓存,对不同工具配置不同的缓存时间,数据实时性要求不高的工具缓存五分钟,只有强实时工具才绕过缓存。
另外,我们把容易失败的第三方接口包了一层带断路器的客户端,连续失败三次直接打开熔断开关,不再让 Agent 反复重试。这个改动对成本的影响立竿见影,因为很多“大火”都源于一个不稳定接口。
5.4 模型分级策略
第四步是模型分级。我们把平台上的模型调用按任务难度分成三级:
- 复杂规划、跨工具调度、生成最终对外回复,使用强推理模型;
- 意图识别、参数提取、工具结果摘要,使用中等能力的轻量模型;
- 格式化输出、关键词提取、缓存命中判断,直接走规则或嵌入模型,完全不调大型模型。
这样调整之后,单次 Agent 任务的平均成本下降了大约四成。更重要的是,整体效果没有明显变化,因为真正考验模型能力的环节仍然保留了强模型,普通环节用轻量模型本来就够用。
5.5 可观测成本:把 token 计量注入每一个请求
第五步是成本观测的精细化。以前我们只知道总费用,这次我们把“成本”做成了一个一等公民指标:每次 Agent 运行结束时,都会输出一个包含任务名、trace_id、模型调用次数、总 Token、估算成本的日志条目。这些数据汇入一个小型 ClickHouse 表,按小时聚合,出了任何异常都能立刻定位到是哪个任务、哪一步在烧钱。
告警也做了分级。像“单任务成本超过 20 元”这种属于紧急级别,会直接打电话;而“某任务成本环比上涨 50%”属于平级,合并到日报里推送。只有这样才能做到“钱花在看不见的地方”变成“钱花在每个 trace 都能查到的地方”。
5.6 修复后的真实成本对比
所有修复上线后,我们跑了整整一周来验证效果。对比数据如下:
| 指标 | 修复前一周 | 修复后一周 |
|---|---|---|
| 日均推理成本 | 约 850 元 | 约 320 元 |
| 单次会话平均 Token 消耗 | 约 24000 | 约 9000 |
| 定时任务失败触发重试次数 | 每天数十次 | 几乎为零 |
| 成本相关告警次数 | 1 次(严重事故) | 0 次 |
这个结果并不意外。成本不是靠省出来的,是靠把“默认的浪费”去掉之后自然降下来的。
6. 把成本护栏焊进项目初期:预算告警与架构审查清单
6.1 上线前必须回答的 10 个成本问题
这次事故之后,我把成本经验固化成了一个问题清单,所有新 Agent 上生产之前,必须逐条回答:
- 单次任务最大的 Token 成本是多少?写没写进文档?
- 工具调用链最长多少轮?如果工具一直失败,循环如何终止?
- 最大重试次数设了几次?重试时上下文会不会翻倍?
- 上下文窗口最多保留多少轮历史?谁来负责裁剪?
- 记忆和向量检索会不会扩大 Prompt?结果有沒有摘要化?
- 每个子任务都用最强模型吗?能不能按难度分级?
- 工具调用有没有预检查和缓存?缓存 TTL 是多少?
- 任务运行超过多长时间会被熔断?超时后人工通知谁?
- 成本可观测性是否覆盖每一次模型调用?能不能按任务拆账?
- 定时任务和无人值守任务有没有独立告警和预算上限?
这些问题看起来琐碎,但每一个背后都对应着一次真实的钱损失。像“重试几次”这种问题,在传统后端开发里几乎不算事,但在 Agent 平台上就是成本放大器,必须正面回答。
6.2 成本设计要进架构评审
以前我们做 Agent 架构评审,只看功能链路:意图识别准不准、工具能不能调通、回答用户满不满意。从来没有人问“如果这个 Agent 一天跑一万次,要花多少钱”。现在我们把成本设计放进了和功能设计同等重要的位置,评审时至少要有一个专门的话题叫“成本模型与熔断策略”。
有个很典型的例子:新的 Agent 需要每五分钟扫描一次全网舆情,如果按原方案把每篇结果都交给大模型做情感分析,单日成本轻松破千。后来在评审阶段改了方案,先用规则模型做粗筛,只让大模型分析置信度低的少数结果,成本直接降了两个数量级。这就是成本设计进评审的价值。
6.3 我的个人教训:平台不是黑盒,Agent 平台的每一笔 token 都有账
踩完这次坑之后,我最大的体会是:Agent 平台本质上不是一个黑盒,它是一台不停运转的 Token 印刷机。每一轮思考、每一次工具调用、每一段历史记录、甚至每一条日志,都被换算成模型输入和输出,最终变成账单上的数字。如果你不主动去拆解它,不去给每一个环节设置边界,它就会用你看不见的方式,把预算一点一点吃掉。
现在我也养成了一个小习惯:每周五下午花十分钟,拉出平台的按任务成本报表,看看哪些 Agent 的成本在悄悄涨。不用等告警,也不用到月底看账单,而是在成本刚刚偏离基线的时候就发现它。对任何做 Agent 平台的人来说,这十分钟可能是整周性价比最高的动作。