# Spec-Driven Development 与 Agent 工程化工作流

## 概述

当 AI agent 成为软件开发的核心参与者，一个根本性问题浮现：**我们应该用什么来驱动 agent 的开发行为？** 传统的答案是代码本身（agent 读代码、写代码、改代码），但越来越多的实践者认为，**规范（specification）**才应该是主要产物——代码只是规范的派生物。

这一思路并非全新。Model-Driven Development (MDD) 在二十年前就提出了类似的愿景，但最终因僵化和开销而失败。LLM 的出现减少了 MDD 的一些限制（如灵活性），却引入了非确定性——这可能导致"两个世界最差的组合" [^24]。

本文综合 Thoughtworks 的 Spec-Driven Development 工具评估、HumanLayer 的 12-Factor Agents 原则及其运维视角，试图回答：如何在吸取历史教训的同时，构建真正可行的 agent 工程化工作流？核心发现是——**成功的 agent 工作流不是"全自动的 spec-to-code 管道"，而是"确定性代码 + 战略性 LLM 决策 + 系统化的知识积累"的三角结构**。

## Spec-Driven Development 的三个层次

Birgitta Böckeler 识别了 SDD 的三个递进层级 [^24]，每个层级代表了规范在开发流程中不同程度的持久化和权威性：

**Spec-first**：规范先于代码编写，指导 agent 的实现过程，但在实现完成后可能被丢弃。这是最低限度的 SDD——本质上是"先写设计文档再编码"的 AI 时代版本。Kiro 的线性工作流（Requirements → Design → Tasks）和 Spec-kit 的实际行为都停留在这一层级 [^24]。

**Spec-anchored**：规范持续存在，不仅指导初始实现，还用于后续功能演进和维护。规范成为"活文档"，与代码保持同步。这是大多数 SDD 工具声称要达到但实际很难实现的层级。

**Spec-as-source**：规范成为主要可编辑产物，代码从规范自动生成。人类主要编辑规范而非代码。目前只有 Tessl 明确追求这一层级，使用 `@generate` 和 `@test` 标签在规范与代码之间维持双向同步 [^24]。

Böckeler 的批判性观察极为重要：**当前 SDD 工具可能放大而非解决问题** [^24]。Spec-kit 生成大量重复的 markdown 文件，导致审查规范比审查代码更费力——这与 SDD 的初衷背道而驰。Agent 频繁忽略或过度解读规范中的指令，产生重复代码和规范违规。简单的 bug 修复被迫走完整的 spec 流程，造成不必要的开销。

这些问题指向一个核心张力：**SDD 工作流必须能适应不同规模的任务**。不是所有任务都值得完整的"需求 → 设计 → 任务分解 → 实现"流程。Claude Code 的最佳实践 [^23] 建议"对范围清晰的小任务可以跳过规划"，这种务实态度值得借鉴。

## 12-Factor Agents 核心原则

Dex Horthy 的 12-Factor Agents [^25] 提供了一套与 SDD 互补的工程化原则。如果说 SDD 关注的是"用什么驱动开发"，12-Factor Agents 关注的是"如何构建 agent 本身"。其核心主张颠覆了当前的主流范式：**有效的 agent 应该是"大部分确定性代码 + 战略性 LLM 决策"的组合，而非纯粹的"prompt + tools + loop"** [^25]。

十二条原则中，以下几条对工作流设计影响最大：

**Own Your Prompts（Factor 2）**：将 prompt 视为**一等公民代码**，而非依赖 framework 抽象 [^25]。这意味着 prompt 应该有版本控制、代码审查、测试和部署流程——与应用代码同等对待。这与 SDD 的"规范即主要产物"理念高度一致：无论你选择 spec 还是 prompt 作为驱动 agent 的主要媒介，它们都必须被当作一等公民来管理。

