
前几天清理任务日志顺手把被限额杀掉的 Claude Code 子 Agent 拉了个清单数字让我愣了好一会儿449 个。更要命的是我重新提交重跑之后发现其中 438 个都在从零开始干活——它们之前改了一半的代码、写了一半的注释、跑到一半的测试几乎全部作废。这个比例远超出我的预料。如果你也在用 Claude Code 做批量重构、并行子任务或者手头正跑着几十上百个 Agent 的“工厂式”开发流程这篇内容值得你花几分钟认真看完。我决定把这次事故的完整复盘写下来包括任务怎么拆的、限额怎么触发、子 Agent 为什么恢复不了现场以及后来我用了哪些方法把“从头重做”的比例降下来。适合正在使用 Claude Code、对 subagent 并行机制感兴趣、或者踩过同样坑的开发者参考。1. 事故现场449 个被杀的子 Agent重跑后只有一个结果1.1 我到底在跑什么任务先说背景。前段时间我把一个遗留的大型代码库拆成几十个模块做一轮批量升级包括接口迁移、依赖替换、单元测试补齐三件事。乍看是重复劳动正好适合扔给 Claude Code 的子 Agent 去并行处理。我给每个子任务写了独立的描述文件指定了输入文件、目标逻辑、自检命令然后让它们分开跑。这种模式下的 Agent 调度其实类似于一个“临时团队”一个主 Agent 负责拆活派活N 个子 Agent 各自领一块任务去执行。表面上看互不干扰但在共享同一个 API 账户、同一个上下文预算池的时候每个子 Agent 消耗的 token 和请求频率都会被计算在一起。我当时没有太在意这一层按 20 个子 Agent 一组并行启动想着反正任务拆得细跑完一个归档一个就行。结果是任务跑到中后期限额头像悬在头顶的刀一样落下来了。不是某一个 Agent 报错而是大面积中断日志里铺满了额度耗尽、请求限流、上下文超出之类的错误。我清点之后计数的确是 449 个被“杀掉”的子任务。1.2 限额到底是怎么“杀”掉子 Agent 的这里的限额不是单一概念至少包含三个层级任何一个被触发当前子 Agent 的执行进程就可能直接被掐断。第一层是 API 请求速率限制单位时间内请求次数或 token 消耗超过阈值就会返回限流错误。第二层是上下文长度限制子 Agent 在执行长任务时对话上下文不断累积一旦超过模型窗口它就没有足够空间继续推理本质上是“大脑装满了”。第三层是账户或组织的预算配额尤其是团队共用的 Claude Code 账号一旦总消耗触达上限所有正在运行的子 Agent 都会被强制终止。我当时跑的是大量并行任务单位时间内的消耗量是线性任务的数倍甚至数十倍。20 个子 Agent 同时推进的时候每一分钟都在大量读取文件、输出中间推理、调用工具速率限制几乎是最容易触碰的。而一旦某一个子 Agent 触发了 429 类限流错误它并不会自己冷静重试而是连同它自己的上下文、工具调用记录、中间产出一起被清除。用户看到的现象就是“子任务失败”但实际上是整个执行上下文已经没有了。1.3 重跑后的统计438 比 11这个数字足够扎心我更关心的是这些被杀掉的任务重新跑一次到底要付出多少成本。于是我把 449 个失败任务全部重新提交按照日志和结果做了分类。结果类别数量占比现象描述重新执行后完全从头开始43897.6%Agent 重新读任务描述、重新分析代码结构、重新生成改动利用已有产物快速收尾112.4%主要是那些任务很短、中断前已经接近完成的幸运儿无法恢复只能人工处理00%这次没有但后来在另一个项目中遇到过438 个任务从头重做意味着几乎每一个被限额中断的任务前期的所有分析、中间步骤、局部改动全都白费了。你以为重跑只是重放一遍实际上 Agent 是“重新思考一遍”。这种浪费不是简单的二倍成本而是多倍放大因为第二次运行还会再次触发类似的限额风险形成恶性循环。为了搞清楚为什么会这样我把子 Agent 的运行机制翻了个底朝天。2. 为什么 438 个子 Agent 注定要“从头重做”2.1 子 Agent 是无状态的执行单元上下文等于工作记忆最核心的一点是每个子 Agent 都是无状态的。它的所有判断依据不管是读取过的文件内容、生成过的代码片段、还是刚刚的思考过程都存放在对话上下文中。这个上下文是“工作记忆”进程一旦结束记忆就没了。类比一下相当于你雇一个临时工去处理一摞文档干到一半你把他辞退了换了一个新临时工。新临时工并不会自动知道你刚才把哪些文件放在了什么地方也不会知道你最开始定的整理规则是什么他只能看你留在桌子上的半成品猜。大多数情况下他猜不中所以他干脆按自己的理解重新整理一遍。子 Agent 重跑时的情况几乎一模一样任务描述还在但它对“已经执行到哪一步”“文件里哪些改动是我做的”“下一步本来要做什么”完全没有记忆。我在早期设计任务的时候没有考虑“进度持久化”。指令是给足了但没有任何中间状态被记录下来。Agent 每一次都从白纸状态开始这是 438 这个数字最根本的来源。2.2 中断时机不确定恢复点几乎无法手动定位我还天真地以为任务中断后能通过检查代码改动来找回现场。结果是根本找不到一个可靠的恢复点。因为限额是随时可能到来的。有些子 Agent 可能刚启动 30 秒就被杀文件一个还没改这算幸运有些子 Agent 已经改了 5 个文件、跑了 2 轮测试然后死在第三轮之前这种属于“半成品”还有些子 Agent 看起来所有步骤都快跑完了但最后一条工具调用没有返回结果输出不完整这种最坑因为你很难判断它的改动是应该保留还是应该重新生成。更麻烦的是Agent 在执行过程中的临时文件、未提交的改动很多只存在于它的临时工作区里。主进程被杀、工作区被清理之后这些改动直接消失连个残留痕迹都不留。重跑时 Agent 看到的是一个干净的代码库当然会认为“这个任务还没开始过”于是从头重做就是唯一可能的结果。2.3 任务描述文件是“目标”不是“进度”再看任务描述文件本身。我给每个子 Agent 写的内容是目标是什么、涉及哪些文件、需要满足什么条件、自检命令是什么。这本质上是一个“需求文档”不是一个“执行状态记录”。Agent 重跑时它拿着需求文档重新规划路径。如果代码文件是干净的它就会重新执行全部计划。如果代码文件已经被改得乱七八糟它可能试图先“修正”到它理解的状态然后再推进这反而可能导致更大的冲突。没有显式的进度记录Agent 就无法回答“我上次做到哪里了”。这一点后来我花了不少力气才想明白任务描述文件只回答“要做什么”而断点续跑需要额外回答“已经做了什么”。这两个信息必须分开存放前者给 Agent 看后者给 Agent 和调度器同时看。2.4 还有一个隐藏杀手重跑本身会重复触雷最开始我处理失败任务的方式非常简单粗暴——原封不动重新提交。结果就是它踩进了另一个循环重跑的任务依然是大任务依然消耗大量上下文依然可能在同一个位置附近被限额杀死。更糟的是如果前一次运行已经部分修改了代码文件重跑时会基于一个“脏”的工作区重新开始代码结构可能已经不符合任务描述里的假设Agent 多花时间去理解现状token 消耗比第一次还高被杀的概率反而更大。我在日志里对比了几组任务第一次失败前的 token 消耗和第二次失败前的 token 消耗第二次普遍比第一次高出 15% 到 40%。因为 Agent 额外花了一部分精力处理“为什么这里已经有改动了”的问题。这是个越跑越亏的循环任务越复杂越容易触发限额越触发限额重跑时上下文消耗越大越容易再次触发。明白这些原因之后我就不再把精力放在“抢救上一次运行”上而是开始重新设计子 Agent 的运行方式。方向就是四个拆小任务、落盘进度、可重入入口、控制并发预算。3. 让子 Agent 从“一锤子买卖”变成“可断点续跑”的四个工程手段3.1 任务粒度把子任务切到“5 分钟能完成”的尺度第一件事是重拆任务。过去我习惯按“模块”来分配任务比如某个子 Agent 负责整个支付模块的接口迁移。这个任务内部包含十几个步骤执行时间可能超过 40 分钟。执行时间越长被限额打断的概率就越高。后来我定了一条硬规矩单个子 Agent 的任务量必须控制在 5 到 10 分钟能完成的尺度内。换算成代码规模大概是只改 1 到 3 个文件只处理一个明确的改动类型只跑一轮对应的验证命令。如果原来的模块任务包含 10 个步骤我就把它们切成 10 个更小的任务按顺序逐个派发。这样做的好处很明显就算某个小子任务被限额杀掉损失也是有限的。而且小任务天然容易在限定时间内完成Agent 在上下文还没膨胀到危险线之前就已经结束了。我统计过切成小任务之后被杀的子 Agent 比例大概下降了三分之二剩下的被杀任务里因为运行时间短重跑成本也很低。你可能担心任务切小了会不会影响整体质量实际上不会。因为大任务的每一步产出物是明确的切小之后只需要把前后任务的衔接点定义清楚让下一个子 Agent 从上一个的产物继续干活。关键点是每个小任务的“完成标准”必须可验证这样即使串行的多个小任务中某个失败其他已完成的小任务不会被连累。3.2 状态文件让进度从内存落到磁盘状态文件是我这次事故之后引入的最有效改变。每个子任务除了任务描述文件之外我还强制要求它维护一个进度日志文件。这个文件记录三件事已经执行的步骤、当前完成的文件改动列表、下一步计划。例如我会在任务描述文件里明确写入一条指令每完成一个关键步骤必须把该步骤追加到.task/progress.md内容包括刚跑了什么命令、改了什么文件、结果如何。当下一个阶段开始时Agent 第一件事是读取这个进度文件判断哪些步骤已经完成然后只从第一个未完成步骤继续。你可以进一步把状态文件做成 JSON 结构方便调度器读取判断状态{ task_id: payment-migrate-008, status: in_progress, completed_steps: [update_imports, fix_type_errors], remaining_steps: [update_api_client, run_unit_tests], changed_files: [src/payment/client.py, src/payment/models.py] }把状态文件交给 Agent 时相当于给新来的临时工一张“交接表”他不需要从零理解只需要接着干。这个方法在实践中立竿见影重跑任务不再是完全的从零开始而是定位到断点能省掉大量前期分析动作。3.3 可重入的入口与幂等检查重跑不是重做有了状态文件之后还有一个配套问题需要解决子 Agent 重跑时如何避免重复执行已完成步骤。这就要引入“幂等”的思维。我要求任务描述文件必须包含一个“进入条件检查”段落Agent 启动后先检查目标文件是否已经满足部分改动如果满足就直接跳过对应步骤。比如任务要求把某个模块的 A 接口改成 B 接口Agent 在改动前先 grep 一下目标文件如果发现 B 接口已经存在就说明这一步已经完成了不需要重新做。这一步非常重要。很多任务被杀时文件已经改了一部分。重跑时如果没有幂等检查Agent 可能把已经改完的代码撤回重写一遍不仅浪费还可能制造新的冲突。让每个子 Agent 拥有“报告进度而不是重复劳动”的意识是降低重跑成本的关键。我还习惯给每个子任务开独立的分支或者工作目录。子 Agent 跑的中间状态不会污染主分支任务失败清理时不会牵连其他模块。如果任务成功合并分支也干净利落。这种隔离策略在并发场景下几乎是必须的。3.4 并发与预算控制从源头减少被杀的概率第三件事是控制并发。最初我脑子里的理想是 20 个 Agent 齐头并进效率最大化。现在我知道了在没有做预算保护之前并行度越高限流来得越快总产出反而可能更低。现在的做法是限制同时运行的 Agent 数量比如同时最多 5 个。并发降下来之后单位时间的消耗速率趋于平缓不太容易触发速率限制。如果你觉得 5 个太保守可以先跑一轮小规模任务看峰值消耗再动态调整。关键是不要一次性把任务全放进去。同时我还会为每一轮批量任务设定一个 token 预算上限预算快耗尽时主动暂停新任务的派发等下一批额度窗口再继续。这相当于在“被杀”之前我们自己先“踩刹车”。相比被动崩溃主动限速能保留更多中间状态重跑比例小得多。针对那些真的会被定额杀死的任务我也设置了两级重试策略第一次失败后降低并发重试第二次失败后把任务拆成更小的子任务再重试。这套流程跑下来“从头重做”的比例从接近 98% 降到了 30% 左右剩下的那部分主要是因为任务本身设计有问题而不是限流导致的。4. 这次事故里总结的 Agent 编排方法论4.1 把子 Agent 当“临时团队成员”来带如果说这次事故带给我最大的认知转变那就是用 Agent 和带实习生是同一套逻辑。你给实习生安排任务不会只丢给他一句“把这个模块迁移一下”然后期待他干完就行。你会给他文档、给他参考实现、约定他遇到问题先记录再提问、每一步产出什么要提前说清楚。子 Agent 也是一样。最重要的一个改变是给任务描述文件里加上了“报平安”机制也就是要求 Agent 每一步都更新状态文件。它起到两个作用一是状态文件本身就是进度快照方便断点恢复二是当任务失败时调度器能通过状态文件判断损失范围而不是盲目重跑。如果你现在手里的任务描述文件只有目标和验收标准我建议马上补上“每完成一个步骤必须记录在哪里”这一条。这可能是成本最低、收益最大的一次改动。4.2 失败设计优先所有任务都要能“死两次”我逐渐养成了一个习惯在设计任何子任务的时候先默认它会失败一次而且失败在不可控的位置。然后我反问自己如果失败发生在这里重跑的代价是什么如果代价很大就说明任务设计不合理。这个思考方式可以筛掉很多“看起来没问题”的任务。比如一个大任务里包含一个很耗时的测试阶段那么我会先把测试阶段拆成独立的小任务让“改代码”和“跑测试”分离。这样即使测试阶段被杀代码改动还在不至于连代码重写一遍。换句话说一个任务如果不能在任意步骤被打断之后仅重放剩余步骤就不够健壮。4.3 不要迷信并行并发是省时间也是放大损失并行给人带来的效率幻想特别强。20 个 Agent 同时跑起来那种进度条飞刷的感觉很容易让人忘记一件事并行的每一个 Agent 都是独立的失败源。一个 Agent 被杀只损失一个任务20 个 Agent 一起被杀损失就是 20 份工作量和 20 份善后成本。后来我给自己定了个规律涉及 API 配额敏感环境时优先保完成率而不是保并发数。一次 5 个并发、稳定跑完远比 20 个并发、触发限流后 17 个重跑要快。你如果真的需要高吞吐可以设计成“分批波次”每波小规模并行波次之间留出缓冲窗口把失败率维持在低位。4.4 对日志保持敬畏每个被杀的子 Agent 都是一份“尸检报告”449 这个数字不是凭空出现的它来自日志。如果当时我没去翻日志只是把所有失败任务又批量重跑了一遍那我永远不会知道有 438 个任务在做无效重试。日志这东西看似枯燥却是排查这类问题最直接的线索。现在我养成了固定习惯每次批量任务结束后先按失败原因分类统计一遍再决定下一步。原因是限流、上下文超长、还是代码冲突对应的处理策略完全不一样。把每个失败任务当成一条数据去统计而不是当成一个孤立事件去处理效率会高很多。5. 检查清单与避坑速查表5.1 团队使用 Checklist下次发任务前对照一遍我整理了一份自己团队现在用的检查清单每次派发批量子 Agent 任务之前过一遍。你可以直接抄走任务执行时间是否控制在 5 到 10 分钟内任务是否会修改超过 3 个文件如果会拆分了吗是否有独立的工作区或分支隔离任务描述文件里是否包含“完成一步记录一步”的强制要求状态文件是否定义了明确的已完成与未完成标记任务重跑是否具备幂等检查能跳过已完成的步骤并发数量是否设置在安全范围内还是盲目拉满是否设置了整体 token 预算和暂停机制是否提前写好了失败后的重试策略而不是手动狂点重跑任何一项不满足我就会停下来先补完再发任务。以前嫌这些工作繁琐但经历过一次大范围重跑之后我宁可把时间花在前面。5.2 常见报错与处理方式速查报错现象可能的触发原因建议处理方式请求被限流429 类单位时间请求次数过多降低并发、加入退避重试机制、错峰调度token 消耗达到账户配额上限批量任务总 token 超预算分批执行、按模块切割任务、升级配额或拆分到多个时段上下文长度超限报错单个 Agent 任务过长、上下文膨胀缩小任务范围、减少单 Agent 携带的参考文件、提前交付状态文件子 Agent 运行到中途进程消失触发限额后进程被杀检查状态文件确定断点重建一个小型续跑任务重跑后代码冲突明显加剧任务可重入性不足检查幂等逻辑为每个子任务配备独立分支避免基于脏状态重跑5.3 已经开跑后发现大量重跑怎么止损如果你现在已经陷入“任务反复被杀、重跑又反复触发限额”的泥潭先不要再点重跑了。停下来做三件事第一从日志里把失败任务按原因分类优先处理“上下文超长”和“配额耗尽”这类全局性问题因为这种情况重跑多少次都没用。第二挑出改动最少、最接近完成的任务人工或单独的小 Agent 收尾先保住能交付的成果。第三检查一下当前是否还在高并发运行马上把并发降到安全水平否则新任务还会继续被杀。止损的核心原则是不要在一个错误的设计上重复投入资源。先降低并发、先切断重试循环等你把任务粒度和状态文件补上之后再考虑重新批量派发。回头再看这 449 个被限额杀掉的子 Agent与其说是一次失败不如说是用真金白银换回来的经验样本。我现在做任何 Agent 编排任务心里都会默认一个最坏情况每个子任务都可能白白死掉一次。如果它没有死那是运气如果它死了我不至于用双倍成本去填坑。这个思路分享出来希望你能少走一段我走过的弯路。