一个 agent 该管多大一块:任务切太碎和切太粗,故障长得完全不一样
数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。
**很多人把返工归因成模型不行,但按现象倒推,相当一部分是任务边界画错了位置——切得太碎导致交接损耗吃掉全部收益,切得太粗导致上下文中途丢失约束。**这两种病的表面症状都是”它写出来的东西不能用”,但排查路径正好相反:一个要往回合并,一个要按验证点切开。你先把病分清楚,再动手,比反复改措辞有用得多。
站内已经有讲角色怎么分、工具怎么设计的两篇——角色分工回答的是”谁来干”,工具设计回答的是”手里给什么”,这篇只回答第三个问题:“一个人一次该端多大一盘菜”。三者是同一套编排里的不同轴,别混着调。
一、先立判据:什么叫一块合适的任务
在看症状之前,先把”合适”落成可检查的条件。我用四条判据,一块任务同时满足四条才算切得住:
有独立的完成信号。 这一块干完,存在一条命令能判定成功与否,退出码或测试结果说了算,不需要你肉眼读代码判断。没有这条命令,说明这块任务的边界压根没定义清楚,后面所有争论都是扯皮。
上下文装得下且有余量。 这块要读的文件、要遵守的约定、要参考的既有实现,一次性放进去还剩得下工作空间。各家产品的窗口规格和压缩策略不同且会调整,以官方最新说明为准,但判据是通用的:如果你要靠”它自己会去搜”来补齐关键约束,这块就偏大了。
失败可以单独回滚。 这块炸了,还没提交就 git checkout -- <路径>、已经提交就丢弃它独占的那个分支,一步回到干净状态,不牵连别的块。做不到,说明两块任务写同一批文件,边界是假的。
对外接口开工前就定死。 函数签名、数据结构、文件路径、返回约定,在这块开工之前已经是白纸黑字。留给它”自由发挥接口”,就是留给你后面手工对齐。
四条里最容易被忽略的是第三条。很多人按”功能模块”切,看起来很整齐,实际上两块都要改同一个路由表、同一份配置、同一个类型定义文件,回滚的时候互相踩。按可回滚性切出来的边界经常比按功能切的难看,但它是能跑的。
二、分因:三类现象对应三种不同的病
现象 A:每块都汇报成功,拼起来跑不通
这是切太碎的典型形态。每个 agent 在自己那一小块里确实自洽,测试也是它自己写的,当然过。问题出在接口面:A 块返回的字段叫一个名字,B 块按另一个名字取,两边都”没错”。
验证方法很直接:拉出这一轮所有改动的范围,看跨了几个边界。
git diff --stat HEAD~1
git diff --name-only HEAD~1 | xargs -n1 dirname | sort | uniq -c | sort -rn
第二条的输出左列是该目录下被改的文件数,右列是目录名(根目录文件会归到 .)。如果一轮改动落在五六个目录,每个目录只动了一两个文件、每处几十行,基本可以判定切得过碎了。真正合适的粒度,一轮改动通常集中在一到两个目录,有一个明显的重心。
另一个信号在你自己身上:如果你这半小时干的事情是把 A 的输出复制给 B、把 B 的疑问转述给 A,你已经变成了人肉消息总线。这不是编排,这是你在替它们补交接成本。
现象 B:越干范围越大,中后段开始忘事
这是切太粗的典型形态。会话前半段还守着你定的约束,到后半段开始重复问已经回答过的问题,或者把前面明确否决的方案又端上来一次。原因是上下文里早期的约束被挤掉或压缩掉了,它读到的”现在”已经不包含”当初”。
验证方法:翻回中后段的产出,看它有没有违反最开始定的某条硬约束(比如不许动某个文件、必须用某个已有函数)。违反了,而且违反得很自然、毫无察觉,那就是遗忘,不是抗命。这类现象的机制层面在上下文管理那篇里展开过,这里只当成粒度信号用。
伴生症状是改动范围扩散:你让它修一个函数,它顺手重构了调用方,又顺手改了测试。范围扩散和遗忘往往同时出现,因为两者同源——它已经不知道自己该管到哪。
现象 C:同一块反复失败,每次失败原因还不一样
这是最容易被误判成”模型笨”的一类。判别关键在于失败原因是否收敛:连续三次报同一个错,是任务本身有个卡点,值得继续调;三次报三种完全不同的错,说明这块任务内部本来就耦合着几个独立难点,它每次绕开一个又撞上另一个。这种情况的解法是拆,不是重试。
顺带说一句,重试策略本身有独立的坑,写在失败重试那篇;这里只用”失败原因是否收敛”这一个判据来定粒度。
三、判别表
| 现象 | 大概率成因 | 怎么验证 | 处置动作 |
|---|---|---|---|
| 各块自测都过,集成不通 | 切太碎,接口在块之间漂移 | git diff --name-only 看改动是否散在多目录、每处都很浅 | 合并相邻块;或先单独产出一份接口定义并落盘,后续块只读不改 |
| 中后段违反开工时的硬约束 | 切太粗,早期约束被挤出上下文 | 回读中后段产出,核对是否触碰了明令禁止的文件或函数 | 按验证点切开,每块开工时重新声明约束 |
| 改动范围一轮比一轮大 | 边界没定义在文件级 | git diff --stat 逐轮对比行数与文件数是否单调上升 | 把允许改动的路径列成白名单写进任务描述,超出即打回 |
| 同一块三次失败、原因各不相同 | 一块里塞了多个独立难点 | 把三次报错抄下来对比,看是否指向不同子系统 | 按难点拆分,每个难点单独一块并配独立验证命令 |
| 你自己在手工搬运接口和结论 | 交接成本已超过并行收益 | 记一轮你花在转述上的时间占比 | 合并到一个 agent 内部完成,或改成串行流水线 |
| 明明是小改动,耗时和花费却持续走高 | 碎块的上下文重复装载 | 把各块开工时读进去的文件列出来求交集,交集越大重复装载越严重 | 合并共享同一批上下文的块 |
| 长时间不产出、翻来覆去改同一处 | 无收敛判据,陷入自我循环 | 看连续几轮的 diff 是否互相抵消 | 立即中断,见第五节止损;机制见下方链接 |
最后一类如果频繁出现,单独看无限循环停机那篇,那是另一条排查线。
四、动作:怎么合、怎么拆、怎么固定边界
合并的刀口是共享上下文。 两块任务如果要读同一批文件、遵守同一套约定,合并它们几乎没有额外代价,反而省掉一次上下文装载。反过来,两块任务读的东西完全不重叠,才有分开的价值。别按”功能听起来独立”来合并,按”要读的东西重不重叠”来合并。
拆分的刀口是验证命令。 想拆一块任务,先问自己:拆完之后,前半块用哪条命令判定完成?答不上来就说明这刀切在了没有观测点的地方,切完两块都悬空。合适的切口通常长这样:前半块跑通类型检查,后半块跑通单元测试;或者前半块让某个接口能被导入,后半块让它返回正确结果。
接口先落盘,再并行。 这是我认为收益最高的一个动作。开工前先做一步只产出骨架的工作:类型定义、函数签名、空实现加待办注释、以及一份会失败的测试。这份骨架落到磁盘、进版本库,后续每一块的任务描述里都写死一句”这些文件只读”。骨架变成物理约束之后,接口漂移这个病基本消失。
每块一个回滚单位。 开工前记一个基线,改完不满意就整块丢掉:
git rev-parse HEAD > /tmp/base.txt
# 干活……
git diff --stat $(cat /tmp/base.txt)
git checkout $(cat /tmp/base.txt) -- src/some/path # 把这一块碰的路径还原到基线
两点要说清楚,否则这套动作会给你假的安全感。第一,如果这一轮改动还没提交,git checkout -- src/some/path 就够了;一旦中途已经提交过,必须带上基线的提交号,否则你只是丢掉了工作区里那点未提交的零头,已提交的部分原封不动留着。第二,git checkout <基线> -- <路径> 只还原基线里存在过的文件内容,基线之后新增的文件不会被它删掉,需要另外用 git status 看一眼未跟踪文件再手工清理。很多”我明明回滚了怎么还是坏的”,卡的就是这两条。
需要真正并行的时候用独立工作树,各自一个目录、各自一个分支,互不覆盖文件。共用一个工作目录做并行,是并发冲突的直接来源。
兜底路线要提前想好。 当粒度怎么调都不顺,退到串行:一块干完、你看过、再开下一块。串行慢,但它把”集成失败”这个最贵的失败类型消掉了。项目临近交付的时候,串行几乎总是更划算的选择。
五、什么情况下别再折腾
调粒度是有边际收益的,下面几条一旦命中,停手比继续调更省。
同一块连续三轮失败且原因发散。 前面说过,这是任务内部耦合的信号。第四轮还想靠改描述解决,就是在赌。停下来拆,或者你自己写掉最硬的那一小段,再把剩下的交回去。
回滚成本开始超过重写成本。 判断方法很土但有效:估一下把当前这摊改动理清楚要多久,再估一下从基线重写要多久。后者更短,就按上一节那两条命令还原到基线重来。人对沉没成本的直觉是不可靠的,这个比较必须显式做一次。
范围已经越过不可逆边界。 数据库迁移、生产配置、密钥轮换、对外已发布的接口——这几类东西一旦被碰,就不是粒度问题了,是事故预防问题。发现改动伸到这些地方,立刻中断并人工接管,别指望再调一轮任务描述能拉回来。
问题根本不在粒度上。 有两种情况调多少轮都白搭:需求你自己也没想清楚,以及这块活超出了当前模型的能力上限。前者的信号是你每轮都在改验收标准;后者的信号是无论怎么拆,最小的那一块也做不对。前者去写清楚需求,后者换条路——换实现方式、降低目标、或者人工写核心部分。
顺便提醒一句:不要指望”换个海外的更强模型”来绕过粒度问题。部分海外 AI 工具的官方服务对中国大陆存在区域限制,具体覆盖范围与开通条件以各家官方最新说明为准,本文不讨论任何绕过方式;何况就算接上了,边界画错该返工还是要返工。
六、避坑清单
按文件数量切分。 会踩是因为文件数是最容易看见的指标,看起来很客观。但三个小文件可能耦合成一件事,一个大文件可能装着三件独立的事。避法是改用验证命令做刀口,切完先问每块用什么命令判定完成。
让多块任务同时改同一个入口文件。 会踩是因为路由表、导出索引、配置总入口这类文件天然被所有功能碰到,切分时容易忽略。避法是把入口文件的改动单独作为一块,放在所有功能块完成之后统一做。
开工时不给可改路径白名单。 会踩是因为你默认它知道边界在哪,而它默认没被禁止就是允许。避法是任务描述里显式列出允许改动的路径,并在收尾时用 git diff --name-only 核对,超纲直接打回。
用重试掩盖切法问题。 会踩是因为重试的即时成本很低,点一下就行,而拆分要动脑子。代价是你会在同一个坑里烧掉几倍的时间和额度。避法是给自己定个硬规矩:同一块失败两次就必须停下来看失败原因是否收敛,不收敛就拆。
并行块共用一个工作目录。 会踩是因为并行开起来最省事的方式就是多开几个会话对着同一个目录。结果是后写的覆盖先写的,而且覆盖发生得悄无声息。避法是每个并行块一个独立工作树或独立副本,合并阶段再统一处理冲突,冲突标记该人工看就人工看。
把编排层的活也切碎。 会踩是因为”既然分工好,那就全都分”。但决定”下一步做什么”这件事本身依赖全局状态,切碎之后没有任何一块看得到全局,最后是你在补。避法是编排决策留在一个地方,只把执行切开。
忽略上游限流对粒度的影响。 会踩是因为碎块意味着更多次独立请求,撞上限流的概率随之上升,而失败重试又会放大请求量,形成正反馈。各家的限流阈值、计费口径和返回的错误码不一样(常见是 HTTP 429,也有产品用别的码表示过载),具体以官方最新说明为准;但机制是一样的:并发越高越容易触发,触发后重试越猛越难恢复。避法是碎块并行时控制同时在跑的数量,看到限流报错先降并发、拉长间隔,再谈别的。
收束
粒度这件事没有一个放之四海的标准答案,但有一套稳定的判据:能不能用一条命令验收、上下文装不装得下还有没有余量、能不能单独回滚、接口是不是开工前就定死了。四条里缺一条,你就会在某个环节替它补窟窿。
开工前过一遍这份自检:
- 这一块的完成信号是哪条命令?说得出来吗?
- 它要读的东西装进去之后,还剩多少工作空间?
- 它炸了,我回滚哪几个路径就能干净?
- 接口定义在不在磁盘上、是不是标了只读?
- 允许它改的路径,我列出来了吗?
- 如果它连续两次失败,我的下一步是拆还是自己写?
六条都有答案,你基本不会再遇到”每块都成功、拼起来跑不通”这种最耗人的返工。