**Context Engineering（Factor 3）**：一切皆为 context engineering [^25]。不使用标准消息格式，而是构建自定义的 context 结构，优化 token 效率和 LLM 性能。这直接呼应了 Claude Code 最佳实践中将 context window 管理作为第一性原则的理念 [^23]——context window 是最稀缺的资源，它的高效利用决定了 agent 的能力上限。

**Small, Focused Agents（Factor 10）**：构建处理 3-20 步工作流的微 agent，而非单体系统 [^25]。随着 context 增长，LLM 更容易迷失或失去焦点。这是对"全能 agent"幻想的务实纠正。

**Own Your Control Flow（Factor 8）**：构建自定义控制结构，决定何时循环、暂停或升级 [^25]。允许在 tool 选择和执行之间插入中断——对审批工作流和安全检查至关重要。

**Agent as Stateless Reducer（Factor 12）**：将 agent 设计为纯函数——给定相同的输入状态和事件序列，产生相同的输出 [^25]。这确保了可预测性、可测试性和可重放性。

## 12-Factor AgentOps 运维框架

从运维视角审视 12-Factor Agents [^26]，揭示了 agent 系统在生产环境中的独特挑战：

**状态管理与可恢复性**是 AgentOps 的核心 [^26]。Factor 5（统一状态）和 Factor 6（Launch/Pause/Resume）共同解决了一个关键问题：agent 在执行长时间任务时可能因各种原因中断（外部系统故障、LLM 服务波动、需要等待人类审批）。将执行元数据与业务状态合并为单一事件流，支持在任意检查点暂停和恢复，这类似于传统微服务中的 event sourcing 模式 [^26]。

**可观测性与调试**要求全链路 tracing——记录每一步的输入 context、LLM 输出和 tool 执行结果 [^26]。Stateless Reducer 模式（Factor 12）使得 agent 行为可重放，对生产环境的 debugging 和 incident response 至关重要。

**自我修复（Self-healing）**通过将错误信息纳入 context window 实现（Factor 9）[^25] [^26]。LLM 可以读取错误并调整后续调用。但必须设置重试上限防止无限错误循环——一个看似简单但容易被忽视的 guardrail。

**人类审批的运维集成**需要建立 SLA、明确升级路径和超时处理策略 [^26]。当 agent 通过 tool call 请求人类输入（Factor 7）时，运维系统需要支持 Slack、邮件等多通道的异步审批通知。这不是一个简单的"加个确认按钮"的问题，而是一个完整的运维工作流设计。

AgentOps 与传统 DevOps 的本质差异在于：除了代码部署和基础设施管理，还需要额外关注 **prompt 版本管理、context window 优化、LLM 行为的不确定性管理以及人机协作工作流** [^26]。运维团队需要新的工具和思维模式。

## 微 Agent 模式 vs 单体 Agent

12-Factor Agents 中"Small, Focused Agents"（Factor 10）的原则与 SDD 工具的实践形成了有趣的对照。

SDD 工具（尤其是 Kiro 和 Spec-kit）倾向于构建**单体工作流**——一个 agent 从需求分析一直做到代码实现 [^24]。这种端到端的方式看似高效，但 Böckeler 的评估显示，它无法灵活适应不同规模的任务，且在长流程中 agent 频繁忽略指令。

**微 Agent 模式** [^25] 主张将大型任务分解为 3-20 步的小 agent，每个 agent 专注于一个明确的子任务，嵌入更大的确定性 DAG（有向无环图）中。作者以一个部署机器人为例：管理前后端发布的 agent 在关键步骤请求人类审批，而非试图一口气完成所有操作。

从实践角度看，微 Agent 模式的优势在于：
- **Context 可控**：每个小 agent 的 context window 不会过度膨胀
- **故障隔离**：单个 agent 失败不影响整体流程
- **可观测性**：每个 agent 的输入输出清晰，便于调试
- **灵活组合**：不同的任务可以组合不同的 agent 管道

Claude Code 的 subagent 和 fan-out 模式 [^23] 是微 Agent 模式的具体实现：subagent 在独立 context 中运行调研任务，保持主会话干净；fan-out 模式将大规模迁移任务分发给多个并行的 agent。

