前阵子帮一个团队做Agent项目瘦身,他们很困惑地问我:功能越做越多,效果却越来越差,额度也越烧越快。我看了一圈代码,第一反应是——他们不是在开发Agent,而是在经营一套微服务系统。21个自定义Skills、13个子代理、30多个工具函数,还有好几层包装的调用链。
这不是个案。我把这类项目统一称为"复杂度税"交得太多的Agent。所谓额度,不管是token配额还是API预算,本质上就是一次任务的成本上限。当你把系统做得足够复杂,每次任务光"无意义的固定开销"就能吃掉一大半额度,真正留给模型思考的空间少得可怜。这篇文章把我自己的项目瘦身经历完整写一遍,包含Skills精简、子代理决策、工具优化三个核心方向,以及如何通过一套可复制的操作,让同样的额度多撑20%以上的任务量。适合那些Agent已经跑起来、但被成本和复杂度压得喘不过气的开发者。
1. 先算账:Agent的额度究竟烧在了哪里
1.1 额度不是被"能力"烧掉的,而是被"冗余"烧掉的
Agent每次做决策,模型都要把系统提示、工具定义、历史对话和当前输入全部读一遍。这一点和人类完全不同:人翻旧账的时候是具体找某句话,模型是每次把"整个文件夹"从头到尾读一遍。所以复杂度直接等于成本,这句话要刻在脑子里。
我在实际项目里见过三种最典型的"烧额度黑洞":
- 上下文膨胀:系统提示从几千token膨胀到几万token,其中大量内容一年都用不上。
- 工具定义重复计费:每个工具的JSON Schema不是只付一次钱,而是每一轮决策都要付一次。工具总数越多,每一轮越贵。
- 失败重试:Agent一旦报错或者工具调用异常,往往不是只损失一次请求,而是带着报错信息重新跑好几轮,消耗成倍增长。
这三种问题叠加起来,一个本可以3000 token完成的任务,最后可能烧掉30000 token。这就是"复杂度税"的真实账单。
1.2 算一笔账:一次普通任务的token都花在哪了
我们假设一个中等规模的Agent任务,需要5轮模型调用。它的token消耗结构大概是下面这样:
| 消耗项目 | 每轮token | 轮次 | 总计 | 占比 |
|---|---|---|---|---|
| 系统提示+Skills清单 | 8k | 5 | 40k | 44% |
| 工具定义 | 6k | 5 | 30k | 33% |
| 历史与任务输入 | 2k | 5 | 10k | 11% |
| 模型输出 | 约2k | 5 | 10k | 11% |
| 合计 | 36k | 5 | 90k | 100% |
数据是估算值,每个项目不完全一样,但看结构就够了:真正给你解决业务问题的"模型输出"只占很小一部分,大头被系统提示和工具定义吃掉。所以精简系统提示和工具定义,是所有优化里收益率最高的动作。
另一个容易被忽略的点是轮次放大效应。任务轮次越多,前面的固定开销被重复的次数越多。把轮次从8轮降到5轮,省的不是3轮的钱,而是3轮乘以全部固定开销的钱。这也是为什么我会建议先做减法、再调细节。
2. Skills瘦身:装得多不等于能力强
2.1 先分清Skills和Agent的关系,别把概念搞混
Skills的本质是一段可复用的指令、代码或行为模板,相当于给Agent配好的一套"操作手册"。Agent是决策主体,Skill是能力模块。很多人在调优时完全不知道从哪下手,就是因为把这两个概念混在一起了:到底是Agent的决策逻辑出了问题,还是Skill本身写得有问题?
我的经验是,先理清层次:Agent负责决定做什么,Skill负责知道怎么做,工具负责实际执行。调优的顺序也是先看Skill数量是否冗余,再看工具是否高效,最后才回头审视Agent的提示词和流程设计。层次不清的时候,你做的很多优化都是在瞎试。
2.2 滥装Skills的三个典型症状
系统提示被撑爆:很多框架会在每次任务开始前把所有Skill的元信息加载进来。Skill装得越多,哪怕根本没用上,每次请求也都在为它们付token费。
选择困难:Skill一多,模型每次都要思考"这个任务该用哪个Skill"。如果几个Skill的描述边界模糊,它甚至会来回切换,白白浪费好几轮。这个过程在日志里看特别明显,经常是同一个任务触发了两三个Skill,互相覆盖。
指令互相打架:Skill A说"生成图片时优先使用写实风格",Skill B说"默认输出插画风",模型被互相矛盾的指令拉扯,最后产出的效果自然不稳定。
Skill的数量不是能力的证明,而是成本的来源。这句话是我做完第一次瘦身之后最大的感悟。
2.3 我是怎么把一个21个Skills的Agent精简到6个的
第一步,盘点。把项目里所有Skill列出来,通过日志统计最近7天每个Skill的实际调用次数。这一看吓了一跳:21个Skill里,真正被高频调用的只有4个,还有9个一次都没被触发过。那9个"僵尸Skill"直接删掉,因为它们除了常年躺在系统提示里占地儿,没有任何用处。
第二步,合并。剩下12个里,有一部分功能高度重叠。比如"生成产品文案"和"生成广告语"本质上是一类能力,合并成一个"营销文案生成",用参数区分场景。这一步对功能没影响,但Skill数量实实在在降下来了。
第三步,重写触发描述。每个Skill描述里明确写清楚"什么情况下使用、什么情况下千万不要使用",避免模型把相似任务分配给错误的Skill。这个动作很小,但误触发率直接降了一半。
最终结果:21个减到6个,系统提示从大约12k token降到6k,单任务token消耗下降了差不多18%。功能没有任何损失,效果反而更稳定了。另外提醒一句,从社区下载的Skill不要直接丢进生产项目,很多只是演示级质量,还可能带着一堆跟你的业务完全无关的默认指令。
3. 子代理:该拆才拆,拆了就要拆干净
3.1 子代理到底解决了什么问题
子代理的核心价值只有三个:独立上下文、状态隔离、并行执行。
- 独立上下文:子代理有自己的system prompt,不受主代理庞杂上下文的干扰。比如一个"图片质检"子代理,只需要极少几条规则,就能专注做判断。
- 状态隔离:子代理内部状态混乱不会污染主代理,让主代理保持干净的决策环境。
- 并行执行:多个子代理同时跑,整体耗时更短。
问题在于,很多人把子代理当成了"架构升级"的象征,不管三七二十一先把任务拆成一大堆子代理,结果成本曲线直接起飞。
3.2 子代理成本失控的经济账
子代理的成本主要在两个地方:启动成本和上下文传输成本。
启动成本很好理解,每个子代理被调用时,都要把自己的system prompt完整加载一遍。假设一个子代理的system prompt是3000 token,调用10次就是30000 token。如果项目里有5个子代理,这个数字很容易变成六位数。
上下文传输成本更隐蔽。主代理把任务交给子代理时,通常需要把相关的背景信息、历史摘要、任务目标全部传过去,传得越全,这笔费用越高。嵌套层级越深,每一层的"转述+摘要"都在损耗信息,也在消耗额度。三层嵌套下来,任务还没正式开始干,几千token已经没了。
我的建议是:先问"这个任务不用子代理行不行",再问"用了子代理能不能明显提高成功率",最后才考虑用。如果两个问题的答案都是否,那你就是在为复杂度付钱。
3.3 一次"主进程退出"问题的排查过程
这里讲一个真实排查案例。有个项目用headless方式跑子代理,结果子代理执行到一半,主进程直接退出了。第一反应是子代理代码的问题,但看日志发现主进程是正常退出,没有崩溃栈。
排查链路大概是这样的:
- 先看主进程退出码与退出前日志,确认是"主动退出"还是"异常退出"。
- 再看子代理是否有未捕获异常。结果发现子代理在无界面模式下执行某个工具时抛了异常,异常没有向上传递到主进程的处理逻辑。
- 最后确认根因:子代理超时策略没设,异常把子代理所在的任务队列打挂,主进程探测不到子代理心跳后自行退出。
修复方案其实很简单:给子代理调用包一层异常兜底,设置超时时间,主进程定期探测子代理状态,而不是被动等结果。这类问题在开发期很容易被忽略,因为很多框架的示例代码里压根没写异常处理,生产环境一跑就露馅。
3.4 别被harness和agent的概念绕进去
讨论子代理的时候,很多人会提到harness。我自己的理解是,harness是让Agent跑起来的壳,负责请求循环、工具注册、输入输出解析和生命周期管理;Agent则是里面的决策逻辑。两者是"运行环境"和"决策实体"的关系。
很多项目的问题是,为了显得"架构正规",先引入一大套harness能力,建了复杂的子代理调度层,最后核心业务逻辑没写几行。如果项目本质就是"一个循环+几个工具函数+几个Skill",那就老老实实先写轻量实现,等任务真的需要隔离和并行时,再引入harness和子代理不迟。先跑通再架构,而不是先架构再跑通,这个顺序几乎能帮你避开一半的复杂度问题。
4. 工具优化:把每次调用都变成低成本调用
4.1 工具描述和Schema优化
工具列表是每轮请求都会完整发送给模型的,所以工具优化的收益会被乘以"请求轮次",是整个系统里放大系数最高的优化项。
先说描述。工具描述不是写给开发同事看的API文档,而是写给模型看的"使用说明书"。好的描述要回答三个问题:这个工具是干什么的?什么时候一定要调用它?什么时候千万别调用它?举个例子,查天气的工具可以这样写:"调用天气查询接口。仅当用户明确表达需要知道天气时调用。不要在用户只是闲聊时调用。"
再说参数Schema。我见过太多把工具参数设计成多级嵌套对象的情况,模型解析这种Schema特别容易出错,出错就会重试,重试就烧额度。我的习惯是:能用字符串解决就不要用数组,能用一级对象解决就不要用二级嵌套,必填字段尽量控制在1到3个。
4.2 工具数量与返回结果瘦身
工具数量直接影响模型的选择成本和误触发率。我的经验法则是:单个Agent的正常工具数量控制在5到8个,超过10个就要考虑合并。同类工具可以合并成一个带type参数的通用方法,比如把"获取用户信息""获取用户订单"合并成"获取用户数据(type)"。数量降下来之后,模型的选择空间变小,误触发的概率自然就低了。
返回结果同样要瘦身。很多后端接口一次性返回几百KB的JSON,Agent读完这些数据,上下文窗口被塞得满满的,后续对话质量下降不说,额度也烧得厉害。我的做法是在工具内部做字段筛选,只返回模型决策真正需要的字段,必要时直接返回一段摘要文本,而不是原始JSON。
4.3 用流程规则减少无效调用
有些Agent像"手停不下来"的人,用户随便问一句"今天天气如何",它先调用工具去解析用户意图,再调用天气接口,最后还调一个时间工具确认今天是哪一天。这些多余调用完全可以通过系统提示约束掉。
我在系统提示里会写一条固定规则:"你可以直接回答的问题,绝不调用任何工具;只有明确需要外部数据或执行动作时,才调用工具。"就这么一句话,工具调用次数能下降一大截。
另外建议加上"失败回退"逻辑:同一工具连续失败两次就停止重试,直接向用户说明当前状态。重试是隐藏的烧钱机器,很多不明不白的额度流失都是无限重试造成的。工具优化的本质是降低模型的不确定性,模型越清楚"什么时候该用什么工具、不该用什么工具",一次成功的概率就越高,额度利用率自然越高。
5. 拿到20%额度提升的落地清单与复盘
5.1 先记录基线,再动手
没有基线的优化全是耍流氓。动手之前,先花两到三天正常使用Agent,记录几个核心指标:
- 单任务平均token消耗
- 平均请求轮次
- 工具误触发率(在工具调用日志里,结果是无意义调用的占比)
- 失败重试率
- 任务成功率
- 日/周额度消耗
后面每一次优化改动,都用这些指标去对比,才能知道哪项变化来自哪个动作。如果一口气改了好几件事,出了问题你根本不知道是哪一步引起的。
5.2 五步操作清单
这套流程我在几个项目上反复验证过,可以直接抄作业:
- 冻结新增。优化期间不增加任何新的Skill、子代理、工具,先把存量理顺。
- 禁用第三方Skill。只保留项目自己写的核心Skill,跑一天观察效果和成本变化。
- 子代理盘点。把每个子代理标注"必要性:高/中/低"和"单次平均调用成本",简单任务接管回主代理直接做,砍掉一半低必要性的子代理。
- 工具重构。同类合并、描述重写、返回结果精简,把工具数量控制在8个以内。
- 系统提示减肥。把系统提示压缩到只保留必要原则、关键规则和核心工作流,目标是不超过4k token。
每一步保留日志,不要好几件事同时改。
5.3 优化前后数据复盘
拿我做过的其中一个项目举例(不同项目数据会有差异,但趋势是一致的):
| 指标 | 优化前 | 优化后 | 变化率 |
|---|---|---|---|
| 系统提示+Skill清单 | 约21k token | 约8k token | -62% |
| 单任务平均请求轮次 | 11轮 | 7轮 | -36% |
| 工具误触发率 | 23% | 6% | -74% |
| 失败重试率 | 17% | 5% | -71% |
| 单任务平均token消耗 | 约68k token | 约42k token | -38% |
| 月度额度使用 | 100% | 78% | 节省22% |
单任务token消耗下降了38%,换算到月度额度,就是标题里说的"多出至少20%"。
还有另一个反直觉的结果:任务成功率反而从84%升到了91%。原因不难理解——模型不再被一堆冗余Skill、矛盾指令和庞杂上下文干扰,决策路径短了,犯错的空间也小了。精简不是砍能力,是给真正需要的能力让路。
5.4 之后怎么持续维持这套低复杂度体系
优化不是一次性的,项目和任务会变,Skill调用频率也会变。我的习惯是每两周做一次"用量体检":看之前那六个核心指标,自动或手动标记低频Skill、低利用率工具、长耗时子代理,然后继续做减法。
我自己常用一个判断标准:如果新增一个Skill或工具,不能稳定省下至少一倍的token,那就不值得加。用这个标准卡住,复杂度基本不会反弹。
最后分享一个心态上的体会。我做过这么多Agent项目,最普遍的坑不是"不会实现功能",而是"停不下来地加功能"。Agent实际能力的上限,从来不取决于你给它配了多少工具和Skill,而取决于它在需要的时候能不能精准调用最合适的那一个。每次想加新东西之前,先花几分钟算一笔账:这次任务的固定开销会涨多少?误触发概率会不会变高?如果答案都是"会",那这件事大概率就是下一个复杂度税来源。控制住这个冲动,额度利用率提升20%是迟早的事,项目整体也好维护得多。