ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent Token 消耗优化:Skill、子代理与工具精简

Agent Token 消耗优化:Skill、子代理与工具精简 1. Agent 复杂度为什么直接吃掉你的额度1.1 一次请求背后token 是怎么被算掉的很多人第一次接触 Agent 开发注意力全在能不能跑通上等到账单出来才发现额度掉得比预想快得多。问题不在模型本身贵而在于你每发一次请求带上去的东西远超你的直觉。一个典型的 Agent 请求实际发送给模型的内容包含这么几块系统提示词、工具Tool定义列表、当前对话历史、检索回来的上下文、以及用户这一轮的输入。系统提示词动辄两三千 token工具定义如果挂了十几个每个定义带描述、参数 schema、枚举值轻松再吃掉两三千。对话历史随着轮次累积线性增长跑到第十轮历史本身可能就是前面所有内容的数倍。真正要命的是这些内容是每一轮都重发的。模型没有记忆它是无状态的你看到的记得前面说过什么是客户端把历史拼回去再发一遍。所以你以为的一轮对话实际计费的是提示词 工具定义 完整历史 新输入的总和每一轮都重算一次。这就解释了一个常见现象同样问一个问题单纯聊天消耗可能几百 token挂上 Agent 之后消耗翻了五到十倍。多出来的部分大部分不是你真正需要的智能而是架构设计带来的固定开销。1.2 复杂度账单上下文、工具定义、历史消息的三重叠加把 Agent 的成本拆开看主要来自三个叠加项。第一层是固定开销。系统提示词和工具定义属于这一层它们和任务难度无关你哪怕只让它回一个字这部分也照发不误。很多团队的提示词是从模板或别处抄来的堆了一大堆通用规则实际当前任务根本用不上纯属浪费。第二层是上下文开销。检索增强也好、文件内容也好、网页抓取结果也好塞进去多少就按多少计费。常见错误是把整个文件、整篇文档不加裁剪地丢进去明明只需要其中一小段。第三层是历史开销。这个最隐蔽因为它随轮次增长。一个跑二十步的自动化任务前面积累的历史在中后期会变成大头而其中大量内容失败的尝试、冗余的工具返回对最终结果毫无贡献。三层叠加之后你会发现额度不够用往往不是模型问题而是架构膨胀的问题。复杂度越高固定层越厚任务越长历史层越重。你为复杂度买的单本质上是在为没必要的上下文付费。1.3 20% 是怎么算出来的不是拍脑袋我拿几个真实项目做过对比把优化前后的 token 消耗拉出来看节省幅度稳定落在 20% 到 45% 之间。为什么差距这么大取决于你原来的架构有多臃肿。如果原来的系统提示词和工具定义已经很精简那你能挤出来的主要是历史层的优化空间大概在 20% 出头。如果原来是把所有功能塞进一个大 Agent、工具挂了十五六个、上下文全量注入那砍掉一半都可能。所以至少 20%这个说法是给一个相对克制但仍有优化空间的架构留的保守估计。它的来源不是玄学而是三个可量化的动作Skill 按需加载省掉固定开销、子代理隔离上下文省掉历史开销、工具精简省掉定义开销。三块加起来20% 是个很稳的底线。下面我把这三块逐个拆开讲每一块都会落到具体的写法和数字上。2. Skills把臃肿的提示词拆成按需加载的模块2.1 Skill、Prompt、Agent 三者的边界到底在哪这三个词现在被混用得很厉害理清楚对省钱很关键。Prompt是你发给模型的一段文字指令它本身没有加载机制你写多少就发多少。Agent是一个能自己决定调用什么工具、跑多少步的执行体它有自己的循环。Skill介于两者之间准确说它是一个按需加载的能力包里面通常包含一段指令、可能还有脚本和资源文件只在被需要时才被读进上下文。关键就在按需两个字。传统的做法是把所有能力都写进系统提示词反正模型可能用得上。Skill 的做法是系统提示词里只留一句你有哪些 Skill 可用各自一句话描述具体内容等模型判断需要时再展开。没被触发的 Skill实际消耗的 token 接近于零。这就是省钱的第一个杠杆。一个功能齐全的 Agent如果它有八个能力分支全写进提示词可能就是四千 token 的固定开销拆成 Skill 之后常驻的可能只有三百 token 的索引真正用到的那个再补上五百到一千。固定开销直接砍掉大半。注意Skill 不是另一种 Prompt 写法它的价值在于延迟加载。如果你把 Skill 内容又原样塞回系统提示词那等于没拆。2.2 一个可用的 Skill 结构长什么样我现在的习惯是一个 Skill 目录里至少放三样东西一个描述文件、一份主指令、以及可选的脚本或模板。描述文件负责让人和模型知道这个 Skill 干嘛的字段精简到几句名称、一句话用途、触发条件。这部分是常驻的所以越短越好通常控制在二三十个 token。主指令是实际被加载的内容写的时候按这个任务该怎么一步步做来组织而不是写成百科。我踩过的一个坑是主指令里塞了大量背景知识和注意事项后来发现真正被执行到的只有流程部分那些背景八成用不上。现在我会把背景知识单独放只在特定分支需要时再引用。脚本和模板是真正省 token 的地方。凡是能用一段确定性脚本完成的比如格式转换、字段提取、文件校验就不要让模型去思考着做。把脚本路径写进 Skill让模型调用脚本返回结果。这比让模型推理省得多也更稳定。一个极简的目录结构大概是这样skills/ >## 输入 一份或多份 CSV/TSV 文本列名可能大小写不一致、含空格或中文。 ## 处理 1. 调用 scripts/normalize.py参数为输入文件路径。 2. 脚本负责列名规范化、去空、类型推断。 3. 若脚本报错读取错误信息判断是编码问题还是结构问题。 ## 输出 按 templates/report.md 的格式输出包含处理行数和异常行清单。第三步写脚本。normalize.py做确定性的活统一列名、去重、补默认值。这部分是纯代码不消耗模型额度只在失败时把错误信息回给模型让模型决定下一步。第四步测触发。这一步很多人偷懒结果上线后模型该用的 Skill 不触发不该用的乱触发。我的做法是拿二十来条真实输入跑一遍看命中率。命中率低于八成就去改索引里那句话的措辞通常是把触发条件写得更具体或者加一两个典型例子。2.4 Skills 用起来最容易踩的几个坑第一个坑是描述写太长。常驻部分每多一句话每次请求都多付一次费。我见过有人把 Skill 描述写成一段说明文八个 Skill 加起来常驻就一千多 token等于白拆。第二个坑是把该写进脚本的逻辑写进指令。像把所有日期格式统一成 YYYY-MM-DD这种事让模型逐条判断既慢又费写个正则十行搞定。凡是规则确定的都交脚本。第三个坑是Skill 之间职责重叠。两个 Skill 都能处理清洗数据模型就会犹豫触发不稳定还会把两个都读进来。我现在维持一个原则一个能力只在一个 Skill 里出现重叠就合并。第四个坑是忘了版本管理。Skill 的主指令是会不停调整的改之前记得留个记录不然哪次改坏了触发率都不知道从哪查起。实操心得判断一个 Skill 是否值得拆看它被调用的频率。高频且轻量的放常驻低频或重量级的做成 Skill。反过来的话拆了也没用。3. 子代理编排把大上下文拆给专注的小角色3.1 什么时候该上子代理什么时候纯属折腾子代理Sub-agent最被误解的一点是很多人以为它是更高级的写法逢事就上。实际它解决的是一个非常具体的痛点单一上下文的膨胀。当一个任务需要翻很多资料、试很多路径所有中间过程都堆在一个上下文里历史会迅速变重。子代理的思路是把一段独立的、目标明确的子任务切出去用一个新的、干净的上下文去跑跑完只把结论带回来。主上下文里只留一句子任务结果是什么不带中间过程。所以判断标准很干脆如果一段子任务的中间过程对主任务没用就值得切出去。比如从十份文档里各提取一段摘要中间读文档的过程主任务根本不关心它只要十段摘要。这种就该用子代理。反过来如果子任务的每一步都需要主上下文里的信息来判断切出去反而要多传一次上下文得不偿失。我见过有人把根据前面讨论改代码这种强依赖上下文的任务拆成子代理结果每次都要把完整历史传过去消耗比不拆还高。3.2 上下文隔离与结果回传的写法子代理省钱的核心机制是上下文隔离。具体做法是主代理只传给子代理完成任务所需的最小信息子代理在自己的上下文里跑完只返回结构化的结论。关键在于最小信息这四个字。很多人偷懒直接把主上下文整个传给子代理那隔离就失效了。我现在的习惯是给子代理定义一个明确的入参结构只要必要的字段。{ task: 提取关键指标, input: 单份文档内容, output_schema: { metric: string, value: number, source_line: number } }回传的时候也讲究。让子代理按固定 schema 返回主代理拿到的是精简结构不是一大段自由文本。自由文本回传看着方便实际又多了一层要解析的内容还是费 token。一个常见误区是子代理的返回还要解释一下。不需要。主代理要的是结论解释属于子代理内部的事留在它自己的上下文里就行。3.3 一个编排流程的完整拆解我拿一个实际做过的任务来拆批量分析二十份用户反馈输出一份分类汇总。不拆的写法是主代理循环读二十份文件每读一份就在主上下文里累积二十份下来历史极长后期每轮都在重发前面所有内容。拆成子代理之后是这样主代理只做三件事——决定要处理哪些文件、把文件逐个派给分析子代理、收集返回的分类结果。分析子代理每次只拿到一份文件和一个固定 schema跑完返回分类 理由一句。主代理的上下文里每份文件只占一行结果二十行汇总就是二十行。整个任务跑完主上下文增长有限而不是线性叠加二十份原文。实测下来这个场景省了将近四成的 token主要省在历史层。这里有个细节要注意子代理不要嵌套太深。我试过主代理派子代理、子代理再派子代理结果每一层都要传上下文中间的传递成本比隔离省下的还多。两层基本够用三层以上要非常谨慎。3.4 子代理的常见问题与排查方向子代理跑不起来八成是三类问题。一类是入参没传全。子代理的上下文是干净的它不知道主代理知道的一切少传一个字段它就抓瞎。排查方法是把子代理单独提出来跑一遍看它在缺少什么信息时卡住。另一类是回传格式不对齐。子代理返回的自由文本和主代理期望的结构不一致主代理解析失败就会反复重试越重试越费。解决办法是回传强制 schema并在子代理指令里明确只返回 JSON不要额外说明。还有一类是任务切分粒度不对。切得太粗子代理内部又变成一个大上下文隔离没意义切得太细通信成本超过收益。经验值是一段子任务的执行步骤在五到十五步之间比较合适太短不如直接做太长说明还能再切。提醒子代理是省 token 的手段不是架构洁癖。如果一个任务在主上下文里跑得挺好别为了看起来更专业去拆它。4. 工具优化少即是多把定义数量控制住4.1 工具定义是怎么悄悄膨胀上下文的工具Tool定义是固定开销里最容易被忽视的一块。每个工具都要附带名称、描述、参数 schema、枚举说明一个中等复杂度的工具定义吃掉两三百 token 很正常。挂十个就是两三千而且每轮都发。问题在于很多人加工具的时候完全不考虑成本觉得多一个能力总是好的。结果是模型面前摆着十五个工具它要花更多注意力去选选错的概率也上升然后失败重试又烧一轮。工具多不光费定义的钱还费决策的钱和试错的钱。我做过一个粗暴的对比同一套功能一个版本挂十二个细粒度工具另一个版本合并成四个粗粒度工具。后者在固定开销上直接省了两千多 token任务成功率还高了一点因为模型不用在一堆相似工具间纠结。4.2 工具合并与参数收敛的实操方法合并的原则是按操作对象而不是按动作来组织。比如原来有读取文件写入文件删除文件列出文件四个工具如果它们操作的是同一类对象可以合并成一个带action参数的文件工具。{ name: file_op, description: 对文件执行读、写、删、列操作, parameters: { action: { enum: [read, write, delete, list] }, path: string, content: string, 仅 write 时使用 } }合并之后定义变短但要注意别过度合并。把毫不相干的工具硬塞一起模型反而更难判断何时用哪个。我的经验是合并的前提是这些操作共享同一个对象和同一套参数否则分开更清晰。参数也要收敛。常见浪费是把所有可选参数都写进 schema还配上一大段说明。实际高频用到的参数往往就两三个其余的可以放默认值说明能省则省。参数描述写清楚就够了不用写使用教程。4.3 工具返回内容的瘦身工具定义省的是发出去的工具返回省的是收进来的。返回内容同样进上下文而且进了之后会随历史一直堆着。最常见的浪费是工具返回一大堆原始数据其中只有一小部分被用到比如查一个字段返回整个记录读一个配置返回整份文件。我现在的做法是工具层做一次裁剪只返回调用方明确要的字段。还有个细节是错误信息。工具报错时如果把完整堆栈抛回来那一段就永久留在历史里了。合理的做法是返回简短的错误码和一句人话描述详细诊断信息写到日志不进上下文。# 工具内部返回前做裁剪 def query_user(user_id): raw db.get(user_id) return {name: raw[name], status: raw[status]} # 只回必要字段实操心得让工具返回结构化的、字段可控的结果比返回自由文本省得多。自由文本看着信息全实际九成内容在后续轮次里都是负担。5. 额度优化实测与常见问题速查5.1 优化前后的对比数据我把一个中等规模的 Agent 项目做了完整优化前后各跑一轮标准测试集数据大致是这样。优化项优化前优化后节省系统提示词 工具定义常驻约 5200 token约 1800 token约 65%单轮平均历史开销约 3400 token约 1900 token约 44%单任务平均总消耗约 78000 token约 51000 token约 35%任务成功率82%88%6 个百分点注意最后一行优化之后成功率反而涨了。这不是巧合。上下文越干净模型注意力越集中出错概率越低。臃肿不只是费钱还费准确率。20% 是针对已经比较克制的架构说的这个项目优化前的基线偏高所以省得多。如果你的起点本身就不臃肿别指望砍一半但稳住 20% 是现实的。5.2 常见问题速查表现象可能原因排查方向额度掉得比预期快固定开销过大看系统提示词和工具定义总长度任务越跑越慢越费历史线性累积检查是否有长循环未清理中间过程Skill 不触发索引描述太模糊补充触发条件和典型例子子代理反复重试回传格式不一致强制 schema去掉自由文本工具选错频繁工具数量过多按对象合并工具减少数量单轮消耗突然暴涨某工具返回超长内容检查工具返回是否做了裁剪这张表是我自己排障时最常用的几行基本能覆盖八成的问题。5.3 我个人的几条经验搞 Agent 优化这两年最深的体会是省额度不是一个专项任务而是一种默认习惯。每加一个 Skill、每挂一个工具、每切一个子代理都要顺手问一句这会不会让固定开销变大。养成这个习惯之后根本不用专门做优化架构自然就是瘦的。另一个体会是先测再改。很多人凭直觉去砍砍错地方反而更差。我的做法是先加一层统计把每轮的提示词、工具定义、历史的 token 数分别记下来看看大头在哪再针对性地动。数据指向哪就改哪比自己猜靠谱得多。还有一点是别追求极限压缩。我试过把提示词压到极简结果模型理解不了任务边界反而要靠多轮澄清总体更费。优化的目标是去掉浪费不是越短越好。留够必要的指令砍掉确定没用的部分这个平衡点需要动手测几次才能找准。最后分享一个小技巧把每个月的消耗按固定开销 / 上下文 / 历史三块记下来画个趋势。你会发现异常往往出现在某次架构改动之后有了趋势线定位问题快到飞起。这个习惯比任何单次优化都值钱。
返回列表