上一篇清晰界定了 Harness 与业务工程的边界:Harness 属于平台层,业务工程构建于其上,并不直接修改平台代码。文章结尾留下了一个关键问题:业务侧的 Spec 究竟应该包含哪些内容?
本篇将正面回答这个问题。对于简单任务,只需将需求用一句话抛给模型即可。当任务复杂到需要多角色协作、跨越多个阶段、并要求每一步都可追溯时,Spec 就不再是一段简单的提示词,而是一套结构严谨的工程资产。我们将从四个维度深入拆解:如何落地多 Agent 架构、编排者入口文件应编写什么、Rules/Docs/Skills 如何进行合理分工,以及 Skill 如何进行层次划分。
一、多 Agent:一个编排者 + 一队专职执行者
面对复杂任务,首要原则是:不推荐试图让一个 Agent 从头到尾独立完成。
上下文会迅速膨胀,职能边界会逐渐模糊。如果一个 Agent 同时承担需求分析、代码编写与评审工作,它自己最终也无法分辨当前的身份以及应遵循哪些规则。业界实践中,一项成熟的经验是:使用一个编排者带领一组专职执行者。
编排者,即入口 Agent,其核心职责是进行工作编排与最终收口。它判断当前处于哪个阶段、门禁是否通过、任务应分配给哪个执行者、以及是否需要继续推进。编排者不会亲自编写业务代码。具体任务将派发给专职的子 Agent,每个子 Agent 在新的上下文中启动,只加载本次任务所需的知识。
执行者依据职责划分角色,常用的角色包括:
- 需求分析师:将需求文档与范围拆解为结构化的输入信息
- 架构师:负责技术设计、制定跨端统一契约、编写 ADR,不涉及具体实现代码
- 各端实现者:按平台划分,每端一个,只专注于自身平台代码
- 代码评审者:从安全性、性能、编码规范、参考实现对齐等多个维度进行核验
- 测试工程师:补充测试用例,编写预期失败的验收测试
- 联调工程师:执行端到端的构建、安装、运行、日志收集与跨端一致性验证
- 效率工程师:将重复出现的模式沉淀为可复用的工程资产
角色划分有一个容易被忽视的关键作用:选错角色,不仅仅是分工不当,更会导致该角色所绑定的规则约束被绕过。 例如,各端实现者会自动加载对应平台的硬性约束清单,而架构师则不会。如果错误地将本应由实现者处理的 UI 任务派给了架构师,那么实现层的全套检查就会失效。许多团队都曾因一次误操作将端上 UI 实现误派给架构师,导致其绕过了多项关卡,视觉对齐问题因此重现。
因此,任务派发不能依赖直觉。规则是:先基于阶段选择角色,再通过“改动文件路径 → 角色”的映射表来校验写入范围。一旦出现冲突,必须暂停并明确说明,绝不能擅自扩大某个 Agent 的写入权限。此外,跨端的改动禁止由一个 Agent 全权负责,必须首先由架构师产出统一契约,然后让各端实现者在隔离的写入范围内并行工作。
并行也需要合理控制。同一任务主线最多挂载 5 个子 Agent,常态并行数为 4 个,第 5 个作为备用。每个子 Agent 在派出时需明确声明:交付什么产物、采用何种格式、写入位置、以及验收条件。完成后先核验产物是否存在且合格,再关闭该 Agent 释放名额。如果没有这套配额机制与产物核验,多 Agent 协作会很快演变为一组失控的并发进程。
二、编排者入口(CLAUDE.md / AGENTS.md)写什么
编排者的行为,很大程度上由它的入口文件(即 CLAUDE.md 或 AGENTS.md)所决定。这个文件编写的好坏,直接决定了整个协作体系是有序还是混乱。
首要原则是:入口文件只存放启动索引与硬性边界,不包含具体执行步骤。
许多人会将所有规则与流程都塞入 CLAUDE.md,导致文件篇幅长达成千上万行,模型每次启动都需完整阅读一遍,既影响效率,又难以抓住重点。正确的做法是让入口文件保持精简:它仅需告知模型“任务到达后先判断类型,然后去读取哪个特定文件”。具体步骤放入 Skill,硬性规则归入 Rule,角色契约归入 Agent,背景与决策记录放入 Docs。入口文件本身只是一张导航地图。
第二点是将编排者的契约固化。入口文件中应包含一段“入口 Agent 契约”,核心内容如下:
- 它是编排者与收口者,而非具体执行者
- 只负责判断阶段、门禁状态、任务交接、是否继续推进、以及任务派发给谁
- 对于小型机械性修订、索引更新、运行校验等工作可自行处理,其余任务默认派发给子 Agent
- 不允许直接修改生产代码,亦不能绕过门禁、评审与校验流程
- 遇到非致命性问题时,首先记录负责人、路径、相关证据及后续步骤,而不是立即暂停并询问
- 只有在发生致命错误,或当前门禁明确被卡住时,才允许暂停
最后两点尤为重要。在复杂工作流中,最怕的是 Agent 一遇到小问题就停下来等待决策,十分钟内可能询问多次。将“什么情况下记录后继续,什么情况下才允许暂停”写入契约,它才能顺利跑完整条任务链路。同理,子 Agent 的派发是编排者的内部操作,不应每次回头询问“能否派下一个”——只有当任务范围扩大、涉及破坏性操作、需要风险接受或外部发布时,才需回到你这里决策。
还有一个常见陷阱需特别提醒:切勿直接编辑生成的入口文件。 入口的真正权威来源是一个模板文件,CLAUDE.md 和 AGENTS.md 都是通过工具从该模板投影生成的。直接修改投影文件,下次同步时便会被覆盖。在多 Agent 协作中,这一点很容易被忽视——你以为修改了规则,实际上只改动了一个一次性产物。
三、rules / docs / skills 怎么组织
Spec 不仅仅是流程,它还涉及“知识、约束、步骤分别存放于何处,以及如何相互引用”。我们通过三类目录来回答三个不同的问题:
- skills:记录如何做(具体步骤和流程)
- rules:规定必须遵守什么(硬性约束、检查清单)
- docs:解释为什么是现在这样(架构背景、长期决策记录)
这种分类方法的核心优势在于,同一条知识只有一个权威存储位置。流程变化时修改 Skill,约束变化时修改 Rule,要追溯某个设计决策的原因则查阅 Docs,从而避免出现多处重复且互相矛盾的说明。
一个 skill 的文件结构长什么样
每个 Skill 是一个目录,其标准结构非常简洁:
某个skill/SKILL.md 入口:路由 + 职责 + 边界 + 输出契约references/按需加载的长内容:方法论、模板、检查段scripts/ 确定性脚本(只有 script-backed skill 才有)
测试样例不放在 Skill 目录内,而是放在一个平行的 evals 目录下,按 Skill 名称一一对应(下一篇将详述)。
SKILL.md 本身也采用固定的骨架,每个段落都非常简短:
- frontmatter:包括名称、用于路由的描述(必须包含“适用 / 不适用 / 典型触发语”)、层级信息、风险等级、是否需要人工复核
- 职责边界:清晰定义该 Skill 负责什么
- 适用与不适用:明确在什么情况下应调用,什么情况下不应调用
- 工作方式:提供最小工作流程,同时注明“何时该去读取哪个 reference 文件”
- 按需加载 references:列出一张清单,将每个场景对应的 reference 文件列出
- 输出与验证:定义交付内容以及如何判定通过
关键原则只有一条:SKILL.md 要精简。 它是为模型快速判断“是否使用我、如何使用我”而设计的路由卡,而非知识库。如果将平台细节、反模式案例、长模板全部堆砌进去,每次调用时模型都需要阅读一篇长文,反而会淹没路由判断的核心信息。
skill 怎么引用 rule、docs 和知识库
Skill 正文精简,那么真正的长知识存放在哪里,以及如何被利用?通过三种不同的引用方式,各自管理一类内容。
References 是 Skill 自身的长内容,可按需按名加载。 方法论和模板不写入 SKILL.md,而是拆分为 references 下的独立文件。在工作方式部分写明“这一步去读哪个 reference”。模型仅在真正走到该步骤时才将其读入上下文,平时不占用资源。
Rule 不在 skill 正文中重复,而是通过路径和角色自动挂载。 一种是目录级规则:每份规则声明它管辖哪些路径,Agent 一进入该路径,规则就会自动加载;另一种是角色绑定:某个角色被派出时,对应的检查清单会自动跟随加载。最硬性的几项约束则不与路径关联,启动即常驻内存。这样,规则的权威来源只有一份,Skill 和角色都只是“引用”它,而非各自复制一份。
Docs 是被消费的知识库,引用关系必须显式声明。 Docs 存放长期知识和决策记录,Skill 在需要背景信息时再去读取。这里有一条特别重要的纪律:docs 中的每一条长期知识,都应该有一个明确的消费方(某个 skill、rule 或角色)进行引用。 没有消费方的知识就是孤儿知识——写下来无人阅读,修改后也无人知晓,久而久之便与实际脱节。新增一条 docs 知识时,顺手补充谁会使用它,这条知识才算真正进入系统,而非堆积在一个无人问津的文件夹里。
三者共同构成一张引用网络:skill 正文精简、按需点读自己的 references;被动挂载由路径和角色绑定的 rule;主动去 docs 获取背景信息。而每条知识都只有一个权威存储位置。
script 怎么组织
scripts 目录存放确定性工具:校验器、命令包装器、流程辅助脚本。其存在意义在于缩小 Agent 自由发挥的空间。凡是存在唯一正确做法的事务,就不应让模型每次现场发挥,而是编写脚本将其固化。
对脚本有几项硬性要求:优先使用标准库和 POSIX shell,减少外部依赖;写入状态文件时必须采用原子写入操作,并发场景下要加锁;命令包装器只调用配置中声明过的入口函数,完整日志需落盘,主会话只回传摘要。还有一条容易疏忽的规则:新脚本必须配备测试,修改流程脚本后应运行回归测试,不能只通过语法检查。脚本的作用是兜住确定性,如果自身不可靠,就失去了其意义。
四、skill 怎么分层,为什么分层
最后一层组织,是 skill 内部的层次划分。
Skill 分为三层,职责严格互不重叠:
- 编排层(orchestrator):通常只有一个,负责维护状态机、派发控制权,不执行具体任务
- 阶段层(phase):对应交付链路的每一段,例如需求、设计、开发、评审、测试、发布,每段管理自己的输入输出和门禁
- 原子层(atom):最底层的单一确定性能力,每个原子只做一件事,不编排其他 skill,也不发明命令
阶段层有一个关键设计称为 gate。每个阶段结束时并非简单标记“完成”,而是给出一个四态结论:pass(通过)、blocked(卡住,需附上负责人和回退路径)、not_required(本阶段对该模块不适用)、risk_accepted(存在问题但已被显式接受)。例如,某个模块根本不涉及界面渲染,其 UI 还原阶段直接标注 not_required 放行;但如果缺少必要的设计参考,模型不会硬着头皮凭感觉调整,而是标注 blocked 退回上游补齐。在没有证据的情况下,宁可卡住也不要假装完成。
仅仅在脑海中分层是不够的,需要将其转化为机器可读、可校验的内容。整个架构图落成一个清单文件,作为所有 skill 的单一事实来源。每个节点声明固定字段:所属层级、执行方式(背后挂脚本还是由模型驱动)、用于路由的描述(必须包含“适用 / 不适用 / 典型触发语”)、输入输出、可用工具、风险等级、是否需要人工复核。
节点之间通过三种边进行连接:
- handoff:阶段之间的流转,由编排者驱动
- dynamic_load:根据当前任务类型,在子 Agent 的新上下文中动态加载对应能力,按需加载,不一次性全量加载
- semantic:证据依赖,例如“发现候选”这一步只产出候选地图,真正的定点证据必须由下游证据 skill 收敛,不能看到候选就直接当作结论
那么,为什么要强制分层? 因为这些事务的变化频率与失败模式完全不同。编排逻辑极少变化,一旦改变就是全局性的;阶段契约以中等频率变化,修改的是某一段的输入输出;原子能力变化最频繁,但每次只涉及一个点。如果混为一谈,就无法只修改一层而不担心其他层出现问题。分层之后,每个能力都能被独立路由、独立测试、独立修改,而不会悄悄影响到其他部分。
记住一句话
回顾本篇内容,正好对应着这三项核心资产。
工作流编排涵盖前两节:多 Agent 架构决定了任务分配给谁、各自遵守什么规则;编排者入口决定了从何处进入、谁拥有决策权、以及如何记录问题并一气呵成地执行,而非频繁中断等待。
Skill 组织贯穿第三、四节:一个 skill 的文件结构(精简入口 + 按需加载的长内容 + 确定性脚本),以及整套 skill 如何划分为编排、阶段、原子三层,确保即使能力不断增加也不会造成混乱。
知识库建连体现在第三节所描述的引用网络:rule 通过路径和角色自动挂载,docs 中的每条知识都必须有消费方,skill 仅按需读取自己的 references。一条知识只有一个权威位置,修改一处即可全局生效,不会出现多处互相矛盾的副本。
这三项资产结合起来,使得“AI 到底在做什么”变得可追踪:每一步应该由谁执行、应遵守什么规则、应产出什么证据、在卡住时退往何处,都明确记载于 spec 之中,而非隐藏在某个临场对话里。
这与第一篇的主题一脉相承——克制。不让能力随意增长,每增加一个能力都必须清楚说明其位置与契约。
然而,写好 spec 仅仅解决了“组织是否清晰”的问题,尚未解决“每个 skill 是否真正好用”。一段路由描述即使写得再漂亮,模型是否一定能准确地在恰当的时机调用它?一个阶段声称会输出门禁结论,是否每次都能稳定输出?这些都需要通过测试来验证。
下一篇将探讨如何测试一个 skill 的好坏,核心围绕三件事。
首先,如何进行对照测试。针对同一道题目,分别在有该 skill 和无该 skill 的条件下运行模型,对比两次输出的差异。只有这样,才能准确判断好的结果是 skill 带来的,还是通用规则本身就能实现的,避免误判功劳归属。
其次,如何保存测试结果。模型每次的真实回答都需作为文件存储并提交到代码库中,方便任何人随时复查,而不是运行完毕后立即遗忘,下次运行又是一套全新的结果。
第三,为什么我不采用另一个大模型作为评分器。让 AI 充当裁判听起来省事,但裁判自身也不稳定,今天打 8 分明天可能只打 6 分。我宁愿使用一组固化的、对错一目了然的检查项来进行判定。
