Spec:让 AI 在实现前真正理解问题
本文是「企业级应用中 Harness Engineering 的实践与思考」系列的第 4 篇。
为什么不能直接开始开发
通过 workflow,Agent 可以进入一个合适的 plan 或 story;通过文档工程,项目中的记忆和知识也可以在合适的时机进入 Agent 的 Context。看起来,到这里似乎已经万事俱备,下一步就应该让 Agent 开始开发。
但在真实企业级应用中,直接这样做通常会很快出问题。
因为找到正确的工作单元,并不等于已经理解了问题;拿到相关上下文,也不等于已经知道应该如何实现。Agent 仍然可能像做 demo 一样,用自己的方式补全缺失的信息:猜测业务规则,设计没有确认过的交互,忽略隐含边界,跳过异常场景,甚至在看起来合理的方向上快速写出大量代码。
这些代码未必不能运行,也未必一眼看上去就是错的。真正的问题是,它们可能不是业务想要的结果,也不是系统应该接受的变化。
这并不是 AI 时代才出现的问题。软件工程长期以来都在强调一件事:反馈链路越短,问题发现得越早,修复代价越低。
如果一个需求理解偏差在实现前就被发现,可能只是改几句话、补几个边界条件、重新确认一个交互逻辑。但如果等到代码已经写完、测试已经补完、review 已经做完,甚至已经进入验收阶段才发现方向错了,代价就完全不同。那时要改的不再只是理解,而是实现、测试、文档、状态,甚至整个 story 的推进节奏。
整个 Harness Engineering 都不是在发明一套全新的软件工程。它更多是在总结、吸收和裁剪过去已经被反复验证过的经验,再把这些经验重新应用到 AI Agent 身上。这里也一样:不要等到实现完成之后才第一次确认理解是否正确,而要在实现之前,就让 Agent 把自己的理解显式化。
这种实现前的理解确认,真正服务的是人。人需要在 Agent 开始大规模生成代码之前,提前看到 Agent 到底如何理解当前问题:它认为要做什么,不做什么,边界在哪里,风险在哪里,哪些地方还不确定。只有这些理解被显式表达出来,人才能真正驾驭 Agent,而不是在代码生成之后被动验收它的猜测。
因此,在企业级应用中,不应该把第一次理解确认放在实现之后。Agent 进入实现之前,还需要一个明确的分析阶段。
分析阶段的产出物:Spec
分析阶段的目的,不是让 Agent 多停留一会儿,也不是让流程看起来更完整。它要解决的是一个更具体的问题:如何把 story 从"可以开始看"转化成"可以开始做"。
story 给出了工作入口,但它通常还不是完整的问题定义。它可能包含需求、增强点、验收标准和一些上下文,但这些内容仍然需要被解释、拆解和补全。哪些内容是必须做的,哪些只是背景;哪些边界不能越过,哪些异常必须处理;哪些地方存在歧义,哪些问题需要人确认;这些都需要在实现前被整理出来。
更重要的是,这些理解不能只停留在当前 conversation 里。一个 Agent 说"已经理解了",对企业级应用来说是不够的。理解如果只存在于一次 session 中,就无法被多人协作,无法被后续追踪,无法解释为什么这样实现,也无法在状态流转中被管理。
所以,分析阶段必须把理解外化成一个明确的载体。这个载体就是 Spec。
这里需要先说清楚,本文中的 Spec 不是"在开发前写一个 Markdown 文件"。如果一份所谓的 spec 只是把需求重新描述一遍,或者只是把 Agent 准备做什么简单列出来,却不能约束实现、不能支撑 review、不能成为 QA 的验证依据,也不能在实现过程中被反向同步,那么它只是一个文档,并不是这里讨论的 Spec。
不能约束实现、不能接受审查、不能指导验证、不能反向同步的 spec,看起来像 spec,实际上只是实现前的一段说明文字。
在 Harness Engineering 中,Spec 是分析阶段产出的工程化问题定义。它必须把当前 story 的需求、上下文、边界、约束、异常、验收标准、实现影响和未决问题,整理成一个可以驱动开发、支撑审查、指导验证,并且能够随着事实变化被同步更新的共同依据。
因此,Spec 不是额外的文档负担。它是 story 到 code 之间的工程中间产物。没有 Spec,后续实现只能依赖 Agent 的即时理解;有了 Spec,后续实现、review 和 QA 才能围绕同一个问题定义继续工作。
Spec 是共识形成的地方
正因为 Spec 是工程化的问题定义,它也自然成为不同的人和不同的 Agent 对当前问题达成共识的地方。
人和 Agent 在 Spec 上对齐问题
在企业级应用里,"人"的判断从来不只来自一个角色。
一个 story 背后,可能有 PO 对业务目标、功能范围和验收口径的判断;有 UX 对交互方式、视觉细节和可用性的判断;有 SA 对架构边界、系统流程和集成方式的判断;有 DevSecOps 对部署环境、权限、安全、网络和运维限制的判断;也可能有测试、支持、历史维护者和真实业务方对异常场景、历史问题和实际使用方式的补充。
这些判断通常散落在不同地方:原型、设计稿、架构文档、会议结论、聊天记录、历史 issue、review 记录,甚至某个人的经验里。它们都很重要,但不会天然进入 Agent 的当前 Context,更不会自动变成 Agent 可以执行的任务边界。
Spec 的作用,就是把这些与当前 story 相关的人的判断聚合起来,转化成 Agent 能够理解、执行和验证的问题定义。
也就是说,Spec 不是把人的判断替换掉,而是把人的判断组织起来,向 Agent 展开。人不需要把所有背景都压进一次 prompt,也不需要等 Agent 写完代码之后再发现它误解了业务、交互、架构或基础设施限制。人的判断应该在实现前就被整理进 Spec,让 Agent 在正确的问题边界内开始工作。
人在分析型 Agent 辅助下完成 Spec
Spec 的形成过程,并不是把各种判断简单复制到一个文件里。真正困难的地方在于,这些判断来自不同角色、不同文档和不同历史上下文,它们之间可能有缺口,也可能存在冲突。
在这套实践中,完成 Spec 的人不是旁观者,而是 Human in the Loop:它在 BA Agent 和 TL Agent 的辅助下负责理解、判断和取舍。
BA Agent 更关注业务理解。它需要从 PO、业务方、原型、历史 story、验收标准和实际使用场景中,整理出当前 story 真正要解决的问题:业务目标是什么,用户路径是什么,哪些规则必须遵守,哪些异常场景不能遗漏,哪些表达仍然有歧义。
TL Agent 更关注技术理解。它需要从架构文档、现有代码、系统边界、依赖关系、技术约定和 DevSecOps 限制中,整理出当前 story 在工程上的约束:应该影响哪些模块,不能破坏哪些边界,需要遵守哪些接口和状态流转,哪些实现路径风险更高。
这个过程不是简单的几轮对话。它会伴随大量来回澄清、文档阅读、代码检索、网络搜索、推理、讨论和取舍。Agent 需要不断把发现的问题带回来,人也需要不断判断哪些信息成立,哪些假设需要推翻,哪些边界必须重新确认。
这也是人在 Harness Engineering 范式下最消耗精力的工作之一。因为这里处理的不是代码细节,而是方向、范围、语义和约束。一旦这里判断错了,或者遗漏了关键场景和边界,后面的实现越快,错误就会被带得越远。
更麻烦的是,这部分没有任何 Agent 能真正替人承担。BA Agent 和 TL Agent 可以辅助阅读、整理、追问和推演,但它们不能替人决定业务目标是否正确,不能替人判断取舍是否合理,也不能替人承担最终责任。这个阶段的负担不会因为有 Agent 辅助而消失,它依然依赖人的经验、判断和注意力。
人在这个过程中负责判断和取舍。Agent 可以发现问题、整理材料、提出假设和生成候选方案,但当业务目标和技术约束发生冲突时,当范围需要收缩或扩大时,当验收标准需要重新定义时,最终决定仍然需要由人做出。
也正因为这个阶段的目标是理解问题,而不是修改实现,所以所有 Agent 都不应该修改任何实现代码。它们可以阅读代码、检索文档、运行必要的只读检查、提出问题和更新 Spec,但不能提前进入实现。否则,分析阶段就会退化成另一种边想边写的开发过程。
所以,Spec 不是某个 Agent 独立写出来的结果,而是人在分析型 Agent 帮助下完成的理解固化过程。它把散落在组织、文档、历史和代码里的判断,收束成当前 story 可以依赖的问题定义。
Spec 写完之后仍然需要 Review
Spec 写完之后,并不意味着 Agent 就可以立刻开始开发。因为 Spec 本身也可能有问题:它可能遗漏场景,保留了模糊表达,内部存在冲突,验收标准不可验证,或者技术约束和业务目标之间还没有真正对齐。
所以,Spec 也需要 review。
Spec Reviewer Agent 要检查的是问题定义本身是否成立。它关注的不是代码质量,而是 Spec 是否清晰、完整、一致、可实现、可验证:有没有歧义没有被消除,有没有关键边界被遗漏,有没有前后矛盾的要求,有没有无法验证的验收标准,有没有和现有系统边界、技术约定或上下文冲突的地方。
人的 review 则负责最终判断。Spec Reviewer Agent 可以指出风险、提出问题和建议修改,但业务目标是否正确、范围是否合理、取舍是否可以接受、验收标准是否足以代表完成,这些仍然需要人确认。
只有当 Spec 通过 review,当前 story 才真正具备进入实现的条件。换句话说,analyzing → implementing 这个 human gate,本质上是人对分析阶段的最终确认。它确认的不是"Agent 已经写完了一份文档",而是"当前问题已经被理解到足以开始实现";这个确认的载体,就是经过 review 的 Spec。
Dev、QA 和 Code Reviewer 以 Spec 为依据
Spec 通过 review 之后,story 才真正进入实现阶段。这个时候,Spec 的角色也发生了变化:它不再只是分析阶段的产物,而是后续 Dev、QA 和 Code Reviewer 共同使用的事实源。
有意思的是,它们读的是同一个 Spec,但看到的不是同一种东西。
Dev Agent 从 Spec 中看到的是实现任务:需要修改哪些模块,实现哪些行为,遵守哪些边界,处理哪些异常,避免哪些副作用。
QA Agent 从 Spec 中看到的是验证标准:哪些用户路径必须覆盖,哪些边界条件必须验证,哪些异常场景必须测试,什么结果才算真正完成。
Code Reviewer 从 Spec 中看到的是代码要求:实现是否偏离问题定义,是否遗漏了 Spec 中的条件,是否越过了范围边界,是否引入了不必要的复杂度或风险。
这正是 Spec 作为共识载体的价值。它不是给某一个角色看的文档,而是让不同 Agent 围绕同一个问题定义展开工作。每个 Agent 都可以从自己的职责出发读取 Spec,但不能脱离 Spec 自行解释任务。
没有 Spec 时,多 Agent 协作很容易变成多套临时理解的叠加:Dev 按自己的理解实现,QA 按自己的理解验证,Code Reviewer 按自己的理解审查。每个环节都可能看起来合理,但它们未必在回答同一个问题。
Spec 让多 Agent 协作不再依赖"各自理解",而是依赖同一个被确认的问题定义。
Spec 是 AI 时代新的协作界面
到这里可以看到,Spec 连接了分析阶段和实现阶段。
对于分析阶段来说,Spec 是输出。人、BA Agent、TL Agent 和 Spec Reviewer Agent 围绕它澄清问题、整理约束、确认边界,并最终判断 story 是否具备进入实现的条件。
对于实现阶段来说,Spec 又是输入。Dev Agent、QA Agent 和 Code Reviewer 从同一份 Spec 中读取不同的职责,把同一个问题定义分别转化成代码、测试和审查。
这让 Spec 成为 AI 时代新的协作界面。
在过去的软件工程中,代码是人和机器共同工作的核心界面。人把意图写成代码,编译器、运行时、测试系统通过代码理解和执行这些意图。代码既是人类协作的对象,也是机器执行的对象。
但当 AI Agent 开始参与开发时,这个界面正在前移。人不再需要把所有实现细节都亲自落实到代码里,而是更多在 Spec 层面表达目标、边界、约束、验收标准和取舍。Agent 则基于 Spec 继续生成代码、补充测试、执行 review,并反馈实现过程中发现的问题。
所以,对人来说,Spec 越来越像进入实现之前最重要的工作界面。很多过去必须在代码中表达、在 review 中纠正、在测试中暴露的问题,现在都应该尽可能前移到 Spec 中被讨论和确认。
这并不意味着人不再需要关心实现结果。人仍然需要阅读报告、验证功能行为,并对最终结果负责。但人的主要创造性输入,应该尽可能在 Spec 处完成。
Spec-Driven Development 的三条开发纪律
当 Spec 成为分析阶段的输出、实现阶段的输入,开发方式也随之发生变化:实现不再应该由 Agent 的即时理解驱动,而应该由经过 review 的 Spec 驱动,这就是这里讨论的 Spec-Driven Development。
这里的 Spec-Driven Development 不是一种文档仪式,而是一组开发纪律:代码从 Spec 中来,代码与 Spec 冲突时以 Spec 为准,对代码方向的调整也必须先回到 Spec。
这些纪律不只是给 Agent 的,更要约束人。让 Agent 遵守规则相对简单,真正困难的是让人也不要绕过 Spec 直接改变代码。只要有人这样做,Spec 就不再是事实源。
No Spec, No Code.
没有经过确认的 Spec,就不能写代码。任何代码都必须能回到 Spec 中解释:为什么要写,解决什么问题,满足哪个验收标准,遵守哪个边界。否则,代码就失去了可解释、可验证、可追踪、可管理的来源。
Spec is Truth.
当代码和 Spec 冲突时,以 Spec 为准。不是因为 Spec 天然不会错,而是因为 Spec 是被确认的事实源。如果实现偏离 Spec,优先修改代码,让它回到问题定义上。
Reverse Sync.
如果实现过程中发现代码不应该按原来的 Spec 继续写下去,不能绕过 Spec 直接改代码。正确做法是先反向同步 Spec:把新事实、新约束、新边界、新取舍更新进去,重新确认后再修改实现。
这里最容易被忽略的是,人也会有直接修改代码的冲动。看到实现不对、边界漏了、交互细节不合理,很自然会想直接改代码,尤其是修改看起来很小的时候。但如果这个修改改变了问题定义、验收标准、边界或实现约束,却没有同步回 Spec,那么 Spec-Driven Development 的纪律就被破坏了。
代码也许暂时变对了,但它已经脱离了被确认的问题定义。之后别人无法解释这段代码为什么存在,QA 无法知道应该如何验证,Code Reviewer 无法判断它是否越界,后续 Agent 也无法追踪这个变化从哪里来。项目的可解释、可验证、可追踪、可管理都会被伤害。
所以,这三条纪律形成的是一条完整链路:No Spec, No Code 保证代码有来源;Spec is Truth 保证代码有对齐对象;Reverse Sync 保证 Spec 不会因为现实变化而变成假权威。
好的 Spec 应该满足什么要求
如果 Spec 是 Spec-Driven Development 的事实源,那么 Spec 本身就必须足够可靠。否则,Spec-Driven Development 只会把错误稳定地传递到实现、review 和 QA 中。
清晰。
Spec 不能有含混表达。像"优化体验""支持权限""适配样式"这样的描述都不够。它必须说明具体行为、边界和结果:什么场景下发生什么,用户看到什么,系统如何响应,什么状态算完成。
细节足够。
Spec 必须细到 Dev 和 QA 不需要再猜。前端项目的 Spec 不能只说"组件要大一点",而要说明具体尺寸、间距、颜色、状态、响应式行为,必要时甚至要给到 CSS 细节。后端项目的 Spec 也不能只说"返回成功",而要说明成功时的 HTTP status code 是 200、201 还是 204,response body 的 schema 是什么,错误时返回什么结构。只要某个细节会影响实现或验证,它就应该进入 Spec。
完整。
Spec 不能只描述 happy path。关键场景、边界条件、异常路径、权限分支、数据状态都需要被覆盖。在企业级应用里,遗漏一个关键分支,往往比写错一个字段更危险。
粒度合适。
不是每一个变化都需要被写成完整 requirement。一个简单 rename、文案修正或小范围 refactor,可能只是 story 里的一个 enhancement 或 task。反过来,一个跨模块、跨角色、跨状态流转的变化,也不能被压缩成一句"支持某功能"。Spec 的粒度必须和工作风险、验证复杂度、协作成本相匹配。
经过压缩和筛选。
分析阶段可能产生大量材料:对话、检索结果、代码阅读记录、方案比较、截图、设计细节、历史背景。但这些内容不都适合直接给 Dev、QA 或 Code Reviewer 使用。好的 Spec 不是分析过程的完整记录,而是分析结果的工程化表达。它应该把分析材料压缩成可执行的问题定义,把噪音留在 conversation、附录或原始记录里。
原始资料不能直接等于 Spec。
Figma、OpenAPI、数据库 DML、架构图、接口文档、历史 issue,都可以是 Spec 的输入,但不能直接替代 Spec。因为 Dev 和 QA 需要知道的不是"资料本身存在",而是这些资料对当前 story 意味着什么:哪些界面细节必须实现,哪些接口字段必须使用,哪些数据库约束会影响行为,哪些验收标准由此产生。
原始资料是证据,不是问题定义。
可实现。
Spec 必须能落到具体工程动作上。它不能只描述愿景,也不能只描述业务目标。Dev Agent 读完之后,至少应该知道大致会影响哪些模块,受到哪些约束,有哪些实现风险,哪些路径不能走。
可验证。
Spec 必须能够形成验收和测试依据。QA Agent 读完之后,应该知道如何证明当前 story 已经完成:哪些用户路径必须覆盖,哪些数据状态必须构造,哪些权限和异常必须验证,哪些结果可以判定为通过。
有边界。
Spec 不只要说明做什么,也要说明不做什么。AI Agent 很容易在看似合理的方向上顺手补全,而边界就是防止自我设计的关键。一个好的 Spec 应该让 Agent 知道哪里应该停下来。
有上下文。
Spec 需要解释为什么这样做。它不需要塞进所有历史,但需要保留足够的背景,让后续人和 Agent 知道这个判断从哪里来、为什么成立、哪些取舍已经被确认。
回到 Harness:Spec 是协作的交汇处
回到 Harness Engineering 的视角,Spec 的位置非常特殊。
更准确地说,Spec 是多条线在 Harness 中交汇的地方:记忆和知识在这里被压缩成当前 story 的问题定义;人和 Agent 在这里完成对齐;需求在这里走向实现;分析在这里交给开发。
它不是一个孤立的文档,也不是开发前的一道手续。业务目标、用户场景、设计约束、架构边界、历史决策、系统规则、异常经验、技术限制,都需要在当前 story 的语境下被重新组织,进入 Spec。
原始需求通常带着模糊、遗漏和歧义;分析阶段的工作,就是通过阅读、检索、讨论、澄清、review 和人的最终判断,把这些不确定逐步压缩成可以实现、可以验证、可以管理的问题定义。
同时,Spec 也是人和 Agent 共同工作的界面。人在这里表达方向、边界、取舍和验收标准;Agent 在这里读取任务、拆解职责、进入实现、生成测试、接受审查。Spec 既是人的工作界面,也是 Agent 的工作界面。
在 Spec-Driven Development 的纪律下,代码从这里出发。代码不应该来自 Agent 的即时猜测,也不应该来自人绕过 Spec 的临时修改,而应该来自一份经过分析、review 和确认的问题定义。
因此,在企业级应用中,让 AI 真正开始实现之前,最重要的事情不是让它更快写代码,而是先让它真正理解问题。