ARTICLE DETAIL

资讯详情

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

Agent开发成本控制:Token消耗、Skills与子代理优化实战

Agent开发成本控制:Token消耗、Skills与子代理优化实战 我那天打开 API 控制台看了一眼账单差点把咖啡喷屏幕上——同样一个任务我在本地反复调 Agent 的时候Tokens消耗比上周足足多出小一半。仔细排查了一圈问题不是出在模型本身而是我自己把 Agent 的架构越搞越复杂子代理东一个西一个Skills 装了十几套工具列表恨不得全给模型摆出来。那一刻我突然意识到一个很朴素的道理Agent 的复杂度不是功能是账单。这个体会我想值得专门写一篇东西分享出来。现在 Agent 开发圈子里有一个很明显的风向就是框架越重越好、技能越多越好、子代理越分工细越好但作为一个真的把 Agent 当成日常生产工具在用的人我越来越确认复杂度的边际收益是递减的而边际成本是线性上升的。这篇文章适合所有正在用 Claude Code、Codex、OpenCode 这类工具做 Agent 开发的人也适合那些刚开始接触 Skills 和子代理机制、想给自己的智能体降本提效的开发者。我会从 Token 消耗的底层逻辑讲起把 Skills、子代理和工具优化这三块怎么省钱、怎么避坑都掰开揉碎聊一遍最后给你一套可以直接上手的方案。1. 先搞清楚你的额度到底花在哪了1.1 一次请求的Token消耗拆解很多开发者在省额度这件事上有一个误区总觉得模型输出越短越省钱。实际上对于 Claude、GPT 这类大模型 API 来说输入的上下文成本占了消耗的大头。为什么因为每轮对话都要把历史消息、System Prompt、工具定义、上下文窗口里的参考资料全部重新发给模型推理一次。我习惯把一个 Agent 单轮请求的成本粗略拆成四块固定开销System Prompt、工具定义、全局设定这部分每一轮都在消耗累积开销历史对话记录越聊越长每一轮都要重新走一遍任务开销当前这一轮的用户输入、Agent 自己的中间思考输出外部回报工具调用返回的结果、子代理回传的总结、文件内容的读取结果所以你会发现一个特别反直觉的现象一个任务能不能省额度很多时候不取决于你让模型少说话而取决于你怎么设计它的上下文结构。一个写得很烂的 Skills可能每个任务加载进来以后占用 3000 Tokens 的上下文看起来不多但如果你有 15 个这样的 Skills 同时驻留那就是 45000 Tokens。模型每次思考的时候都背着这一大堆东西即使一个字都不用钱也已经花了。1.2 Agent复杂度模型每多一个环节多烧一轮Token做过 Agent 开发的人应该都有过这种经历原本一个直接调用模型就能解决的任务为了让 Agent显得强大硬是加了一个规划器、两个子代理、四个工具结果任务跑起来以后Agent 在最简单的步骤上也要反复跨越多个上下文环境来回调度。我曾经用过一个非常重的 Agent 框架做智能客服里面嵌套了三层子代理一个负责意图识别一个负责知识库检索一个负责话术生成。理论上很合理对吧但实际跑起来一条订单什么时候到这种 5 秒钟就能答完的问题主代理要发起任务、子代理要初始化自己的上下文、检索完还要回传摘要主代理再根据摘要生成回复整个链路走下来花了 2 万多个 Tokens。而且这里还没算失败重试的成本。子代理一旦某个环节出错你往往不会立刻发现而是等它把错误结果回传给主代理之后主代理才发现不对再派一个新的子代理从头跑一遍。我管这个叫连环翻车税——每次翻车前面的所有 Token 全部白烧。2. Skills把重复思考变成定向调用2.1 先分清 Skills 和子代理的区别网上关于这两个概念的讨论特别多尤其是搜 Agent、Skills、子代理相关热词的时候最常见的两个问题就是Skill 和 Agent 区别以及用 Skill 还是用子代理。我自己的理解其实很简单Skills 是预设的知识与行为模板它告诉模型当你遇到这类任务时应该按这个标准流程来做子代理是独立的执行单元它拥有自己的上下文窗口独立循环推理再把结果交回主代理两者最大的区别在于上下文隔离。子代理的上下文和主代理是分开的所以适合处理那些不能让主上下文被污染的任务而 Skills 本质上是在主上下文里直接注入一套方法论的说明让模型少走弯路。正因为这样Skills 在省 Token 这件事上有天然优势它不需要额外启动一轮完整的推理循环也不需要做结果回传所有过程都发生在主上下文里没有多余的开销。但前提是这个 Skill 本身得写得够好。如果你的 Skill 只是把一段 Prompt 包装成 YAML然后往上下文里塞一堆冗长的步骤说明那它不但不省 Token反而会因为上下文膨胀让你付更多钱。2.2 高质量Skill的四个设计要求我自己写了不少 Agent Skills也帮团队建过内部的 Skills 库踩了无数坑之后总结出四个核心要求。第一触发条件要明确。一个 Skill 的开头必须写清楚This skill applies ONLY when...用来让模型判断这个技能是否适用于当前场景。模糊的触发条件会让模型在面对普通任务时也去加载不相关的技能纯属浪费。第二知识密度要高废话要少。我之前见过有人写一个做图片生成的 Skills光是描述什么是好图片就写了一千多字充满了抽象形容词。真正有用的 Skills 应该像工具说明书步骤、参数、限制、示例一句闲话都不要。第三要带可复用的模板或代码骨架。一个 Skill 如果想要让模型稳定输出特定格式最好直接给出示例模板让模型照着填而不是自己发挥。第四要有反模式提醒。告诉模型不要做什么往往比要做什么更能节省 Token因为它能减少模型试错和额外追问的概率。2.3 一个可复用的Skill编写示例写一个非常简单的例子来说明什么叫高质量 Skill。假设你想做一个 LaTeX 排版的 Skills这是最近我经常被问到的一个场景因为科技论文写作越来越依赖 Agent 辅助排版。先看反面案例——一个只有三行描述的 LaTeX Skill--- name: latex-formatting description: 用于LaTeX排版。 ---这个东西放进 Agent 里基本只会让模型知道有个东西能排版具体怎么做完全靠模型自己猜生成出来的 LaTeX 代码经常因为缺包、缺环境、格式错误反复报错。你让它改三遍三次 Prompt 全走完了Token 当然花得多。再看一个能用、且省 Token 的版本--- name: latex-paper-layout description: 用于 ACM 双栏论文模板的 LaTeX 排版与修改仅当用户要求排版或修改 LaTeX 论文时使用。 --- - 文档类型固定为 \documentclass[sigconf]{acmart} - 标题区必须包含 title、author、affiliation、email、abstract - 图表环境统一使用 figure* 双栏跨栏图表内容用 graphicx 宏包 - 参考文献格式统一为 \bibliographystyle{ACM-Reference-Format} - 禁止修改正文的 \section 层级结构、文末的生成日期 - 典型布局模板直接填内容[在此给出一个完整可编译的 .tex 骨架]这个版本把模型需要在每次排版时自己推断、尝试的信息全部预置好了模型拿到任务后直接套模板几乎不会因为格式问题反复重跑。一次排版任务能少跑好几轮。注意Skills 不是越多越好更不是越详细越好。每个常驻 Skills 都在吃你的上下文预算。给 Agent 装 20 个 Skills 却不做按需加载等于每天多背几个大包袱上路。3. 子代理好钢用在刀刃上别动不动就fork3.1 子代理的适用边界与代价子代理是个好工具但问题是现在的 Agent 开发社区里很多人把子代理用成了炫技——不管任务多简单都要拆出一个独立的子代理去跑。要知道fork 一个子代理是有明确成本的子代理有自己的上下文窗口需要独立初始化每个子代理从建立到输出结果至少经历一次完整的“指令解析-执行-总结”循环子代理回传的信息会继续占用主上下文空间我看到过一份针对某个 Agent 项目的监控数据平均每引入一个子代理相同任务的 Token 消耗增加约 35% 到 50%。换句话说每 fork 一个子代理你的账单就会肉眼可见地厚一截。那什么情况下才应该用子代理我自己总结的边界是三个必须必须隔离上下文时比如主上下文里已经有大量与当前任务无关的对话历史与其让模型在嘈杂信息里找线索不如把子任务派给子代理让它在一个干净环境里处理必须并行处理时有些任务天然可以拆成多个独立分支同时在子代理中执行最后汇总结果这时候并行的收益大于独立调度的成本必须使用不同模型时有些细分的子任务可能更适合用小模型、便宜模型来跑这时子代理的隔离特性正好用来切换模型但国内开发环境里这个用得少可以先不做重点除此之外能用 Skills 解决的坚决不要用子代理。如果只是想让模型按流程做Skill 就够了。3.2 子代理的致命陷阱上下文碎片化与额度双花子代理省不省 Token有一个更关键的问题在于它的内部循环。很多人以为子代理跑完就完事了但真相是子代理从接收任务到给出结果之间可能要经过好几轮的内部思考、工具调用、自我纠错。我调过一个专门做网页信息提取的子代理它的内部循环大概是读取需求 → 请求 URL → 发现页面结构变了 → 调整选择器 → 重试 → 解析内容 → 整理摘要。每一步都走一遍完整的模型调用等它把结果交回主代理的时候光是它的内部消耗可能就是主代理直接处理的三倍。这还没算额度双花的坑子代理把结果交回给主代理以后主代理通常还要基于这个结果再做一轮分析或生成。结果就是同一个数据子代理花了一次钱理解一遍主代理又花钱读一遍摘要。没有隔离好上下文的话主代理还可能会把子代理的原始输出整个塞进自己的上下文那就相当于把子代理的完整消耗又复制了一份。所以如果你必须用子代理请务必遵循两条铁律子代理的输出必须经过压缩交回主代理的是一份摘要 关键数据而不是原始日志把子代理的设计目标限定在产出结构化结论不要让它做open-ended的探索任务3.3 一个容易被忽略的坑子代理运行失败导致主进程退出这个坑是从真实事故里学到的。我们在一个 headless 环境里批量跑 Agent 任务时遇到过dsh headless 运行子代理导致主进程退出的问题——具体表现是子代理运行到一半报错整个主进程直接崩了前面所有已经跑完的子任务结果全部没保存。排查了半天发现根因是子代理在实现上并不是真正的独立进程它和主代理共享同一个调度进程的生命周期。如果在子代理执行期间发生了未捕获的异常比如工具返回超时、某个资源被释放这个异常会一路抛回到主进程的 event loop直接把整个进程带崩。这个问题对额度优化的启发是别以为用子代理就能做进程级容错它只能做上下文级隔离。如果你的任务需要很强的可靠性应该在主代理外层再做一层任务管理和重试机制而不是把所有希望寄托在子代理不会出错上。每崩一次重跑的成本全部由你自己买单。这个坑也提醒我们在设计任何 Agent 系统的时候崩溃恢复和任务快照的成本应该被计入复杂度税里。复杂度不只是让每轮任务变慢还会让失败场景的恢复成本贵上好几倍。4. 工具优化让模型少试错、一次过4.1 工具描述是门学问如果说 Skills 解决的是模型老是走弯路的问题那么工具优化解决的就是模型在各种工具选择之间反复犹豫的问题。你有没有遇到过这种情况Agent 明明可以直接调一个函数解决问题它却在几个工具之间来回试探先读了一堆文件又搜了一堆资料最后才下定决心调用正确的工具。这个试探过程每次往返都是 Token。而试探的根源很多时候是工具列表设计得太烂。优化工具设计的第一件事是精简工具描述。API 的 function calling 机制是这样的模型在决定要不要调用某个工具时会阅读工具的名字和描述。描述写得越模棱两可模型越容易误判。举个例子一个文件搜索工具的描述如果写成Search files in the workspace模型可能什么都想搜一遍。但如果写成Search for filenames by keyword; use only when user asks to find a file by its name; for content search use grep instead模型就能在恰当的时机精准调用而不是把它当成万能搜索。工具列表里还存在一个选择干扰现象可调用工具越多模型做选择决定的难度越大出错率也越高。每多一个工具模型在决策时就需要多读一个工具定义、多权衡一次可能性。所以精简工具列表本身就能直接降低 Token 消耗。4.2 MCP服务器数量要克制现在很多 Agent 框架都支持 MCPModel Context Protocol来接入外部数据源和工具。MCP 本身是个好东西但它的罪名和子代理一样——太好用了容易让人失去节制。一个典型场景是开发者为了图省事把十几个 MCP 服务器全挂在配置里包括数据库、日历、邮件、笔记、代码托管平台。结果每轮对话模型都要自动扫描所有 MCP 服务器的工具定义即便某些服务器和当前任务八竿子打不着它的工具描述也全部排在模型面前。我实测过在 Claude Code 里挂一个未使用的 MCP 服务器大约会多消耗好几百到一千多 Tokens 的输入预算。挂三五个可能没什么感觉挂十几个每轮对话的输入就直接翻倍。更麻烦的是很多 MCP 服务器连接时还要做身份验证和握手。一旦某个服务端响应超时整个 Agent 的响应速度都会受影响而 Agent 的进一步操作可能变成继续等待或重试Token 又烧一轮。所以我的建议非常直白MCP 按需启用不用就删。要做数据分析的时候临时连上数据库服务器做完立刻断掉。不要懒不要嫌麻烦这一点能帮你省下至少 5% 到 10% 的输入开销。4.3 工具实践中的节流技巧在实际开发里我发现有几招对工具调用降本特别有效第一给工具加默认参数。如果你的工具函数需要传很多参数尽量在定义时给足默认值。模型不需要反复确认每个参数的值能少输出一轮。第二工具结果截断。很多工具调用的返回结果非常大比如一个文件内容如果不做截断Agent 会把整个结果都塞进上下文。正确做法是在工具返回前先做摘要或者只返回前 N 行/关键字段。这个动作通常能立刻砍掉 30% 以上的输入。第三用批处理工具替代多次单发。如果一个流程需要连续调同一个工具五六次比如逐行处理多个文件不如写一个批量处理工具让 Agent 一次性把参数列表传进来一次调用全部搞定。这个优化立竿见影相当于把 5 次请求的模型推理开销压缩成 1 次。第四明确的工具分工和路由规则。让工具描述里写明如果用户需要 X应优先使用工具 A如果 A 失败才能 fallback 到工具 B这能大幅减少模型在工具选择上的盲目试错。5. 落地一整套方案后我们能省多少5.1 一套组合拳该怎么打讲了这么多理论最后给一套可以直接落地的组合方案。你可以把这套东西当成一个优化检查清单一条一条照着改盘点你当前 Agent 的常驻上下文里有哪些内容System Prompt、全部 Skills 描述、所有工具定义、历史对话长度把不常用的 Skills 移出常驻列表改成按需加载如果框架不支持按需加载那就把不用的 Skills 删掉重写所有 Skill 和工具描述确保触发条件明确、描述精准、示例具体、有反模式提示梳理子代理使用场景凡是能用主上下文直接处理的一律不要 fork 子代理必须用子代理的给子代理输出加上强制摘要要求检查所有 MCP 服务器关掉与当前项目无关的连接给工具调用加上结果截断策略限制工具返回的最大长度如果采用 headless 批量运行给每个 Agent 实例加上任务快照和异常隔离防止子代理失败把整个进程带崩这一套做完以后根据我自己的项目经验Token 消耗平均降低 20% 到 30% 是基本可以实现的。如果业务场景本身非常结构化、工具调用密集优化空间甚至能到 40%。5.2 这个方案的量化测算我拿一个真实项目来做参考。这是一个内部的知识库问答 Agent原来实现了 8 个子代理、15 个 Skills、6 个 MCP 服务器单次问答平均消耗要 28000 Tokens。优化之后子代理缩减到只有 2 个只在文档检索与总结时启用、Skills 精简到 4 个核心技能、MCP 服务器只保留 2 个常开、所有工具返回做了截断、子代理强制返回结构化的 200 Tokens 以内的摘要。优化之后单次问答平均消耗降到了 18000 Tokens 左右。按一天 1000 次调用计算每天省下来 1000 万 Tokens。如果按 API 价格折算成真实的成本一个月省出来的钱足够买很多额外的 API 额度了。再强调一句这里省下来的不只是钱还有时间。上下文越短模型的响应速度越快用户的等待时间越短。我们实际观测到优化后的平均响应延迟从 12 秒降到了 8 秒左右。省额度这件事本质上是在省钱的同时把你的 Agent 也变快了。5.3 踩过的坑与最后的经验最后说几个我没有在正文里详写、但实际开发中非常容易踩的坑。第一个坑是有些人为了省 Tokens 把 System Prompt 压缩到极端结果模型失去了必要的行为约束频繁犯错重试反而更费钱。省 Token 的正确姿势是减少重复计算而不是减少必要信息。该写的约束一条都不能少。第二个坑是对 Skills 的改动不记录版本。我今天改了一个 Skill 的描述明天改了另一个跑几天以后发现消耗涨了却完全不知道是哪次改动导致的。所以如果你要系统性优化一定要给 Skill 和工具定义打版本标签记录每次修改前后的 Token 消耗数据。数据驱动的优化才是可持续的。第三个坑是盲目追求并行。我曾经为了让多个子代理同时跑强行把任务拆了两段结果两个子代理抢同一个资源互相等待死锁最后全部超时重试Token 花了两倍还不止。并行之前先问一句这些任务真的互不依赖吗如果不是老老实实串行。我的整体体会是Agent 开发里的复杂度管理和养猫很像你以为给猫多买几个玩具是爱它后来发现猫只对纸箱子情有独钟剩下的全是在浪费空间。Skills、子代理、工具优化本质上都是同一个道理——不为复杂度买单只把资源花在真正有价值的地方额度自然就够用了。
返回列表