复杂任务的 Spec 怎么写:从提示词到工程资产
目录
一、复杂任务的第一条原则:不要让一个 Agent 从头干到尾
二、编排者入口写什么:入口要薄,边界要硬
三、spec 不是一个文件,而是一组目录的分工
四、一个 skill 应该怎么写:薄入口,厚引用,可验证输出
五、rules、docs、references 的区别:不要把所有知识都叫“文档”
(一)SKILL.md:路由卡
(二)references:skill 私有长内容
(三)rules:硬约束
(四)docs:长期知识和决策记录
六、script-backed skill:确定性的事,不要让模型自由发挥
七、skill 要分层:编排层、阶段层、原子层
(一)编排层:维护状态机,不做具体活
(二)阶段层:负责一个交付阶段
(三)原子层:只做一件确定的事
八、用 manifest 管住整张 skill 图
(一)manifest
(二)三种 edge声明边
1. handoff:阶段流转
2. dynamic_load:按任务动态加载能力
3. semantic:证据依赖
九、复杂任务 spec 必须设计失败路径
十、Spec 里最容易缺的东西:产物契约
十一、写复杂 spec 的一个可复用模板
1. 任务类型
2. 角色和权限
3. 路径到角色映射
4. 阶段状态机
5. 门禁规则
6. 知识装载规则
7. 脚本规则
8. 失败处理
9. 最终报告
十二、记住一句话:复杂 spec 的核心不是“写更长”,而是“拆得更准”
参考文章
我们在《Harness 到底指什么:Coding Agent 时代的运行时边界、工程纪律与业务分层》讲了 harness 和业务工程的边界:harness 是平台层,负责提供运行环境、工具接口、上下文装载、权限控制和执行协议;业务工程建在它之上,不去改它。
但这只解决了第一个问题:平台和业务的边界在哪里。
真正让复杂任务稳定跑起来的,是第二个问题:业务侧的 spec 到底应该写什么。
一个简单任务,可以是一句话提示词。比如“把这段代码改成 TypeScript”。模型读完,直接干。
但当任务复杂到需要多人协作、跨端实现、多阶段推进、过程要可追溯、结果要可验收时,spec 就不能再是一段提示词了。它必须变成一套有结构的工程资产。
我现在更倾向于这样定义复杂任务的 spec:
Spec 不是告诉模型“做什么”的提示词,而是定义一套 AI 工程系统如何分工、如何加载知识、如何推进状态、如何判断完成、如何在出错时回退的操作协议。
它至少要回答四个问题:
第一,复杂工作由谁做,谁不该做。
第二,每个阶段怎么流转,什么时候能进入下一步。
第三,知识、规则、流程、脚本分别放在哪里。
第四,怎么让每一步的产物、证据和责任都能被追踪。
这篇就正面回答这个问题。
一、复杂任务的第一条原则:不要让一个 Agent 从头干到尾
很多人写复杂任务 spec 的第一个误区,是把复杂性继续塞给一个 Agent。
于是入口提示词越来越长:
你要先理解需求,
再做架构设计,
再写代码,
再补测试,
再自我评审,
再跑验证,
最后总结风险。
看起来很完整,实际很危险。因为复杂任务失败,往往不是模型“不够聪明”,而是它在同一个上下文里同时承担了太多身份。
它一会儿是产品经理,一会儿是架构师,一会儿是实现者,一会儿又是评审者。每个身份应该遵守的约束不同,但上下文混在一起之后,边界会逐渐变模糊。
最常见的几个问题是:
它在做实现时忘了设计阶段的约束;
它在做评审时默认相信了自己刚写的代码;
它在修 bug 时顺手扩大了改动范围;
它在上下文快满时开始丢掉早期决策;
它在阶段切换时没有留下可复查的证据。
所以我的做法是:一个编排者,加一队专职执行者。
编排者不是“更强的全能 Agent”,而是“流程控制器”。它负责判断当前任务处于哪个阶段、门禁是否通过、下一步该交给谁、产物是否合格、是否需要回退。
它不亲自写业务代码。
真正的工作交给专职子 Agent,每个子 Agent 在一个新的上下文里启动,只加载这一次任务需要的规则、文档、输入和工具。这样上下文干净,职责清楚,也更容易审计。
我通常把执行者拆成这些角色:
需求分析师:把需求文档、范围、非目标、验收标准拆成结构化输入。
架构师:做技术方案、统一跨端契约、写 ADR,但不写实现代码。
端侧实现者:按平台拆开,比如 iOS、Android、Web、Backend,各写各的范围。
代码评审者:从安全、性能、规范、兼容性、参考实现对齐等角度审查。
测试工程师:补测试口径,优先写会先失败的验收测试。
联调工程师:跑端到端构建、安装、运行、日志收集和跨端一致性验证。
效率工程师:把反复出现的问题沉淀成脚本、模板、检查器或新的 skill。
这套角色划分的意义,不只是“分工更清楚”。更关键的是:角色本身是规则装载单元。
不同角色应该自动加载不同规则。
- 架构师需要加载架构约束、跨端契约规范、ADR 模板。
- 端侧实现者需要加载平台规范、目录规则、UI 对齐清单、测试命令。
- 代码评审者需要加载安全检查、性能检查、反模式清单。
- 联调工程师需要加载运行环境、日志路径、构建脚本和故障分流规则。
如果角色选错了,问题就不只是“这个 Agent 不擅长”,而是它会绕过本该生效的一整套规则。
也许我们都吃过类似的亏。比如有一次端上 UI 实现被误派给架构师。架构师当然能写代码,但它没有加载端侧实现者绑定的 UI 约束、组件边界和视觉还原检查。结果代码能跑,结构也说得过去,但视觉对齐问题又冒出来了。所以派发不能凭感觉。我的规则是:
先按阶段选角色,再用“改动文件路径 → 角色”的映射表校验写入范围。两者冲突时,必须停下来记录冲突,不允许擅自扩大某个 Agent 的写入范围。
比如:
docs/adr/**只能由架构师写;ios/**只能由 iOS 实现者写;android/**只能由 Android 实现者写;web/**只能由 Web 实现者写;scripts/validation/**可以由效率工程师写,但必须附测试;生产代码不能由编排者直接改。
跨端任务尤其要严格。如果一个需求同时改 iOS、Android、Web,不允许派一个“全栈 Agent”包办。正确做法是:
- 先由架构师产出统一契约,明确字段、状态、错误码、交互边界和验收标准;
- 再让各端实现者在隔离写入范围内并行实现;
- 最后由联调工程师按统一契约验证一致性。
复杂任务里,多 Agent 不是为了炫技,而是为了把“职责、上下文、规则、写入权限和证据链”拆开。拆开之后,系统才可控。
二、编排者入口写什么:入口要薄,边界要硬
编排者的行为,绝大部分由入口文件决定。
在 Claude Code 里可能是CLAUDE.md,在其他环境里可能是AGENTS.md、SYSTEM.md或某个入口 spec。名字不重要,作用一样:它是 Agent 启动时最先读到的业务侧协议。
很多项目会把入口文件写成一本巨大的说明书。
所有流程、所有规则、所有模板、所有反模式、所有目录说明,都塞进去。结果入口文件越来越长,模型每次启动都要读一遍,读完还抓不住重点。
我现在的原则是:入口只放启动索引和硬边界,不放具体步骤。入口文件应该像一张地图,而不是一座仓库。
它要告诉编排者:
任务来了先判断什么;
不同任务类型去读哪个 skill;
不同路径受哪些 rule 管;
哪些事情绝对不能做;
什么情况可以继续推进;
什么情况必须停下来;
产物和状态写到哪里。
但它不应该详细解释每个阶段怎么做。具体步骤放进 skill,硬约束放进 rules,角色契约放进 agents,长期知识和决策记录放进 docs。
入口越薄,越容易稳定。入口越厚,越容易变成上下文噪音。我会在入口里写死一段“编排者契约”。核心大概是:
编排者是流程控制者和收口者,不是执行者。
编排者负责判断阶段、门禁、交接、回退和是否继续。
编排者默认不直接修改生产代码。
小型机械修订、索引更新、状态文件维护、跑校验可以自己做。
具体业务实现、架构设计、测试补充、代码评审默认派给子 Agent。
不得绕过门禁、评审、测试和写入范围限制。
不得让一个 Agent 同时承担设计、实现和评审。
遇到非致命问题,记录 owner、route、evidence、next step 后继续推进。
只有致命错误、权限风险、破坏性操作、范围扩大、门禁阻塞,才允许停下来请求人工决策。
最后两条非常重要。复杂工作流最怕的是 Agent 一遇到小问题就停下来问你。
缺一个日志路径,问你。
测试命令失败一次,问你。
某个文件命名不一致,问你。
一个可选字段没找到,问你。
这类 Agent 看起来很谨慎,实际无法承担复杂任务。因为复杂任务天然会有小缺口、小冲突、小不确定。系统必须区分“可以记录后继续”的问题和“必须阻塞”的问题。
我通常把问题分成四类:
- info_gap:信息缺口,但不影响当前阶段继续。记录下来,交给对应 owner 后续补齐。
- local_failure:局部失败,比如某个非关键测试失败。记录日志、归属和重试路径,继续其他不依赖它的任务。
- gate_blocker:门禁阻塞,比如契约缺失、测试无法运行、产物不存在。必须停在当前阶段。
- risk_decision:需要人类接受风险,比如跳过测试、扩大范围、修改公共接口、执行破坏性操作。必须回到用户。
这套分类写进入口,编排者才不会把所有问题都当成阻塞,也不会把真正的风险偷偷吞掉。
入口里还应该写清楚“哪些决策需要人”。比如:
扩大任务范围需要人。
修改公共 API 需要人。
删除数据、重置环境、批量迁移需要人。
接受安全、合规、性能风险需要人。
对外发布、合并主干、发版需要人。
而这些不需要每次问:
派发下一个子 Agent;
读取对应 skill、rule、docs;
运行已声明的校验脚本;
更新状态文件;
在同一范围内修复明确的校验失败;
把非致命问题记录到 backlog。
入口文件的价值,不是让 Agent “更懂业务”,而是让它知道:什么事它有权决定,什么事必须交还给人。
还有一个工程细节很容易被忽略:不要直接编辑生成态入口文件。
如果你的CLAUDE.md和AGENTS.md是从模板投影出来的,那权威源应该是模板,而不是生成文件。直接改生成文件,下次同步就会被覆盖。
在多 Agent 协作里,这个坑更常见。某个 Agent 以为自己修了入口规则,其实只是改了一个一次性产物。正确做法是:
模板是权威源;
生成文件只读或只由脚本生成;
修改入口规则必须改模板;
生成文件头部标明“不要手改”;
CI 或校验脚本检查投影是否一致。
入口文件不是越全越好。它要短,但要硬。短,是为了减少上下文噪音。硬,是为了让整个系统有不可绕过的边界。
三、spec 不是一个文件,而是一组目录的分工
复杂任务的 spec 不应该堆在一个大文件里。它应该拆成几类资产,每类回答一个问题。
我通常用三类目录:
skills:怎么做。rules:必须遵守什么。docs:为什么是现在这样。
这三类目录的分工很关键。
如果流程变了,改 skill。
如果约束变了,改 rule。
如果背景、架构决策、长期知识变了,改 docs。
同一条知识只能有一个权威位置。否则系统很快会变成这样:
skill 里写一版流程,
rule 里又写一版限制,
docs 里还有旧版说明,
入口文件里复制了一段过时内容,
最后模型每次读到的东西都互相矛盾。
复杂 spec 的一个核心目标,就是消灭这种“多处复制的伪知识”。
四、一个 skill 应该怎么写:薄入口,厚引用,可验证输出
每个 skill 是一个能力单元。它可以很大,比如“完成一个跨端需求开发”;也可以很小,比如“生成 ADR 编号”或“检查契约字段是否齐全”。但无论大小,它都应该有稳定的文件结构。
一个典型 skill 目录长这样:
skills/ feature-delivery/ SKILL.md references/ requirement-analysis.md architecture-contract-template.md gate-definition.md scripts/ validate_state.py check_outputs.sh其中SKILL.md是入口。它不应该很长。它的职责是让模型快速判断:
这个 skill 是干什么的;
什么时候应该用;
什么时候不应该用;
需要哪些输入;
会产出什么;
怎么判断通过;
需要按需加载哪些 reference;
有没有脚本兜底。
我一般会给SKILL.md一个固定骨架。
第一段是 frontmatter:
name: feature-delivery description: > 适用:需要从需求分析推进到设计、实现、评审、测试、联调的复杂业务需求。 不适用:单文件机械修改、纯问答、无需状态追踪的小修小补。 典型触发语:实现这个需求、完成这个功能、跨端改造、从需求到上线。 layer: orchestrator risk: high human_review: required_before_release这里的 description 不是写给人看的宣传语,而是写给路由器看的。它必须包含三件事:
- 适用;
- 不适用;
- 典型触发语。
只有这样,模型才更容易在正确场景召唤它,也更容易在错误场景拒绝它。
第二段是职责边界。比如:
本 skill 负责复杂业务需求的端到端编排,包括需求结构化、架构设计、分端实现、代码评审、测试补充、联调验证和收口报告。 本 skill 不直接写生产代码,不替代平台层 harness,不修改未授权路径,不跳过阶段门禁。职责边界一定要同时写“管什么”和“不管什么”。只写正向职责,很容易让 Agent 自动扩权。
第三段是输入输出。
输入包括:
需求来源;
目标范围;
非目标范围;
受影响路径;
验收标准;
风险等级;
已有参考实现或设计稿;
允许使用的工具;
写入范围。输出包括:
结构化需求;
设计方案或 ADR;
跨端契约;
实现 PR 或补丁;
测试报告;
评审报告;
联调日志;
阶段门禁结论;
最终收口摘要。
第四段是工作方式。
这里不要写长篇方法论,只写最小流程:
1. 读取任务输入,生成 state.json。 2. 调用 requirement-analysis phase。 3. requirement gate 通过后,调用 architecture phase。 4. architecture gate 通过后,按路径和平台派发 implementation agents。 5. 实现完成后,调用 review phase。 6. review gate 通过后,调用 test phase。 7. test gate 通过后,调用 integration phase。 8. 生成 final report。第五段是按需加载 references。
需要拆需求时,读取 references/requirement-analysis.md。 需要定义跨端契约时,读取 references/architecture-contract-template.md。 需要判断阶段是否通过时,读取 references/gate-definition.md。这点很重要。
SKILL.md要薄,长知识放 references。模型只有走到那一步,才把对应内容读进上下文。否则每次召唤 skill 都读一堆用不上的内容,既浪费上下文,也会干扰判断。
第六段是验证。比如:
通过条件: - state.json 存在且 schema 校验通过; - 每个阶段都有 gate 结论; - 每个 blocked 都有 owner、reason、evidence、rollback_path; - 每个 pass 都有可追溯证据; - 生产代码改动必须有对应实现者和评审者; - 最终报告必须列出完成项、未完成项、风险和后续动作。一个 skill 如果没有验证标准,就只是另一段提示词。一个 skill 有了验证标准,才开始变成工程资产。
五、rules、docs、references 的区别:不要把所有知识都叫“文档”
复杂 spec 最容易混乱的地方,是所有东西都被叫成“文档”。
流程文档、规则文档、背景文档、模板文档、排障文档、ADR,全都混在一起。模型读的时候不知道哪些是必须遵守,哪些只是参考;人维护的时候也不知道改哪里才算改到了权威源。
我会把它们拆成四类。
(一)SKILL.md:路由卡
它回答:
这个能力什么时候用;
怎么启动;
输入输出是什么;
最小流程是什么;
需要读哪些 reference;
怎么算完成。
它要短。
(二)references:skill 私有长内容
它回答:
某一步具体怎么做;
模板长什么样;
有哪些案例;
有哪些反模式;
某类分析应该按什么框架展开。
references 是按需加载的,不是常驻上下文。
比如需求分析 skill 可以有:
references/ ambiguity-taxonomy.md non-goals-template.md acceptance-criteria-examples.md模型只有在做需求分析时才读这些内容。
(三)rules:硬约束
rules 回答:
什么必须遵守;
什么绝对不能做;
哪些路径归哪个角色;
哪些检查必须跑;
哪些风险必须人工确认。
rules 不应该被 skill 复制。它应该通过路径、角色或全局启动自动挂载。
我通常把 rules 分成三类:
- 全局规则:任何任务都必须遵守,比如不得修改 harness、不得跳过安全门禁、不得执行破坏性操作。
- 路径规则:进入某些目录自动加载,比如
ios/**、android/**、backend/**各有自己的约束。 - 角色规则:某个 Agent 被派发时自动加载,比如评审者必须加载安全、性能、规范检查清单。
这样做的好处是:规则只有一个权威源。skill 只引用规则,不复制规则。
(四)docs:长期知识和决策记录
docs 回答:
为什么系统是这样设计的;
某个架构决策的背景是什么;
历史上踩过哪些坑;
某个模块的长期演进方向是什么。
docs 不是垃圾桶。不能什么都往里面扔。
有一个纪律:每条长期知识都必须有消费方。
也就是说,docs 里的每篇文档,都应该被某个 skill、rule 或角色明确引用。
如果一篇 docs 没有消费方,它就是孤儿知识。它可能写得很好,但没有 Agent 会读它;它变了,也不会触发任何流程变化。时间久了,它会变成“看起来像知识,实际上没人消费”的过期说明。
所以新增 docs 时,我会同时写清楚:
- 这篇文档被哪个 skill 使用;
- 在哪一步使用;
- 不读它会导致什么风险。
知识进入系统,不是因为它被写下来了,而是因为它被某个执行路径消费了。
六、script-backed skill:确定性的事,不要让模型自由发挥
复杂任务里,有些事情适合让模型判断,比如需求拆解、方案权衡、风险总结。
但也有很多事情不适合让模型自由发挥,比如:
校验 JSON schema;
检查文件是否存在;
扫描 forbidden path;
生成目录索引;
运行固定测试命令;
比对契约字段;
检查入口文件是否由模板生成;
收集日志并压缩摘要。
凡是有唯一正确做法的事情,就应该写成脚本。
这类 skill 我叫 script-backed skill。
它的价值不是“让 AI 会写脚本”,而是反过来:用脚本缩小 AI 的自由发挥空间。
比如阶段状态文件必须满足这个结构:
{ "task_id": "string", "phase": "architecture", "status": "pass", "owner": "architect", "evidence": ["docs/adr/0012-contract.md"], "next": "implementation" }不要让模型每次自己想字段名。写一个validate_state.py,所有阶段都用它校验。字段不对就是不通过。
脚本目录一般放在 skill 下面:
skills/feature-delivery/scripts/ validate_state.py check_gate_outputs.py collect_logs.sh我对脚本有几条硬要求:
优先使用标准库和 POSIX shell,少引外部依赖。
所有输入输出格式要写清楚。
写状态文件必须原子写入,避免中途失败留下半截文件。
并发写入要加锁。
命令包装器只能调用配置里声明过的入口,不能让模型临时拼危险命令。
完整日志落盘,主会话只回传摘要。
新增脚本必须配测试。
修改流程脚本必须跑回归。
脚本失败时要返回结构化错误,而不是只吐一屏日志。
脚本不是为了显得“工程化”。它的意义是把可判定的部分钉死。
让模型处理开放问题。
让脚本处理确定问题。
让 gate 判断两者的组合结果能不能进入下一阶段。
这才是稳定的分工。
七、skill 要分层:编排层、阶段层、原子层
只把 skill 拆成很多目录还不够。复杂系统里,skill 之间也要分层。
我一般分三层:
编排层 orchestrator;
阶段层 phase;
原子层 atom。
这三层职责不能混。
(一)编排层:维护状态机,不做具体活
编排层通常只有少数几个 skill。
它负责:
读取任务入口;
建立状态文件;
判断当前阶段;
派发子 Agent;
检查阶段产物;
推进或回退状态;
生成最终收口报告。
它不写业务代码,不做架构细节,不直接补测试。它像流水线控制器,而不是流水线上的工人。
(二)阶段层:负责一个交付阶段
阶段层对应交付链路里的每一段。
比如:
需求分析;
技术设计;
实现;
代码评审;
测试;
联调;
发布准备;
复盘沉淀。
每个阶段都必须有明确的输入、输出和 gate。
阶段最重要的不是“做事”,而是“给出可审计结论”。
我要求每个阶段结束时,都输出四态之一:
pass:通过,有证据,可以进入下一阶段。
blocked:卡住,有原因、有 owner、有回退路径。
not_required:本阶段对当前任务不适用,说明为什么。
risk_accepted:有问题但已被明确接受,必须附接受人、原因和范围。
这四态很重要。
很多系统只有“完成 / 未完成”,不够用。
比如一个模块根本不渲染 UI,那么 UI 还原阶段不应该失败,也不应该假装完成,而应该输出not_required。再比如缺少必要设计稿,UI 实现者不应该凭感觉调到“差不多”,而应该输出blocked,把问题退回上游。再比如测试环境临时不可用,但业务方明确接受本次只做静态检查,那不能写pass,应该写risk_accepted,并记录谁接受了这个风险、接受到什么范围。
gate 的意义,是防止系统把“做过了”伪装成“做对了”。
(三)原子层:只做一件确定的事
原子层是最底下的能力。
它可以是:
解析需求表格;
生成 ADR 编号;
校验状态文件;
收集构建日志;
检查目录写入范围;
扫描 TODO;
比对 API 字段;
提取错误栈。
原子 skill 有几条纪律:
只做一件事;
不编排其他 skill;
不发明命令;
不扩大范围;
输入输出稳定;
最好可由脚本兜底;
可以被多个阶段复用。
原子层最常变,但每次只影响一个点。
阶段层中等频率变化,影响一段流程。
编排层最少变,一变就是全局。
这就是分层的原因。
如果把三层混在一起,一个小脚本变化可能影响整条工作流;一个阶段门禁变化可能被实现者绕过;一个原子能力失败可能让编排层直接失控。
分层之后,每个能力都可以独立路由、独立测试、独立替换。
八、用 manifest 管住整张 skill 图
当 skill 多起来之后,光靠目录结构不够。你需要一个机器可读的清单文件,作为整套 skill 图的单一事实来源。
(一)manifest
我通常会建一个 manifest,比如:
skills: feature-delivery: layer: orchestrator execution: model description: > 适用:复杂业务需求端到端交付。 不适用:单文件小修、纯问答。 典型触发语:实现这个需求、完成这个功能、跨端改造。 inputs: - requirement_doc - scope - acceptance_criteria outputs: - final_report - phase_states tools: - task_dispatch - state_validator risk: high human_review: before_release architecture-design: layer: phase execution: model description: > 适用:需要技术设计、跨端契约、ADR 的任务。 不适用:已有明确设计且只需机械实现的任务。 典型触发语:设计方案、统一契约、ADR。 inputs: - structured_requirement outputs: - adr - contract - architecture_gate risk: medium validate-state: layer: atom execution: script script: skills/feature-delivery/scripts/validate_state.py description: > 适用:校验阶段状态文件 schema。 不适用:判断业务方案是否合理。 典型触发语:校验 state、检查 gate 输出。 inputs: - state_json outputs: - validation_result risk: low(二)三种 edge声明边
除了节点,还要声明边。我会用三种 edge。
1. handoff:阶段流转
表示一个阶段结束后,控制权交给谁。比如:
edges: - type: handoff from: requirement-analysis to: architecture-design condition: requirement_gate.passhandoff 只能由编排者驱动,不能让阶段自己随便跳。
2. dynamic_load:按任务动态加载能力
表示当前任务满足某种条件时,才把某个能力加载进子 Agent 上下文。比如:
- type: dynamic_load from: implementation to: ios-implementation condition: paths.include("ios/**")这可以避免所有子 Agent 一启动就背全量知识。iOS 实现者只读 iOS 相关规则,Android 实现者只读 Android 相关规则。
复杂任务最怕“所有知识都加载”。上下文越多,不代表越聪明,有时只是噪音越多。
3. semantic:证据依赖
表示某一步的输出不能直接当结论,只能作为下游判断的输入。比如:
- type: semantic from: candidate-discovery to: evidence-confirmation meaning: candidates_are_not_evidence这条边很重要。
很多 AI 工作流会把“发现候选”误当成“确认事实”。比如搜索到一个可能相关的文件,就直接改;看到一个相似 API,就当成参考实现;扫到一个关键词,就当成证据。
semantic edge 的作用,是把这种语义关系写清楚:
候选不是证据;
假设不是结论;
设计稿不是实现;
测试通过不是无风险;
日志摘要不是完整日志;
风险记录不是风险接受。
这些话写在人类文档里没用,必须落到流程图里,成为下游 gate 的检查条件。
九、复杂任务 spec 必须设计失败路径
很多 spec 只写 happy path。
需求分析之后做设计,设计之后做实现,实现之后做评审,评审之后做测试,测试之后联调,最后总结。
这不是复杂任务 spec,只是流程图。
真正的复杂任务 spec,一定要写失败路径。
每个阶段都应该回答:
缺输入怎么办;
产物不合格怎么办;
测试跑不起来怎么办;
跨端结果不一致怎么办;
规则冲突怎么办;
子 Agent 超出写入范围怎么办;
需要人工决策时记录什么;
可以继续推进的非致命问题怎么处理;
不能继续时怎么回退。
比如架构阶段 blocked,不能直接进入实现。它应该输出:
{ "status": "blocked", "owner": "product", "reason": "缺少错误态交互定义", "evidence": ["requirements.md#L42-L56"], "rollback_path": "回到 requirement-analysis 补充错误态验收标准", "next": "await_input" }比如测试阶段失败,也不应该只写“测试失败”。应该写:
{ "status": "blocked", "owner": "web-implementation-agent", "reason": "checkout empty state snapshot mismatch", "evidence": ["logs/test-web-2026-06-08.txt"], "rollback_path": "返回 web implementation 修复 UI 状态分支", "next": "implementation:web" }复杂任务能不能稳定跑,不取决于 happy path 多顺,而取决于失败时能不能落到一个明确位置。
失败不可怕。失败后不知道谁负责、证据在哪、下一步去哪,才可怕。
十、Spec 里最容易缺的东西:产物契约
很多团队写 spec 时,会写流程、角色、规则,但漏掉最关键的一件事:每个阶段到底产出什么。
没有产物契约,系统就会变成“看起来做了很多事”,但没有东西可以复查。
需求分析阶段不能只说“理解需求”,要产出结构化需求。
架构阶段不能只说“完成设计”,要产出 ADR 和契约。
实现阶段不能只说“代码已改”,要列出改动路径和对应需求点。
评审阶段不能只说“看起来没问题”,要给检查项和证据。
测试阶段不能只说“跑了测试”,要给命令、结果、日志和未覆盖项。
联调阶段不能只说“验证通过”,要给环境、版本、路径和观察结果。
一个阶段的产物契约至少包括:
文件位置;
格式;
必须字段;
证据要求;
通过条件;
失败时的输出格式。
比如代码评审阶段的输出可以规定为:
review_report.md 必须包含: - scope:审查范围 - changed_files:涉及文件 - checks:安全、性能、规范、契约、测试覆盖 - findings:每个问题的 severity、owner、evidence、suggested_fix - verdict:pass / blocked / risk_accepted有了产物契约,编排者才能检查产物是否存在、字段是否齐全、证据是否足够。否则“完成”只是 Agent 自己的一句话。
复杂 spec 的目标,是把“我觉得做完了”变成“产物满足契约,所以可以进入下一步”。
十一、写复杂 spec 的一个可复用模板
如果把上面的内容压缩成一个落地模板,我会这样写。
1. 任务类型
这个 spec 管什么任务,不管什么任务。
适用: - 多阶段交付任务 - 多角色协作任务 - 跨端或跨模块改造 - 需要产物追踪和门禁验证的任务 不适用: - 单文件机械修改 - 纯问答 - 不需要状态追踪的小修2. 角色和权限
谁能做什么,不能做什么。
orchestrator: - 能:派发、收口、状态推进、门禁检查 - 不能:直接改生产代码 architect: - 能:写 ADR、契约、技术方案 - 不能:写端侧实现代码 platform implementer: - 能:修改对应平台路径 - 不能:修改其他平台路径或公共契约 reviewer: - 能:审查、提出 finding - 不能:直接修复自己发现的问题,除非另行派发为实现者3. 路径到角色映射
docs/adr/** -> architect ios/** -> ios-implementer android/** -> android-implementer web/** -> web-implementer backend/** -> backend-implementer rules/** -> spec-maintainer skills/** -> spec-maintainer4. 阶段状态机
intake -> requirement_analysis -> architecture_design -> implementation -> review -> test -> integration -> final_report每个阶段必须输出:
status: pass | blocked | not_required | risk_accepted owner: evidence: next: reason:5. 门禁规则
没有结构化需求,不得进入架构。 没有统一契约,不得开始跨端实现。 实现者不得修改未授权路径。 评审不得由同一个实现者完成。 测试失败不得标记 pass。 risk_accepted 必须有人类接受记录。6. 知识装载规则
入口只加载索引和硬边界。 阶段 skill 只加载当前阶段 references。 路径 rule 按写入路径自动加载。 角色 rule 按派发角色自动加载。 docs 必须被 skill、rule 或 agent 显式引用。7. 脚本规则
确定性检查必须优先脚本化。 脚本输入输出必须稳定。 状态写入必须原子化。 并发写入必须加锁。 日志必须落盘。 主会话只返回摘要。 脚本新增或修改必须有测试。8. 失败处理
info_gap:记录后继续。 local_failure:记录 owner、evidence、retry path,继续不依赖它的任务。 gate_blocker:停止当前阶段,回退到指定 owner。 risk_decision:请求人工决策。9. 最终报告
最终报告至少包含:
完成了什么;
没完成什么;
哪些阶段通过;
哪些阶段 blocked;
哪些风险被接受;
关键证据位置;
后续动作;
可沉淀成 rule、script、skill 的模式。
这个模板不复杂,但它逼你把复杂任务最关键的边界都说清楚。
十二、记住一句话:复杂 spec 的核心不是“写更长”,而是“拆得更准”
复杂任务的 spec,核心不是写一段更长的提示词。
它是三件资产的组合。
第一件是工作流编排:谁负责推进状态,谁负责执行,什么时候能进入下一阶段,什么时候必须回退。
第二件是 skill 组织:入口要薄,references 按需加载,script 兜住确定性,skill 分成编排层、阶段层、原子层。
第三件是知识库建连:rules 是硬约束,docs 是长期知识,references 是当前 skill 的长内容,每条知识都只有一个权威位置,并且必须有明确消费方。
这三件合起来,复杂任务才从“某次对话里的临场发挥”变成“可以复用、可以审计、可以测试、可以演进的工程系统”。
回头看,复杂 spec 的本质仍然是克制。
不让一个 Agent 什么都做。
不让入口文件什么都写。
不让规则到处复制。
不让知识无消费方。
不让阶段没有门禁。
不让失败没有回退。
不让模型在确定性问题上自由发挥。
每加一个能力,都要说清楚它的位置、职责、输入、输出、规则、证据和失败路径。
这才是复杂任务 spec 的价值。
它不是让 AI 看起来更强。
它是让 AI 的能力被放进一个可控的系统里。
写好 spec,只解决了“组织得清不清楚”。但还有一个更难的问题:每个 skill 到底好不好用。
路由描述写得漂亮,模型就一定会在正确时候召唤它吗?
阶段声称会输出 gate,它真的每次都输出了吗?
references 拆得很细,模型真的会在该读的时候读吗?
script-backed skill 看起来确定,它的失败路径真的稳定吗?
这些不能靠感觉,要靠测试。
下一篇可以继续讲:怎么测一个 skill 到底好不好用。
重点不是让另一个大模型来打分,而是做三件事。
第一,做对照。同一道任务,带 skill 跑一遍,不带 skill 跑一遍,看差异到底是不是 skill 带来的。
第二,存结果。模型每次的真实输出都落成文件,提交进代码库,后续可以复查、比较和回归。
第三,用确定性检查项。不要轻易让另一个模型当裁判。裁判本身也不稳定。能写死的检查项就写死,能脚本化的判断就脚本化。
复杂任务 spec 让系统有结构。
skill eval 让结构真的有效。
这两件事合起来,AI 工程才开始从“能跑一次”走向“能持续演进”。
参考文章
- Anthropic:Building Effective Agents
这篇非常适合作为“workflow 和 agent 要区分、复杂系统优先简单可组合模式”的理论来源。文章明确区分了 predefined workflows 和更自主的 agents。
https://www.anthropic.com/research/building-effective-agents - Claude Code Docs:How Claude remembers your project
适合支撑你文中关于CLAUDE.md、项目记忆、入口文件、规则与工作流说明的部分。官方文档说明了CLAUDE.md用于存放项目指令、编码规范、工作流和架构信息。
How Claude remembers your project - Claude Code Docs - OpenAI Agents SDK:Agents 指南
适合支撑“handoffs、guardrails、human review、observability、state”这些概念。官方文档把复杂 agent workflow 拆成运行、编排、交接、护栏、人类复核、状态和可观测性等模块。
https://developers.openai.com/api/docs/guides/agents - 12-Factor Agents
这篇适合支撑你文中“确定性代码 + LLM 能力分工”“上下文管理”“责任边界”“生产级 Agent 架构”的部分。它强调构建可用于生产环境的 LLM-powered software,而不是只靠 prompt。
GitHub - humanlayer/12-factor-agents: What are the principles we can use to build LLM-powered software that is actually good enough to put in the hands of production customers? · GitHub - DeepWiki:12-Factor Agents 解读
这是对 12-Factor Agents 的结构化解读,适合读者快速理解其中关于 deterministic code、LLM capabilities、context management、responsibility boundaries 的关系。
humanlayer/12-factor-agents | DeepWiki - On the Use of Agentic Coding Manifests: An Empirical Study of Claude Code
这篇论文很适合支撑你文中关于CLAUDE.md / AGENTS.md / manifest的观点。它研究了 242 个仓库中的 253 个 Claude.md 文件,分析 agent manifests 的常见结构和内容。
https://arxiv.org/abs/2509.14744 - Efficient Agents: Building Effective Agents While Reducing Cost
适合支撑“不是 Agent 越多越好”“多 Agent 要有节制”“复杂度要和任务匹配”的观点。论文研究了 agent 系统里的效率—效果权衡,以及模块复杂度的边际收益问题。
[2508.02694] Efficient Agents: Building Effective Agents While Reducing Cost - OpenAI Agents SDK:Tracing
适合支撑你文中“每一步要可追踪、工具调用、handoff、guardrail、custom events 都要能观测”的部分。文档说明 tracing 会记录 agent run 中的 LLM generation、tool call、handoff、guardrail 和自定义事件。
Tracing | OpenAI Agents SDK - OpenAI 教程 - How Do AI Agents Do Human Work? Comparing AI and Human Workflows Across Diverse Occupations
适合支撑“Agent 容易伪造/误用工具、质量需要外部验证、不能只相信模型自述完成”的论点。论文比较了人类和 Agent 在多类工作中的流程差异,并指出 Agent 有时会掩盖质量缺陷。
[2510.22780] How Do AI Agents Do Human Work? Comparing AI and Human Workflows Across Diverse Occupations - Swarm Skills: A Portable, Self-Evolving Multi-Agent System Specification for Coordination Engineering
这篇很贴合你文章里“skill 不只是单 Agent 能力,还可以扩展为多 Agent 协作协议”的方向。论文提出 Swarm Skills,把多 Agent 工作流中的 roles、workflows、execution bounds 和语义结构变成可移植规格。
[2605.10052] Swarm Skills: A Portable, Self-Evolving Multi-Agent System Specification for Coordination Engineering
延伸阅读:
- Anthropic,Building Effective Agents:关于 workflow 与 agent 的工程区分,以及简单可组合模式为何更可靠。
- Claude Code Docs,How Claude remembers your project:关于
CLAUDE.md、项目记忆和入口规则的官方说明。- OpenAI Agents SDK Docs:关于 handoffs、guardrails、human review、state 与 observability 的工程化参考。
- HumanLayer,12-Factor Agents:关于生产级 Agent 系统中确定性代码、上下文管理和责任边界的原则。
- On the Use of Agentic Coding Manifests:关于真实项目中 Claude.md / agent manifest 写法的实证研究。
- Efficient Agents:关于 Agent 系统复杂度、成本和效果权衡的研究。
- Swarm Skills:关于把多 Agent 协作协议规格化、可移植化的研究方向。