## 提示作为一等公民代码

"Own Your Prompts"（Factor 2）[^25] 提出了一个在实践中经常被忽视的原则：**prompt 应该像代码一样被版本控制、审查、测试和部署**。

当前大多数 agent framework 将 prompt 隐藏在抽象层背后——开发者调用 `agent.run()` 而不知道实际发送给 LLM 的完整 prompt 是什么。12-Factor Agents 认为这是一个根本性错误：开发者应该对发送给 LLM 的每一个 token 拥有完全的可见性和控制力 [^25]。

这一原则与 SDD 的"规范即主要产物"理念形成了有趣的交汇。无论主要产物是 spec 文件还是 prompt 模板，核心思想是一致的：**驱动 agent 行为的"指令"必须被当作第一优先级的工程产物来管理**。

Claude Code 的实践提供了具体的实现路径 [^23]：
- **CLAUDE.md** 作为项目级的 prompt 配置，提交到 git 让团队共同维护
- **Skills**（`.claude/skills/` 中的 SKILL.md 文件）作为按需加载的领域知识
- **Hooks** 作为确定性的工作流控制点，补充 advisory 性质的 prompt 指令

SDD 工具中，Kiro 的 "steering memory bank" [^24] 和 Spec-kit 的 "Constitution" [^24] 也扮演了类似角色——它们都是持久化的 prompt/spec 配置，试图跨会话保持 agent 行为的一致性。

## 复合知识的系统化积累

一个跨所有文章的隐含主题是：**agent 的长期价值不仅在于单次任务执行，更在于知识的系统化积累**。

Code APIs 模式中的 **Skills** 系统 [^17] 展示了一种"学习"能力——agent 将常用的工具组合保存为可复用的函数，随时间建立越来越丰富的高级能力库。这不是 LLM 层面的学习（模型权重不变），而是 harness 层面的知识积累。

参考应用（Reference Application）[^21] 是另一种知识积累形式——组织的编码标准和最佳实践以可编译代码的形式持久化，通过 MCP server 持续暴露给 agent。每次 drift detection 和修复都是一次"学到什么是好代码"的过程。

12-Factor Agents 中的 **统一状态管理（Factor 5）** 和 **Stateless Reducer（Factor 12）** [^25] [^26] 则为知识积累提供了基础设施——每次 agent 执行都被完整记录为事件流，可用于后续的分析、优化和模式发现。

这种系统化的知识积累将"使用 agent"从一次性的任务执行提升为**组织能力的持续建设**。团队投入 agent 工作流的每一分努力（写更好的 CLAUDE.md、维护参考应用、优化 prompt 模板）都在积累复合收益。

## 与历史 MDD 的类比与教训

Böckeler 将 SDD 与 Model-Driven Development 的历史进行类比 [^24]，这是本综述中最具战略价值的视角。

MDD 的核心愿景——"从高级模型自动生成代码"——与 SDD 的"从规范生成代码"惊人相似。MDD 最终失败的原因包括：模型语言的表达力不足、生成代码难以定制、双向同步（模型与代码）的技术难度、以及前期建模开销与灵活性的矛盾。

LLM 确实减少了 MDD 的一些限制 [^24]：自然语言比 UML 更灵活，LLM 可以处理模糊和不完整的规范，生成的代码可以自由定制。但 LLM 引入了 MDD 不曾有过的新问题——**非确定性**。同样的规范输入两次，可能产生不同的代码输出；agent 可能忽略规范中的关键指令；生成的代码可能违反规范本身声明的约束。

12-Factor Agents 的回应方式值得注意：它不试图构建"完美的 spec-to-code 管道"，而是承认非确定性的存在并在架构层面管理它 [^25]。Stateless Reducer 模式确保可重放性；小而专注的 agent 限制非确定性的影响范围；自定义 control flow 允许在关键节点插入确定性检查。

