干Agent项目一年多,我最大的一个感受是:很多团队嘴上说的是优化成本,手上做的却是跟Prompt字数较劲。今天这篇内容,我想把这个账摊开算清楚——为什么让Agent少踩坑,比压缩Prompt更省钱。
起因是我带过一个内部工单分类Agent,上线跑了两周,费用报告出来了,数字相当难看。当时我第一反应也是去压缩Prompt:案例从五条减到两条,工具说明从几十行缩成一句话。账面上确实好看了一点,但分类错误率反而从8%飙到21%,每天人工返修的工时多了两个多小时。后来跟项目组复盘了一个下午,才发现一个很扎心的事实:Agent真正的成本黑洞,从来不是Prompt太长,而是它反复试错、来回绕路、最后还得人来兜底。
这篇内容适合这几类人看:正在跑Agent项目的开发者,为Token费用发愁的负责人,还有那些已经觉得"Prompt压缩边际收益越来越小"的人。我会从踩坑模式、底层设计、一个完整改造案例、排查技巧和成本算法几个角度,把整套思路拆开讲。
1. 先说"省钱"这件事到底省在哪
1.1 Agent账单上的三大开销
正常来讲,Agent每处理一个任务,钱都花在三个地方。
第一,有效推理token。这是模型真正理解问题、想清楚怎么做、输出结果的花费,属于该花的钱。第二,试错token。这一步就耐人寻味了,Agent调用工具失败、返回格式不对、绕进死胡同、自己跟自己反复解释,每一次失败都在烧token,而且往往比正常推理烧得还猛。第三,多余上下文token。把历史记录、知识库全文、上一轮的失败原因一股脑塞进来,模型看的时候要按输入token全量计费。
很多人压缩Prompt,压缩的主要是第一类里的"字面长度"和第三类的一部分,但对第二类,也就是试错token,几乎没有约束力。我见过不少项目,Prompt已经压得特别短了,账单却没怎么降,就是因为Agent的失败轮次一点没少,甚至因为Prompt太短导致它更迷茫,失败率反而升了。我统计过几个项目,试错和纠错相关的token,一般能占到总消耗的40%到60%。这个数字很说明问题:你每付1块钱给Agent,里面有差不多一半是在为错误买单。
1.2 压缩Prompt的边际陷阱
必须说清楚,我不是让大家别压缩Prompt。Prompt精简本身是对的,问题在于很多人把它当成了唯一的省钱杠杆。
压缩Prompt有一个明显的边际递减规律:刚开始从五千字压到三千字,效果立竿见影,Token成本肉眼可见地降;再往下压,就开始伤筋动骨了。我自己踩过的几个典型坑是这样的:
删掉示例后,模型的输出格式开始飘。少了一条"错误输入示例",它就对边界情况没有概念,偶尔会给你传一个明显不合理的参数。把CoT步骤从十步精简成三步,复杂任务开始"跳步",推理过程不完整,结果质量明显下降。为了省字数,把一些约束条件写得过于省略,模型理解不到位,甚至会自行发挥出一些业务上绝对不能允许的行为。
压缩Prompt在本质上是在"删信息",而Agent的稳定性高度依赖信息完整性。当信息删过头的时候,表面上每个token单价低了,执行次数却变多了,总账反而更难看。这就引出一个关键概念:Agent成本不只是Prompt长度的线性函数,它是"轮次×每轮token"的乘积。当你把失败轮次从8次降到2次,即使每轮贵一点,总成本也在降。
2. Agent踩的坑都有哪些特征?
2.1 典型的五个踩坑模式
要让Agent少踩坑,先得知道坑长什么样。我把平时见过最多的失败模式整理成了一张表:
| 踩坑模式 | 典型表现 | 费钱方式 |
|---|---|---|
| 指令漂移 | 跑着跑着开始自行加戏,偏离原始目标 | 多做无用轮次,容易把后续逻辑带偏 |
| 工具参数错误 | 日期格式传错、把字符串传成数字、漏必填参数 | 一次失败,这次调用的token全白花了 |
| 上下文污染 | 历史失败记录、无关对话都留在上下文里,模型被带偏 | 在一个错误方向上反复努力 |
| 死循环 | 同一个失败方案换个姿势再来一次,直到预算烧穿 | 最烧钱的一种模式 |
| 过早放弃 | 遇到小挫折就整个任务中断 | 前面步骤全部白做,用户还得重新发起 |
指令漂移是很多复杂任务里最隐蔽的问题。Agent一开始还沿着你的目标走,跑到第三轮开始自己给自己"加戏",做一些跟原目标不太相关的事。比如让它整理数据,它中途开始给数据做可视化;让它做分类,它顺手生成了一段报告。这些扩展动作表面上看起来"很能干",但实际上消耗了额外的轮次,而且很容易让任务的最终输出偏离预期。
工具参数错误是调用类Agent最常见的问题,工具越复杂、参数越多,这种错误越频繁。上下文污染的麻烦在于它不会直接报错,只会默默影响判断——模型读到一堆"垃圾信息",很容易被带偏,在错误方向上反复努力。死循环和过早放弃则是两个极端,前者是太执着,后者是太脆弱,都让成本失控。
2.2 失败是有"连锁成本"的
一个Agent任务失败,不是只浪费当次那几轮token。它的连锁成本是这样滚起来的:第一次失败触发了自我修正,修正又错了,继续浪费,最终失败,然后把这一堆错误历史全塞给下一个任务,下一个任务也被污染。每一层都在放大消耗。这也是为什么"减少失败率"对成本的影响,远远超过"减少单次token"。
我做过的项目里,一个普通任务如果成功,平均两三轮就能结束;如果失败,平均会跑到八九轮甚至更多。也就是说,一次失败的成本大约是成功任务的三到四倍。如果把失败率从20%降到5%,省下来的不是15%的成本,而是接近一半的无效消耗。这个杠杆效应,是没有任何Prompt压缩手段能替代的。
2.3 为什么"撞运气式调参"解决不了问题
有些团队的做法是,在Agent出错之后改Prompt、调温度参数、换模型,但下次遇到类似问题还是错。原因是这些做法没有触及真正的问题结构。
举一个真实的例子。Agent把日期格式传错,你怎么改Prompt都很难让它每次都记住"要传YYYY-MM-DD",因为这是模型在生成输出时天然的弱点。但如果你把工具接口改成"直接接收已格式化的日期对象",或者让它在调用工具之前先经过一道规则校验,问题就从根本上消失了。
所以,让Agent少踩坑,不能只靠"在字面上劝它别犯错",而是要在架构上让错误发生的机会变少。这也是我今天要强调的重点。
3. 让Agent少踩坑的五个底层设计
3.1 工具设计:接口短而准,错误消息要说人话
先讲工具设计,因为这是Agent落地里影响最大、却最容易被忽视的一层。
Agent会调用你暴露给它的每一个工具,但模型对工具的理解完全来自你的工具描述。描述写得不清楚,它一定踩坑;描述写得太长,它聚焦不了;工具参数太多,它总会在某个参数上出错。
我的设计原则是三条。第一,参数越少越好。不要给工具塞五个甚至八个参数,能用默认值、能自动获取的,就不要让模型"猜"。参数少了,出错面自然就小了。第二,错误消息要结构化,并且包含下一步建议。很多工具在失败时只给一个"Error 500"或者"调用失败",模型看到这种信息根本不知道怎么处理,只能瞎试。第三,在工具描述里把常见误用写进去。工具描述本身就是一个免费的Prompt,它不会增加多少token,但能显著降低出错的概率。
我贴一个小例子,这是一个"查询相似工单"工具的简化版描述:
工具:find_similar_tickets(query: str, limit: int = 3) 说明:根据关键词查找历史相似工单,用于辅助判断分类。 注意: - query只传核心关键词,例如"退款失败",不要传完整工单内容。 - limit最大为10,传大了会被拒绝。 - 找不到结果时返回空列表,不代表失败;不要重复尝试。这段描述一共不到一百个字,但把三个最容易踩的坑都提前堵住了。我在几个项目里都试过,工具层描述稍微加几条注意,参数错误率能明显降一截,而且这部分几乎不消耗token。
3.2 上下文管理:不该喂的信息就别喂
上下文窗口很大,不代表可以无脑塞。你喂给模型的一切,它都会读、会联想、会被影响。上下文里如果充满了历史失败记录、无关的领域知识、上一轮用户随口说的一句话,这些信息都会干扰Agent对当前任务的判断。
更关键的是,上下文的长度直接决定每次调用的费用。所以上下文管理是"少踩坑"和"省钱"的交汇点。
我的做法是三条。第一,滚动窗口加摘要。最近的几轮对话保留原始信息,更早的内容压缩成一个摘要,需要时再展开。这样Agent既不会丢失关键背景,也不会被远古的噪音带偏。第二,按阶段加载上下文。规划阶段只给它规划需要的信息,执行阶段再加载具体的数据细节,不要一个任务从头到尾都背着一整本手册。第三,工作记忆和长期记忆分开。工作记忆放当前任务相关的内容,任务结束就清空;长期记忆放用户偏好、企业规则、知识库条目,按需检索。两类记忆混在一起,是上下文污染的高发原因。
举一个生活化的类比:你让一个新人处理工单,你不会把公司十年内的所有聊天记录都丢给他,而是告诉他"你先看这个工单本身,遇到不会的再查知识库"。Agent也是同样的道理,给它看什么,比它有多聪明更重要。
3.3 任务拆解:改造成Plan-Execute-Check循环
很多Agent项目出问题,是因为让一个Agent从头到尾负责完整任务,中间没有阶段,做完就是做完,错了就整个重来。
更稳的思路,是把任务拆成三个环节循环起来:先规划,再执行,最后校验。具体是这样的:
- 规划:Agent先输出一个简短的步骤列表,比如"先查工单内容,然后判断分类,最后给出结论"。
- 执行:每一步只做一件事,只调用一个工具,不要并行、不要跳步。
- 校验:拿到每一步的结果后,比对预期。如果结果异常,立刻停下来修正,而不是硬着头皮继续下一步。
这个循环看起来多了一步校验,多花了一点token,但换来的稳定性非常可观。错误可以在小范围内被立即拦截,而不是等到最后一起爆发。我在一个报表生成Agent上试过这个改造。原来Agent是"一条龙式"从头跑到尾,偶尔出错就要全部重来,平均每个任务要跑四到五轮;改成Plan-Execute-Check之后,绝大多数任务稳定在两轮以内完成,token总量反而降了。
3.4 路由前置:小模型做判断,大模型做苦力
如果你觉得所有任务都必须用最强的模型才靠谱,那你的Agent成本一定会失控。真实场景里,很多任务根本不需要大模型——比如判断工单属于网络类还是账户类,比如判断一句话是查询还是投诉,这类简单意图识别,一个便宜的小模型就能做好。
我常用的方案是加一层路由前置:用户请求先进一个轻量模型,它只做一件事——把请求分派给对应的专业Agent或者直接走模板化流程。只有那些确实需要复杂推理的请求,才进入大模型Agent的环节。
这个方案的好处是显而易见的。大模型的调用次数大幅减少,账单直接降一截;每个子Agent只处理自己擅长的那类请求,指令更聚焦,上下文更干净,踩坑更少。而且小模型判断意图的准确率其实不低,按我的经验,大部分场景能到95%以上。
这里要提醒一个常见误区:有些人想省钱就直接把贵模型整体换成便宜模型。如果任务本身复杂度高,换了之后质量掉下来,用户就会开始返工、投诉,隐性成本反而上来。折中的方案就是路由:便宜模型负责"选路",贵模型负责"干活"。谁擅长什么,就让谁做什么。
3.5 失败降级:给Agent装一个"熔断器"
没有完美的Agent,它一定会犯错。区别在于,犯错之后它知道该怎么收拾。
我建议给Agent定义一套清晰的降级规则,别让它自由发挥。第一,重试次数上限。一般同一类失败最多重试两次,超过就停止。有些问题重试一万次也不会成功,让它重试就是在烧钱。第二,降级阶梯。重试失败之后,按照预设的路径走:先尝试简化版本的任务处理,比如只输出部分结果;还不行,就要求用户确认或者补充信息;最后一步,转人工处理。第三,把"放弃"也当成一种正常输出。Agent应该能诚实地输出"这个问题超出我的能力,我已经完成的部分是……"而不是假装成功然后输出一个错误结果。
在代码层面,可以给Agent的执行循环包一层观察者逻辑,记录尝试次数,超过阈值就自动降级。这个熔断器不复杂,十几行代码就能实现,但它能把死循环和过早放弃两个极端都控制住。
4. 实操记录:一个工单分类Agent的省钱改造
前面讲了一堆设计原则,这里分享一个我在实际工作中做过的完整改造案例。为了叙述方便,我把背景稍微简化一些,但成本数字和改造思路都是可以直接复用的。
4.1 改造前的痛点
某公司内部有一个工单分类Agent,职责是根据用户提交的工单内容,自动判断工单属于哪个类目,并分派给对应的处理小组。比如"我付了款但没收到订阅"应该分给支付组,"Wi-Fi经常掉线"应该分给网络组。
改造前,这个Agent的架构是这样的:一个大模型接收全部工单内容,系统Prompt里塞了一份该业务全部的分类规则、客服手册和几个示例,上下文里还加载着这个用户近期的所有历史工单。一单下来经常要烧三万多个token,分类错误率在20%上下浮动,错误工单需要人工重新分派,整个流程效率很低。
复盘这个项目时,我发现问题不是"大模型不够聪明",而是架构把事情复杂化了:所有工单都进同一个大模型,模型的上下文里混着各种类型的知识,它在判断"网络问题"时会受到"支付问题"规则的干扰;上下文里塞满了用户历史工单,大部分跟当前工单无关,纯属噪音;工具里的知识库查询参数特别宽泛,模型经常查回一堆不相干的内容。
4.2 改造方案与落地步骤
我把它改成了四层结构。
第一步,加路由层。入口先过一个轻量模型,根据工单标题和内容做一个粗分类:网络、账户、支付、其他四类。这一步用小模型,单次成本极低,响应也快。
第二步,拆子Agent。每一类工单对应一个专项Agent,每个专项Agent的系统Prompt只包含本类别的规则和典型场景。比如支付Agent不需要知道Wi-Fi怎么修,网络Agent也不需要理解退款流程。上下文一下子就干净了。
第三步,工具瘦身。把"查历史相似工单"工具的query参数改为"只传核心关键词",并且加上了上面那种注意提示;把"查用户信息"工具改成直接接收用户ID,而不是让模型自己去解析。
第四步,校验层。分类结果在返回给用户之前,过一道规则校验。比如支付类工单必须带有订单号,网络类工单必须有关键设备词。不满足校验就要求Agent重新处理,最多重试两次,两次还不对就转人工。
整个改造的代码工作量其实不大,主要是把原来的单一大模型调用改成了路由加多个轻量Agent,再加了一小段校验逻辑。
4.3 改造效果与账本
改造之后,我跑了三周的数据来做对比:
- 单次处理token:从平均三万左右降到了不到一万;
- 分类错误率:从20%左右降到了4%到5%;
- 每千张工单的token总成本:下降了一大截;
- 人工返工量:减少了大部分,这部分省下的工时,比token费用还值钱。
特别要说的是,token成本下降的原因不是Prompt变短了,而是每单跑的轮次变少了。很多工单在路由层就直接被分派了,根本轮不到大模型出场;需要大模型处理的工单,上下文也干净了,Agent不再反复试错。
这个案例让我彻底相信了一件事:架构上的"少犯错"优化,效果远远超过文字层面的"压字数"。
5. Agent出问题时,按这四层来排查
设计方案是一回事,线上出问题还是不可避免。关键是怎么快速定位。我自己用的是一个"四层归因法",每次Agent表现不对,都会按这个顺序检查。
5.1 四层归因法
第一层,指令层。先看Agent收到的原始指令是什么,是否存在歧义、缺少背景、例子没有覆盖到。很多时候,问题根本不是模型不行,而是你给它的指令本身就带着坑。
第二层,工具层。检查工具调用记录,看模型是不是传了错误的参数、调用了错误的工具,还是工具本身返回了模糊的报错。这一层的特征是有明确的技术错误日志,好定位。
第三层,上下文层。看上下文里有没有被无关信息污染,有没有该有的信息但没加载进来。上下文问题很隐蔽,因为它不会直接报错,只会默默影响决策。
第四层,策略层。看Agent是不是没有正确的重试策略,遇到失败就死循环,或者一失败就放弃。策略层的问题通常表现为轮次数异常,token消耗曲线突然拔高。
几乎所有的Agent异常都能归到这四层里。定位到层之后,改动方式就清晰了:指令层就改Prompt,工具层就改工具设计和校验,上下文层就改加载逻辑,策略层就加重试和降级机制。
5.2 用结构化日志做"事故回放"
排查的前提是,得有日志可以看。我见过很多Agent项目上线后连基本调用记录都没有,出了问题只能靠猜。这是绝对不可取的。
我在项目里习惯给每次Agent执行记录一份结构化日志,字段包括:步骤号、当前动作、调用的工具、工具输入、工具输出、token数、是否成功。就这一个简单做法,排查效率直接上一个台阶。大致结构是这样的:
{ "step": 3, "action": "call_tool", "tool": "find_similar_tickets", "input": {"query": "退款失败"}, "output": {"matched": 2}, "token_count": 812, "success": true }出问题时,拿着这份日志回到现场,就能准确看到每一步发生了什么,是哪一层的错误,花了多少token。没有这份日志,优化就永远是盲人摸象。
5.3 常见问题速查表
我把日常运维中遇到的几个高频问题整理成一张速查表:
| 现象 | 优先排查位置 | 常见处理 |
|---|---|---|
| Prompt已经压得很短,费用还是没降 | 策略层/工具层 | 看看是不是有死循环或高频重试 |
| Agent偶尔输出格式不规范 | 指令层 | 补一条边界示例,或者加校验 |
| 换了便宜模型后质量下降明显 | 指令层/上下文层 | 不要整体换,改成路由方案 |
| 上下文不大,但总是判断失准 | 上下文层 | 检查是否加载了不相关的历史内容 |
| Agent老是重复调用同一个工具 | 策略层 | 加最大重试次数和降级阶梯 |
| 工具调用时报参数错误 | 工具层 | 精简参数,补充常见误用提示 |
这张表是我日常排查的起点,大多数问题照着这个顺序走一遍,很快能找到根因。
6. 这笔账怎么算,才能说服自己和老板
如果你正在推动Agent架构改造,领导首先会问一句:"这么做能省多少钱?"我建议建立一套简单的成本对比模型,不要用"感觉省了很多"这种回答。
6.1 一个可复用的成本对比公式
先定义几个基础变量:
- T:平均每轮调用的token数;
- R成功:成功任务的平均轮数;
- R失败:失败任务的平均轮数(通常远高于成功任务);
- F:任务失败率。
每100次任务的总token消耗可以近似算成:成功任务的消耗是(1-F)×100×T×R成功,失败任务的消耗是F×100×T×R失败,总成本等于两者相加。
这个公式的价值在于,它把两类钱拆开了:一类是"正常干活的钱",另一类是"为失败付的钱"。你会发现,优化失败率F和失败轮数R失败,比优化单轮token数T敏感得多。原因很简单:R失败往往是R成功的三到四倍,失败率哪怕只是降几个百分点,总成本的降幅都会被放大。
6.2 三套方案的同口径对比
下面用这个公式模拟一下三种方案。假设某个Agent任务,每次调用平均token是1万,成功任务平均2轮,失败任务平均8轮。
方案A,基线:失败率20%。100次任务的总token是(1-0.2)×100×1万×2 + 0.2×100×1万×8,算下来160万加160万,总共320万。
方案B,压缩Prompt:单轮token降30%,但失败率升到30%。总token是0.7×100×0.7万×2加0.3×100×0.7万×8,也就是98万加168万,总共266万。
方案C,架构优化:单轮token略涨20%,因为多了路由和校验开销,但失败率降到5%。总token是1.2×100×1.2万×2加0.05×100×1.2万×8,也就是228万加48万,总共276万。
| 方案 | 单轮token | 失败率 | 每100次任务总token |
|---|---|---|---|
| A 基线 | 1万 | 20% | 320万 |
| B 压缩Prompt | 0.7万 | 30% | 266万 |
| C 架构优化 | 1.2万 | 5% | 276万 |
有意思的地方来了:光看总token,方案C和方案B差不多,甚至C还略高一点。但方案C的真实优势体现在隐性成本上。
6.3 隐性成本往往才是大头
Agent失败的隐性成本,包括但不限于:人工返工的时间、用户体验下降造成的用户流失、错误结果上线后的修复费用、以及团队反复排查问题消耗的精力。
这些隐性成本有一个特点:不出现在账单里,但实实在在地影响收益。方案B虽然token表面省了一点,但失败率更高了,意味着每天都有相当数量的工单需要人工返工,按时薪一折算,立刻超过省下来的token钱。方案C失败率低,人工介入少,体验稳定,这部分省下来的钱远超账面差异。
所以,每次别人问我到底怎么给Agent省钱,我都会说一句话:先别盯着Prompt那几行字,先把失败率打下来。让Agent少踩坑,省下来的不是某一个token,而是一整条错误链上的所有浪费。
写到这里,我再回头想那个工单分类Agent的事。当初为了省一点点token去压缩Prompt,反而让失败率飞涨、人工救火忙成一片的做法,其实是个典型的"捡了芝麻丢西瓜"。我现在做Agent项目,会先花大量精力在工具设计、上下文管理、路由策略这些"让人不犯错"的细节上,最后才考虑Prompt要不要缩。这并不花太多钱,却能把Agent的成功率稳住。如果你也正在为Agent的成本发愁,我建议你先去查一下你的失败率和重试次数,答案可能一眼就出来了。