拆分任务与编写 prompt
怎样拆分任务、安排依赖,写出全新会话能独立完成的 prompt,并把每个任务的结论交给下游。
工作流跑得好不好,很大程度上取决于任务怎么拆、prompt 怎么写。每个任务都由一个全新的 Agent 会话执行,它只知道 prompt 和它自己从仓库里读到的文件(原因见任务与会话);拆任务、排依赖和写 prompt 时要处理的问题,大多由此而来。
拆任务:一个任务一个目标
一个任务对应一个能用一句话说清、完成后能够检验的目标,工作量控制在一个会话能从容做完的范围内。拆分时要同时照顾两头:任务太大,会话的上下文会被撑满;任务太小,信息会在任务之间丢失。
以“给项目的 API 加上基于角色的权限控制”为例。把它整个写成一个任务,Agent 要在一个会话里读完路由、中间件和数据模型的代码、决定方案、写测试、实现、跑测试。项目小的时候这也许没有问题;项目大一些,光是读代码就占去大半上下文,等写到实现时,前面定下的约束可能已经不在上下文里了。更稳妥的拆法是三个任务,先写测试,再实现:
- 规划权限方案: 读路由、中间件和数据模型相关的代码,决定有哪些角色、每个接口要求什么权限、检查加在哪一层、没有权限时返回什么,以及要改哪些文件、怎样测试,写进
docs/plans/access-control.md。 - 按方案写测试: 按方案写出权限检查的测试,运行它们,确认因为权限控制还没有实现而失败。
- 实现权限控制并跑通: 按方案实现角色的数据模型、权限检查和各个接口的接入,运行全部测试,修到全部通过。
拆开之后,规划任务可以放开读代码,读得再多也只占用它自己的上下文;写测试和实现的任务都从一份写好的方案开始,上下文里只有它们真正需要的东西;测试先写好,实现任务的完成标准也就现成了:让这些测试通过。方案文件还是一个天然的检查点,想先看过方案再动手,就给规划任务设 autoAccepted: none,让它完成后停下来等你审核(见自动接受)。
粒度是否合适,可以从两个方向检查:
- 太大的信号: 同样的工作放在你平时的交互会话里,常常做到上下文被自动压缩;或者目标要用“并且”连起好几件事才说得完。这样的任务越往后越容易走样,按阶段或按模块再拆开。
- 太小的信号: 几步紧密耦合,一步的产出只是下一步的半成品,往往要来回几轮才能做完。比如把“修一个报错”拆成了“排查原因并输出分析报告”和“依照分析报告进行修复”两个任务:第一次排查不一定准,修完运行测试发现还有偏差,就得再排查、再修复,拆开之后这样的迭代做不成。排查的思路也要先落成报告才能传过去,途中容易丢失;下游还得把上游读过的代码重读一遍,多花一份 Agent 用量,多出的任务也多消耗 1 点 CodeDAG 额度(见估算一个工作流)。这样的几步合成一个任务更好。
各任务的工作量最好大致相当。一个任务明显比其他任务大,它往往就是最该再拆的那个;在并行的几路里,它还会让等它的下游等得最久。
安排依赖:只表达真实的先后
依赖只写真实存在的先后:一个任务要用到另一个任务的产出,就依赖它;用不到,就不要把它们串起来。需要把几路结果放在一起综合或验证时,加一个依赖它们全部的汇合任务。
依赖决定了下游能看到什么:在默认的隔离分支模式下,没有声明依赖的任务,即使对方早已做完,也看不到对方的改动(见CodeDAG 中的依赖是什么)。反过来,把互不相干的任务串在一起,结果不会更好,只会让后面的任务白白排队。
常见的依赖形状有三种。
线性: 每一步都要用到上一步的结果,比如前面拆出的规划、写测试、实现。
flowchart LR t1["任务 1<br/>规划权限方案"] --> t2["任务 2<br/>按方案写测试"] t2 --> t3["任务 3<br/>实现权限控制并跑通"]扇出扇入: 几件互不相关的事同时做,最后由一个任务把结果合起来,比如从安全、性能、可读性三个角度各审查一遍同一份改动,再汇总成一份问题清单。
flowchart LR t1["任务 1<br/>安全审查"] --> t4["任务 4<br/>汇总问题清单"] t2["任务 2<br/>性能审查"] --> t4 t3["任务 3<br/>可读性审查"] --> t4菱形: 几个并行任务都以同一份产出为前提,最后再汇合验证。权限控制的例子也可以这样安排:方案写好之后,一个任务按方案写测试,另一个任务按方案写实现,两者同时进行,最后一个任务运行测试、修到全部通过。
flowchart LR t1["任务 1<br/>规划权限方案"] --> t2["任务 2<br/>按方案写测试"] t1 --> t3["任务 3<br/>按方案实现"] t2 --> t4["任务 4<br/>运行测试并修复"] t3 --> t4合并分支不需要单独安排一个任务。一个任务依赖几个上游时,CodeDAG 先把各路上游的结果自动合并进它的分支,遇到冲突也自动先交给 Agent 处理(见汇聚节点的合并与冲突),然后才开始执行这个任务自己的工作;没有任务把几路汇到一起时,它们的结果也会在工作流结束时合并到集成分支。所以汇合任务只为真实的工作而加:这件工作要同时用到几路结果才能做,比如综合、比较或运行测试。
图上并列的任务,也不一定真的同时执行。隔离分支模式下,同时执行的任务数不会超过可用的工作区数,只有主仓库时它们会依次执行,结果相同,只是慢一些;准备更多工作区见并行执行与工作区。并行的几路改到同一个文件时会发生什么,见两个并行任务改了同一个文件怎么办。
写 prompt:让一个全新的会话独立完成
给任务写 prompt,和你平时把一项耗时较长的工作交给 Agent、让它独自做完时的写法没有两样:平时怎么写、用什么语言写,这里就怎么写,CodeDAG 对 prompt 的格式没有要求。值得多留意的,是任务会话和你平时的交互会话不同的几处:
- 它从零开始。 任务 Agent 只知道 prompt 和它自己从仓库里读到的文件,看不到你和别人的讨论,也看不到工作流里的其他任务。为什么要做这件事、上游任务把结果写在了哪里、有没有别的任务同时在做另一部分,都要写进 prompt 它才知道。
- 它结束工作,任务就算完成。 CodeDAG 以 Agent 结束工作作为任务完成的依据,不检查结果是否达标。做到什么程度算完,值得写得具体一些,最好是可以检验的结果,比如“运行项目的全部测试并全部通过”,而不是“优化一下”。
- 它提问时可能没有人回答。 无人值守时,它停下来等人,任务就卡在那里。预料得到的意外,比如要找的东西可能找不到,最好事先交代出路:写明兜底的处理方式,或者直接说明由它按合适的方式自行处理。几条要求在某种情况下互相矛盾,也会让它停下,见约束要在任何情况下都能同时满足。
- 它读到的不一定是你眼前的文件。 默认的隔离分支模式下,任务只读得到自己那条任务分支上的内容,不在这条分支上的改动默认读不到,见必读材料要让任务读得到。
一个可以参考的结构
还不熟悉怎样给这类任务写 prompt 的话,可以拿下面四段当起点:
目标:
要做成什么,一两句话。任务无人值守时,可以直接写成“以无人值守的方式实现……”。
上下文与必读材料:
- 为什么要实现这个目标,结果打算怎么用。
- Agent 不一定会去读、而你认为必须读的材料,写出具体位置,比如上游任务产出的文件。
完成标准:
- 交付什么:哪些改动,或者写到哪个路径的文件、包含哪些内容。
- 怎样确认已经完成:运行哪些命令,结果应当是什么。
约束:
- 可以改动的范围,以及不能碰的地方。目标里的“以无人值守的方式实现……”,适合在确定任务无人值守、也没有设人工审核时写上,让它知道没有人可问。
“上下文与必读材料”补上的是只凭目标得不到的信息;实现这个目标本来就会去读的内容不必写,比如目标是修复登录接口的超时,就不必再写“先读登录相关的代码”。
这个结构可以随任务增减:哪一段没有内容可写就省去,四段装不下的内容另起一段。比如:
- 目标比较复杂时: 可以在目标之后加一段,把一两句话写不下的内容展开,按内容取名:
- 对输出格式有要求时: 要求简单的,比如只规定文件分哪几节,写在完成标准里就可以;比较复杂的,可以另起一段“输出格式”。
- 对完成的流程有明确要求时: 可以在约束之后加一段流程,按顺序写明步骤,比如修一个缺陷时,先复现问题,再写出能暴露它的失败测试,然后修复,最后运行全部测试;只是建议的标为“推荐流程”,必须照做的标为“强制流程”。
- 有容易被忽略的要点时: 前面几段都没有覆盖、Agent 不一定会留意,而你预料到忽略了会明显影响结果的地方,可以集中写进一段“注意事项”,比如某个目录里的文件是自动生成的,直接改会被覆盖。
下面是第二个工作流教程中任务 2 的 prompt。上游任务 1 已经把两个待办的计划写进文件,任务 2 实现其中第一项,同时任务 3 在另一个工作区实现第二项:
目标:
实现 docs/plans/two-small-changes.md 中“第一项”描述的待办。
上下文与必读材料:
- docs/plans/two-small-changes.md 是上一个任务写的计划,“第一项”一节写明了目标、涉及的文件和完成标准。
完成标准:
- 达到“第一项”写明的完成标准:先写出最小的测试,再实现到测试通过。
约束:
- 只修改“第一项”列出的文件,不修改计划文件,也不实现“第二项”的内容。
注意事项:
- 另一个会话正在另一条分支上同时实现“第二项”,你看不到它的改动;两边的结果之后由 CodeDAG 合并。目标没有复述待办的内容,而是指向计划文件中的一节:具体做什么由上游任务决定,prompt 不必随之改写。注意事项写的是它自己发现不了的事:另一项正由另一个会话同时实现。
约束要在任何情况下都能同时满足
写完 prompt 后,值得再检查一遍:其中的各项要求,在各种情况下能不能与约束同时满足。任务 Agent 遇到互相矛盾的要求,常常会停下来问人该怎么办;无人值守时没有人回答,这个任务就卡在那里,处理方法见Agent 在等你审批或回答。
最容易忽略的是重新运行。比如完成标准要求“计划写到 docs/plans/two-small-changes.md”,约束写着“不修改任何已有文件”,第一次运行没有问题;重新运行时这个文件已经存在,两条要求就冲突了。在完成标准里补上“文件已存在时直接覆盖”,约束改成“除了这份计划,不新建、不修改任何文件”,两种情况就都有了出路。想保留每一次的结果、不让旧文件被覆盖时,做法见写进约定的文件。
必读材料要让任务读得到
prompt 点名要读的材料,要确保任务真的读得到。在默认的隔离分支模式下,任务读到的是它自己那条任务分支上的内容:本次工作流起点的那次提交,加上上游已被接受的结果(见任务 Agent 能看到什么)。不在这条分支上的改动,它默认读不到:工作流开始之后你自己继续做的修改是这样,没有被它依赖的其他任务的改动也是这样。所以必读材料要么在工作流开始前已经提交,要么由上游任务产出;刚写好的需求文档,先用 git commit 提交到当前分支,再提交工作流。
把结论传给下游
上游把结论写进约定的文件,下游的 prompt 指名去读这个文件。任务之间相通的只有仓库里的文件,依赖只保证上游的文件在下游开始时已经到位(见依赖);读哪个文件、读哪一部分,要由下游的 prompt 告诉它。分析、调研、审查这类不改代码的任务也不例外,结论只留在对话里,下游就无从得知。
写进约定的文件
在上游 prompt 的完成标准里写明文件路径和内容结构,在下游 prompt 的必读材料里写上同一个路径,这个路径就是两个任务之间的接口。以权限控制为例,规划任务的完成标准写“方案写到 docs/plans/access-control.md,分为‘角色与权限’‘改动位置’‘测试要点’三节”,实现任务的必读材料写“docs/plans/access-control.md 是上一个任务写的方案,按其中‘角色与权限’和‘改动位置’两节实现”。约定好小节,下游就能按小节名引用,不必在 prompt 里复述内容。
下游要依赖上游写的文件时,在 prompt 开头就列出这些文件,并写明“先完整阅读以下文件,再开始行动”,让它在动手之前掌握上游的结论。文件很长时,可以再补一句“分批阅读,确保读完”。
这些文件和代码改动一样,会随着任务的结果进入集成分支。不想让它们进入主分支,在合入主分支之前删除即可。
同一个工作流反复运行时,写到固定路径的文件会被下一次运行覆盖。共享工作区模式下 CodeDAG 不建提交,被覆盖的旧内容只有你自己提交过才找得回来。需要保留每一次的结果时,可以把约定从一个固定的路径换成一条规则,例如快速开始让上游任务每次按序号新建目录,下游任务读序号最大的那个。
多个任务共用的背景放进 .codedag/context/
需求、约定这类不由任何任务产出、却有好几个任务要读的材料,可以提交到项目仓库里,也可以放进 .codedag/context/。.codedag/ 是独立于项目仓库的 Git 仓库,放在这里的文件不用提交到项目,不会让主仓库的工作区变得不干净,也不会出现在任务的改动和集成分支里,还能随 .codedag 仓库与团队共享(见团队共享)。context/ 这个目录需要你自己创建,CodeDAG 不会自动把里面的内容交给任务,仍要在 prompt 里指名。
.codedag/ 只存在于主仓库里。任务在副本中执行时,它的工作区里没有 .codedag/,所以项目登记了副本时,prompt 里的路径要能从副本指到主仓库。副本和主仓库放在同一个目录下时,写主仓库相对于副本的路径即可,例如 ../my-app/.codedag/context/access-control-spec.md;这个路径在主仓库里同样成立,任务分到哪个工作区都读得到。各个副本相对主仓库的位置不一样时,改写绝对路径,例如 /Users/you/my-app/.codedag/context/access-control-spec.md。两种写法都依赖本机的目录布局,绝对路径还依赖仓库放在哪里;工作流要共享给团队时,做法见让共享的工作流在每台电脑上都能运行。
对在副本里执行的任务来说,主仓库里的文件在它的工作目录之外:用 Claude Code 且不是完全授权时,读取它可能要先经过审批,无人值守的任务会停在那里。想让任务不在这里停下,有三种办法:
- 给任务完全授权,见决定无人值守还是有人值守。
- 任务以
auto模式运行时,提交之前先手动允许一次,见提前允许 Claude Code 读取工作目录以外的文件。 - 把材料改为提交到项目仓库里,让任务在自己的工作区里就能读到。
让下游直接查看上游的改动
上游的产出本身就是代码时,下游可以用 Git 查看它改了什么,不必另写一份说明。在隔离分支模式下,上游被接受的结果以提交的形式合并进下游的分支,下游的 prompt 写明和什么比较即可。比如审查任务写“运行 git diff main...HEAD,审查本次工作流开始以来的全部改动”;只关心某个文件时,在命令末尾加上 -- <文件路径>。如果本次工作流不是从 main 开始的,把命令里的 main 换成起点所在的分支。共享工作区模式下 CodeDAG 不建提交,git diff 看到的是所有任务混在一起的未提交改动,分不出哪一部分来自哪个任务。
复用你已有的技能与规则
任务会话就是在你仓库的工作区里启动的普通 Claude Code 或 Codex 会话,CLAUDE.md、AGENTS.md、你为 Agent 配置的规则和安装的技能都照常生效,不需要再抄进 prompt。想在某个任务里用某个技能,直接在 prompt 里点名,例如“用某某技能完成这次审查”。
Claude Code 的 hooks 在任务会话里同样会触发。比如你用 Stop hook 要求它结束前必须跑完测试,它在任务里也会照做,CodeDAG 会等它真正停下,才判定任务完成。
常见问题
把 cdg 命令写进 prompt 会怎样
任务 Agent 会照做,而这些命令作用于真实的队列。任务会话能像你的终端一样运行 cdg,装了 CodeDAG Guide 插件时它还知道怎么用:它在任务里提交的工作流会排进同一个项目的队列,要等当前工作流结束才开始,开始时同样按任务数消耗额度;审核、暂停、中断这类命令作用于正在执行的工作流,可能连任务自己也一起停下。
推进流程本来就不需要任务 Agent 参与:下一个任务什么时候开始由依赖决定,结果要不要放行由审核门禁决定,提交、审核和干预由编排者在工作流之外操作(见谁负责什么)。prompt 只写这个任务自己的工作;想在某个任务之后停下来确认,就给它设 autoAccepted: none,而不是在 prompt 里写“完成后等我确认”。
给编排 Agent 的要求,要原样写进任务的 prompt 吗
不要原样转述。你对编排 Agent 提的要求,大多是关于工作流怎么搭的,要落实成任务的拆分和字段,而不是写进某个任务的 prompt。比如“实现完成后让另一个模型审查一遍”,应当变成一个依赖实现任务、由另一家 Agent 执行的审查任务;“每一步都等我看过再继续”,应当变成工作流的 taskAutoAccepted: none。把这些话抄进任务的 prompt,任务 Agent 既加不了任务,也改不了审核设置,只能照字面去猜,甚至自己去运行 cdg(见把 cdg 命令写进 prompt 会怎样)。
只有真正需要任务 Agent 去做、去读、去遵守的内容,才写进 prompt。CodeDAG Guide 也要求编排 Agent 这样区分;审阅它写的工作流时,可以顺带看看 prompt 里有没有混进这类转述的要求。
上游把结论只留在了对话里怎么办
趁上游还没被接受,让它原来的会话把结论写进文件。上游停在审核门禁上时,比如你给它设了 autoAccepted: none,或者它是隔离分支模式下没有留下任何改动的任务,用 request-changes 把要求交给原会话:
cdg task review request-changes 1 -w access-control -m "把你的结论写进 docs/plans/access-control.md,分为角色与权限、改动位置、测试要点三节"原会话里还保留着当时的对话,它补写完文件后回到审核门禁,你检查之后再接受,下游才会开始,做法见想让它在现有结果上接着改:request-changes。
上游如果已经被接受,下游就在没有这份结论的情况下开始了,这一点在本次执行中无法挽回(见审核结论不能更改)。共享工作区模式下的任务,即使什么都没写也会被自动接受,更容易出现这种情况。这时可以用 cdg task logs <任务 ID> -w <工作流> 读上游在对话里的回复,把需要的结论整理进文件,再把受影响的工作放进新的工作流接着做,见已经接受的结果有问题。
两个并行任务改了同一个文件怎么办
多数时候不需要特别处理:两个任务改的是同一个文件的不同位置,改动会合到一起。会出问题的只是少数情形,是哪些取决于执行模式。
- 隔离分支模式: 两个任务各在自己的分支上修改,执行时互不影响。它们的结果在汇合任务开始前或汇入集成分支时合并,改的位置不同就自动合并,改到同一处才会冲突。发生冲突时,CodeDAG 会自动先交给 Agent 解决,三轮仍未解决才停在
on-hold等你处理,见汇聚节点的合并与冲突与故障排除中的合并冲突。 - 共享工作区模式: 两个任务直接改同一个目录里的同一个文件。Agent 每次通常只改动文件里的一小段,两边改的段落不同,改动就叠加在一起;恰好改到同一段时,后改的一方会因为要替换的原文已经变了而编辑失败,重新读取文件后再改一次。真正会互相影响的是另一种情形:一个任务的代码改到一半,另一个任务开始编译或运行测试,没写完的代码会影响它的结果,CodeDAG 不会发现,也不会提示。
预计两个任务会大量改动同一处时,可以在拆分时避开:在各自的约束里写明只改哪些文件;确实要改同一处,就让一个任务依赖另一个,改成先后执行;需要合成一份结果时,让它们各写各的文件,再由汇合任务合并内容。要同时改代码、运行测试的几个任务,用默认的隔离分支模式更合适,见执行模式。
相关
最后更新