历史的教训是清晰的：**不要期望规范到代码的完全自动化**。MDD 在追求完全自动化的道路上失败了，SDD 如果重蹈覆辙，结果不会不同。更务实的路径是 Böckeler 建议的"先从 spec-first 开始，验证价值后再逐步向 spec-anchored 演进" [^24]，以及 12-Factor Agents 主张的"确定性代码 + 战略性 LLM 决策"的混合架构 [^25]。

## 跨文章洞见

### 规范、Prompt 与 Harness 的三位一体

SDD 的"规范"、12-Factor Agents 的"prompt as first-class code"、以及 Fowler 团队的"harness"概念，本质上指向同一个东西：**控制 agent 行为的工程化产物**。无论你称之为 spec、prompt、还是 harness，关键是将它们从"附属文档"提升为"一等公民产物"，用软件工程的全套纪律来管理。

### 确定性与非确定性的边界管理

12-Factor Agents 主张"大部分确定性代码 + 战略性 LLM 决策" [^25]，SDD 工具试图用规范来约束 LLM 的非确定性 [^24]，Claude Code 用 hooks（确定性）补充 CLAUDE.md（advisory）[^23]。一个一致的模式浮现：**成功的 agent 架构将 LLM 的非确定性限定在明确的决策点，其余部分保持确定性**。

### 工具成熟度的现实差距

Böckeler 对 SDD 工具的评估 [^24] 与 12-Factor Agents 对 agent framework 的批评 [^25] 指向同一个现实：**当前的工具生态还远未成熟**。SDD 工具可能放大审查负担，agent framework 可能过度抽象关键控制点。对技术管理者而言，这意味着需要保持工具选择的灵活性，避免过早锁定在任何一个 framework 或工作流上。

## 对技术管理者的建议

1. **从 Spec-first 开始，不要直接追求 Spec-as-source**。对复杂功能要求 agent 先产出规范文档再编码，但不要强迫简单任务也走完整流程。渐进式采纳比激进式变革风险更低。

2. **将 Prompt 纳入工程管理体系**。对 CLAUDE.md、AGENTS.md、prompt 模板实施版本控制和代码审查。建立 prompt 变更的回归测试——prompt 的修改可能像代码修改一样引入 regression。

3. **采用微 Agent 架构而非单体 Agent**。将复杂工作流分解为 3-20 步的小 agent，用确定性的 DAG 编排。这提供了更好的可观测性、故障隔离和灵活性。

4. **投资 AgentOps 基础设施**。建立状态持久化、全链路 tracing、错误重试上限、人类审批 SLA。这些运维基础设施的缺失会在生产环境中造成严重问题。

5. **建立复合知识积累机制**。维护参考应用、Skills 库、prompt 模板库。将每次 agent 使用中的经验教训固化到这些知识资产中，形成组织级的复合收益。

6. **学习 MDD 的历史教训**。不要期望完全自动化的 spec-to-code 管道。保持人类在关键决策点的参与（on the loop），将 LLM 的非确定性限定在明确的边界内，用确定性检查（测试、linter、hooks）兜底。

7. **保持工具选择的灵活性**。当前 SDD 工具和 agent framework 都在快速演化中。避免过早锁定，优先投资于可迁移的知识资产（规范、测试、参考应用）而非特定工具的配置。

## 引用来源

- [^24]: [Fowler SDD Tools](../notes/24-fowler-sdd-tools.md)
- [^25]: [HumanLayer 12-Factor Agents](../notes/25-humanlayer-12-factor-agents.md)
- [^26]: [12-Factor AgentOps](../notes/26-12-factor-agentops.md)
- [^17]: [Anthropic Code Execution with MCP](../notes/17-anthropic-code-execution-mcp.md)
- [^21]: [Fowler Anchoring to Reference Application](../notes/21-fowler-anchoring-reference.md)
- [^23]: [Claude Code Best Practices](../notes/23-claude-code-best-practices.md)
