上周我把一个大模块的重构拆成四个独立任务,开了四个 Agent 并行跑,本想着能省一晚上的时间。结果第二天看到账单,API 消耗直接翻了 4 倍,数字大到让人肉疼。问题不在我用的模型本身变贵了,而是这 4 个 Agent 全在用又大又全的配置,做那些本来只需要小模型就能完成的活。
准确说,是 Claude Code 在并行调度子任务时,把主会话的模型设置一路传递了下去。最后真正把账单压回来的,不是少开 Agent,也不是把主模型换便宜,而是只动了一个配置:给只读、检索、总结这类子 Agent 单独指定小模型。就这么一个字段,省下来的钱比我想象中还夸张。
这篇把整个排查链路、费用结构和这个配置的用法一起捋一遍,给同样在玩 Agent 并行开发的人做个参考。
1. 事情是怎么失控的
1.1 我当时的并行方式
我当时的场景挺典型:手上有四个改动方向,分别是接口重构、日志链路调整、测试用例修补、文档同步更新。这几个方向虽然都落在同一个仓库里,但涉及的文件基本不重叠,逻辑上也相对独立,按道理很适合并行处理。
于是我在终端里开了四个 Claude Code 会话,用了类似 tmux 多窗口那种方式,四个会话同时跑。另外主会话里我还挂了几个子任务,用来做代码搜索、文件阅读和变更摘要。从编排角度看,这已经是一个典型的 Agent 框架用法了:一个主 Agent 负责任务拆解和决策,多个子 Agent 分别执行窄任务。
概念上没问题,账单一出来问题就大了。我翻了下费用明细,四个会话并行跑了大约一个多小时,输入 token 消耗几乎是原来单用户会话的六倍,总费用则到了原来的四倍多。第一反应是模型是不是被切成更贵的档位了,结果查了一圈,模型没变,变的是每一笔请求都在重复付“读资料”的钱。
1.2 账单结构长什么样
我去控制台把按模型拆分的用量拉出来,看到了一个很典型的现象:output token 其实没有暴涨很多,真正暴涨的是 input token。如果把一次完整请求拆开看,里面大头不是模型生成回复的那段,而是每次都要带上的 system prompt、工具定义、代码索引、历史消息,这一大坨都按 input 计费。
以我当时的基线做参照,大概是这样一组数据:
| 场景 | 输入 token 消耗 | 输出 token 消耗 | 缓存命中率 | 账单变化 |
|---|---|---|---|---|
| 单会话、单轮小任务 | 基线 1x | 基线 1x | 较高 | 基线 |
| 单会话长任务 | 2-3x | 1.5x | 中等 | 1.5-2x |
| 4 个会话全并行 | 6-8x | 2x | 很低 | 4x+ |
| 4 个并行 + 子 Agent 继承主模型 | 10x+ | 3x | 极低 | 6-8x |
我属于最后一行那种情况,既开了多会话,又在会话里继续派生子 Agent。两个因素叠在一起,账单自然就压不住了。
1.3 什么任务适合并行,什么不适合
这里要先说一句公道话,并行 Agent 不是原罪。它最适合的场景是任务之间文件不重叠、状态不共享,比如我刚才说的接口重构和文档更新,各改各的文件,互不影响。
真正不适合的,是多个 Agent 同时写同一个文件,或者多个 Agent 共享同一份可变状态。我见过有人让两个 Agent 同时改一个配置文件,A 改完 B 又覆盖回来,两边反复“救火”,来回折腾了七八轮才结束。每一轮都是一次完整的 API 请求,成本直接线性叠加。这种不是提效,是花钱买冲突。
所以并行之前,先判断依赖关系。依赖关系清楚的,并行是效率放大器;依赖关系乱成一团的,并行只会让 Agent 框架替你承担“沟通成本”,最终的代价还是会落在账单上。
2. 账单为什么翻 4 倍:三个问题叠加
2.1 基础上下文被多份重复计费
这是最核心的机制问题。每次调用 API,模型并不知道上一个请求发生了什么,它看到的只有当前请求里携带的上下文。Claude Code 为了让对话连续,会把之前的消息、工具结果、文件内容一并塞进下一次请求。这个设计在单会话时没问题,一旦并行,问题就来了。
四个会话各自维护独立的上下文,等于同一份代码库摘要、同一份 system prompt、同一批工具定义被复制了四份。每一份都按 input token 计费。这不是四倍的小开销,而是好几倍的重复开销。
我当时请了四个“专家”来改代码,每请一位专家,都要把整摞资料从头念一遍。四个专家同时进场,就是四份资料的重复阅读费。更关键的是,这些资料里大量内容是重复的,系统提示词一样、项目说明一样、最近一次 git 变更一样,但它们分散在四个不同的上下文里,谁也共享不了谁。
这里就牵出 Anthropic 的 prompt caching 机制。它本意是好的:请求里命中缓存的部分,按很低的折扣计费。但缓存命中有前提,它依赖请求前缀的一致性。四个并行会话如果从不同的工作目录启动,废话不多说继续把错误分摊到四份独立账单里,缓存几乎全部落空。缓存命中率一旦跌到谷底,成本就不是线性涨,而是几何级涨。
2.2 模型继承导致“高射炮打蚊子”
第二个问题出在模型选择上。Claude Code 的自定义 Agent 允许你给每个 Agent 单独指定模型,但如果你不写这个字段,很多情况下它会沿用主会话的模型设置。
我最初图省事,直接在 settings 里把主模型设成了很强的档位,想着这样能保证质量。结果所有子 Agent 也全部走了这个模型计费。检索一段代码、总结一个文件、跑一遍测试结果,这些都算在贵模型头上。打个比方,让全科主任医师去给每个病人量体温,体温是量得准,可这个人力成本完全不合理。
更隐蔽的是,子 Agent 有时候还会被用于一些后台辅助任务,比如自动生成任务标题、整理变更摘要。这些任务的输入输出都不长,但请求次数频繁。如果它们也继承贵模型,那每一笔小额支出都会变成一个小额放大镜,次数一多,总额就非常可观。
真正合理的用法是分级:需要推理、规划、改代码的任务,用强模型;只做检索、过滤、摘要、格式整理的任务,用便宜模型。就像团队里既有架构师也有实习生,架构师定方案,实习生收集资料,这样才能把人力成本压到最低。
2.3 失败重试与工具膨胀的隐形消耗
这一块是最容易被忽视的。并行 Agent 跑起来之后,非常容易触发 API 的速率限制,报错信息会直接出现在终端里,类似 agent execution terminated due to error。Claude Code 遇到这类错误,通常会做自动重试。一次重试不是重新算一遍这么简单,而是把整个请求重新发送一遍,等于同样的成本再付一次。
我排查日志的时候发现,最严重的那个会话在十分钟内重试了六次,其中有三次是限流导致的,有两次是上下文超长被截断后重推。这些重试请求基本没有产出有效结果,却全部按正常 token 计费。这就解释了为什么我的 output token 没有暴涨,但总费用还是冲了上去。
另一个大坑是工具定义膨胀。Claude Code 里挂的 MCP 服务器、插件、自定义工具,每个请求都要带上工具描述。工具越多,每次请求的 input token 越大。单个会话可能感觉不出来,四个会话并行之后,工具定义被重复加载四遍,哪怕没实际调用,描述文本也已经计入费用了。如果某个 MCP 服务器的描述写得特别啰嗦,那它几乎是在偷偷烧钱。
3. 一个配置解决:给子 Agent 指定专用模型
3.1 配置入口和具体写法
先说结论,这个配置写在项目的.claude/agents/目录里。Claude Code 允许你用 Markdown 文件定义自定义 Agent,每个文件代表一个 Agent,文件头部有一段 YAML frontmatter,里面可以声明 name、description、tools 和 model。
我的做法是,给使用频率最高的几个子 Agent 分别建了文件,并且统一加上了 model 字段,全部指向便宜的小模型。
举个例子,我建了一个代码检索 Agent:
--- name: code-searcher description: 在仓库中检索代码、查找定义和引用关系,输出结构化结果 tools: Read, Grep, Glob model: claude-haiku ---再比如变更摘要 Agent:
--- name: change-summarizer description: 读取 git diff 和相关文件,生成简洁的变更说明 tools: Read, Grep model: claude-haiku ---关键就是那个 model 字段。没有这个字段时,子 Agent 走的是主会话的模型配置,费用高;填上之后,它就按照你指定的模型去跑,单价立刻降下来。这就是标题里说的那一个配置。
可能有读者会问,所有子 Agent 都写 model 字段会不会很麻烦。我的经验是值得的,你只需要给那些高频、窄任务、只读类型的 Agent 写上小模型,真正负责写代码的主力 Agent 保留强模型就行。一个仓库里这种文件的个数通常不超过十个,一次配置,长期生效。
3.2 配置之后账单发生了什么变化
我改完配置之后,重新跑了一轮同样的任务,效果非常直接。子 Agent 承担了大量检索、阅读、摘要工作,这些工作以前按强模型计费,现在按小模型计费,成本单价大概下降了一个数量级。整体账单回到开并行之前的水准,某些重复性任务甚至比单会话全量跑还低。
成本变低不等于质量下降。关键是我把任务边界也理清了:子 Agent 只做范围明确的窄任务,最终方案和代码改动还是由主 Agent 拍板。窄任务对模型能力的要求本来就不高,用便宜模型完全够用。反而是以前那种让强模型去反复检索文件的做法,既浪费钱,速度还慢。
这里给一个直观的换算思路。假设一次子任务消耗的 input token 相同,模型 A 的输入单价约为模型 B 的十分之一,那么同样的任务,单位成本就是原来的十分之一。如果整个工作流里有六成 token 消耗发生在子 Agent 身上,那总成本能省掉差不多一半以上。这是最简单的算术,但前提是这个配置得真的写对。
3.3 如果你还是想开多个终端会话
如果你和我一样,确实需要同时开多个终端会话跑不同任务,那除了子 Agent 配置,还有几个配套做法需要跟上。
第一,尽量用 continue 模式延续会话,不要每次都从全新的空会话开始。延续会话至少能保住一部分 prompt 前缀,让缓存命中率不至于跌到零。我从多个会话平行切到续跑模式之后,缓存命中率上来了,input token 的浪费少了不少。
第二,不同任务不要各自为战地开在不同目录。Claude Code 的上下文很吃工作目录,目录不同、加载的 MCP 配置不同,缓存前缀就对不上。要么统一在同一个项目根目录启动,要么干脆就用子 Agent 来区分任务,而不是再开一整份会话上下文。
第三,控制并发上限。一个简单的 Bash 脚本就能做到串行队列:
for task in task-a task-b task-c task-d; do echo "running $task" claude -p "执行 $task 目录下的重构任务,只输出最终变更" done这个脚本把四个任务排成队,每个跑完再跑下一个。虽然总耗时变长了,但成本比四个全并行低很多。如果你想微调,也可以用类似 GNU parallel 的工具控制最大并行数设为 2,效果介于全并行和全串行之间。
4. 排查方法和避坑清单
4.1 快速定位账单增长点
遇到账单异常,第一件事不是去改配置,而是先搞清楚钱花在哪。我建议去 Anthropic 控制台的用量页面,按模型维度看 token 消耗分布。如果发现某个贵模型的 input token 数量远超你预期,那大概率就是模型继承问题。
第二个办法是看缓存命中率。控制台通常能体现缓存相关数据,缓存命中率的趋势一旦明显下行,就可以怀疑是并行会话导致的重复上下文。这时候把多个会话改成续跑模式,或者统一从同一目录启动,命中率会很快回暖。
第三个办法是开调试模式观察请求细节。Claude Code 支持查看请求日志,你可以确认每个请求实际使用的模型名称,看看是不是有子 Agent 在偷偷用贵模型。这个日志在排查阶段特别有用,比对着账单猜要快得多。
4.2 避坑速查表
| 场景 | 现象 | 处理方法 |
|---|---|---|
| 子 Agent 未指定模型 | 用量记录里贵模型 input 暴涨 | 在.claude/agents/里给子 Agent 写 model 字段 |
| 多个会话并行 | 缓存命中率极低,input 重复上涨 | 用 continue 延续会话,或改用子 Agent 而非重量级会话 |
| MCP 服务器挂太多 | 每次请求 tools 描述很大 | 精简子 Agent 的 tools,不往子 Agent 堆一堆 MCP 服务器 |
| 频繁报错重试 | 成功次数少但费用高 | 降低并发数,检查是否触发速率限制,必要时做任务排队 |
| 上下文被截断后重推 | 输出 token 不高但 input 异常高 | 调小单次任务范围,不要让一个会话揽全仓库的活 |
这张表基本覆盖了我这次踩坑的所有场景。对照着排查,半小时内就能定位到问题根因。
4.3 几个容易被忽略的细节
再分享几个细节层面的坑。
第一,子 Agent 里的 tools 越少越好。tools 字段不只是权限声明,它还会直接影响每次请求携带的工具描述。子 Agent 能干活的前提是它有足够的工具,但给它塞一堆不需要的 MCP 工具,只会让 input token 白白膨胀。
第二,注意配置文件的覆盖顺序。Claude Code 的配置有全局、项目、本地之分,.claude/settings.local.json会覆盖项目配置。如果你改了项目配置但没生效,先检查本地配置里是不是有同名项。
第三,给 Agent 命名的时候,description 要写得具体一点。Claude Code 调度子 Agent 时,会根据 description 判断该调哪个 Agent。描述含糊会导致主 Agent 选错子 Agent,选错了就得重新跑,来回折腾的每一轮都是钱。把 description 写清楚,比如“只负责检索,不负责修改代码”,调度准确率会高很多。
5. 成本优化不是不开 Agent,而是分级
5.1 让每一层 Agent 干它该干的活
经过这一轮折腾,我对 Agent 编排的理解变了不少。以前总觉得 Agent 多开就是提效,现在更倾向于把 Agent 分成不同层级,每层用不同模型、不同工具、不同权限。
主 Agent 承担规划、决策、代码修改,它需要强模型和完整工具链;子 Agent 承担检索、验证、摘要、翻译,它需要小模型和窄工具集。这种分级方式,和 Agent 框架里的 harness 与 agent 的关系很类似,harness 提供运行环境和调度骨架,agent 在骨架里执行具体任务。框架解决了“怎么跑起来”的问题,但没帮你解决“谁该用什么模型”的问题,后者得自己配置。
Skill 和 Agent 的区别也值得拎出来说一下。Skill 更像是一组能力包,比如“代码审查技能包”“测试生成技能包”,它不绑定模型,可以被不同 Agent 调用。Agent 则是带模型、带工具、带权限的执行单元。成本优化的重点不在 Skill,而在 Agent 的模型和工具组合。模型贵不贵、工具多不多,直接决定了每一轮任务的单价。
5.2 警惕 Agent 记忆带来的上下文重复
热词里有 Agent 记忆相关的讨论,我也踩到了边。并行 Agent 各自有各自的上下文,彼此不知道对方已经读过哪些文件。如果每个 Agent 为了完成任务,都把仓库的核心文件重新读一遍,那多 Agent 的记忆优势就变成了成本灾难。
解决方式也很简单:任务描述里给出足够上下文,但不要给整段对话粘贴。比如子 Agent 需要知道某个函数的位置,直接把函数签名和文件路径给它,而不是让它自己从头读一遍整个项目。更不要在主上下文里塞入子 Agent 已经读过的东西,能不重复就不重复。
我这里要特别提醒,多个 Agent 并行改代码时,最好的状态是“各看各的文件”,最怕的状态是“每个人都把项目目录扫一遍”。想判断是不是犯了这个错,可以看每次请求的 token 数,如果单个请求动不动就几万 input token,且其中大部分是和任务无关的全局文件,那就要做上下文收敛。
5.3 把配置沉淀进仓库,团队一起受益
这个改善不能只停留在个人配置里。我最后把.claude/agents/目录提交到了仓库,里面每个 Agent 文件都写清楚了模型和工具,团队其他人拉下来就能直接用。这样大家都不会掉进模型继承的坑,新人也省得自己从头摸索。
另外,我还在项目 settings 里做了预算相关的提醒,隔一段时间看一眼用量,不用等账单炸了才反应。成本优化的本质不是不开 Agent,而是用最合适的配置,让每个 Agent 干最擅长的事,付最匹配的价钱。
我这几天把配置改成上面的样子,并行还是照开,只是每个子代理的工具和模型都做了收敛。实测账单回到开并行之前的水准,某些重复性任务甚至更低。最后再提醒一句,别只看模型单价,子代理的工具数量、缓存命中率、自动重试次数才是隐藏账单。先把模型分级这个配置补上,再谈多 Agent 提效。