Files
junhong_cmp_fiber/docs/engineering/从零构建Agent友好项目最佳实践.md
break c64f3d8b80
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m31s
全局审计完成
2026-08-07 11:02:52 +08:00

557 lines
23 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 从零构建 Agent 友好项目Harness 工程实践与文档标准
> 用途:用于新项目初始化和长期治理。它定义“哪些知识需要存在、放在哪里、按什么格式写、如何验证”。
>
> 核心结论OpenSpec 只负责当前行为与行为变更;完整 Harness 还必须让 Agent 能找到事实、运行系统、观察结果、遵守边界、独立验证并持续清理熵。
## 1. 先澄清ADR 不是 OpenSpec
- OpenSpec 的标准 Artifact 是 `proposal`、增量 `specs``design``tasks`
- `design.md` 记录**一次 Change** 的实现设计、取舍、迁移和回滚。
- ADR 是业界通用但独立的“长期架构决策记录”,不属于 OpenSpec也不是新项目必需品。
- 默认不建 `adr/`。只有一个跨 Change、长期生效、存在真实竞争方案且无法从代码理解原因的决策才考虑单独记录否则留在对应 Change 的 `design.md`
## 2. Harness 的完整范围
OpenAI 的 Harness Engineering 不只是“短 AGENTS.md + docs”。一个完整闭环包含
1. **仓库可读**:事实进入仓库,并有清晰索引和单一权威位置。
2. **环境可运行**:每个工作区能独立启动、测试和复现。
3. **结果可观察**Agent 能读取日志、指标、追踪、页面和 DOM而不是靠猜。
4. **架构可约束**:依赖方向、边界数据解析和关键不变量可机械检查。
5. **任务可判定**:需求被写成可观察、可验证的行为契约。
6. **执行可闭环**Agent 能复现、修改、验证、比较前后结果并处理反馈。
7. **知识可维护**链接、Owner、新鲜度和生成来源可检查过期内容持续清除。
8. **人类掌舵**人负责目标、优先级和判断点Agent 负责读取、执行、验证和维护。
因此Context 收敛不是“少写文档”,而是让默认 Context 很小、按需 Context 有标准、运行反馈足够强。
## 3. 从最小集合开始
### 3.1 所有项目 Day 0 必需
```text
AGENTS.md # 短导航与真正的全局硬约束
README.md # 人和 Agent 都可执行的启动入口
ARCHITECTURE.md # 一页系统地图和依赖边界
openspec/ # 当前行为与行为变更
CI / scripts # 格式化、检查、测试、构建命令
```
### 3.2 有真实内容时才创建
```text
docs/integrations/ # 存在第三方契约
docs/engineering/ # 存在跨模块、长期工程约束
docs/generated/ # 能由命令稳定再生的参考资料
RELIABILITY.md # 存在队列、重试、容灾、SLO 等系统性要求
SECURITY.md # 存在统一信任边界、敏感数据或威胁模型
FRONTEND.md # 前端规模足以需要统一交互/状态/设计规则
PRODUCT_SENSE.md # 多团队或 Agent 经常误判产品取舍
QUALITY_SCORE.md # 已有明确评分维度和维护机制
```
不要预建空目录、空模板或“以后也许有用”的文档。
### 3.3 如何理解 OpenAI 文章中的示例目录
文章展示的是一套成熟仓库的实际结构,不是要求每个项目照抄。与本指南的映射如下:
| 文章中的示例 | 解决的问题 | 本指南的默认选择 |
|---|---|---|
| `ARCHITECTURE.md` | 系统地图与边界 | Day 0 创建一页版本 |
| `docs/design-docs/``DESIGN.md` | 跨范围设计知识 | 单次变更放 OpenSpec `design.md`;只有长期跨 Change 设计才另建 |
| `docs/exec-plans/{active,completed}``PLANS.md` | 长任务持续执行 | 默认用 OpenSpec `tasks.md`;非行为型长期治理才建唯一执行计划 |
| `docs/product-specs/` | 当前产品行为 | 使用 `openspec/specs/`,不再复制一套 |
| `docs/generated/db-schema.md` | Agent 可检索的生成事实 | 有稳定生成命令时放 `docs/generated/` |
| `docs/references/*-llms.txt` | 将仓库外依赖资料变成 Agent 可读输入 | 仅保存任务高频需要且无法稳定在线获取的官方资料;第三方协议归 `docs/integrations/` |
| `FRONTEND.md` | 前端统一边界 | 前端复杂度触发后创建 |
| `PRODUCT_SENSE.md` | 产品判断原则 | 重复误判触发后创建 |
| `QUALITY_SCORE.md` | 领域/架构质量评分 | 有固定评分和治理动作后创建 |
| `RELIABILITY.md``SECURITY.md` | 跨系统可靠性和安全约束 | 对应风险出现后创建 |
关键不是目录名一致而是每类知识有唯一权威位置、Agent 可发现、能被验证、不会和 OpenSpec 重复。
### 3.4 不重复的事实分工
| 知识 | 权威位置 | 不应放入 |
|---|---|---|
| 当前可观察业务行为 | `openspec/specs/` | AGENTS、工程规范 |
| 拟议行为变化 | `openspec/changes/<change>/` | 主 Specs、路线图副本 |
| 系统地图和依赖方向 | `ARCHITECTURE.md` | 每个 Change 重复描述 |
| 第三方提供的契约 | `docs/integrations/` | 业务 Spec 原文复制 |
| 长期工程约束 | `docs/engineering/` + 检查器 | 任务型 Skill |
| 实现事实 | 代码、配置、迁移 | 叙述文档副本 |
| 可再生参考资料 | `docs/generated/` | 手工维护文档 |
| 任务过程 | OpenSpec Change 或唯一执行计划 | 第二套 TODO/状态台账 |
| 完成证据 | 测试、CI、运行输出 | 长期“完成总结” |
## 4. 每类文档的统一质量契约
除生成文档外,长期文档至少要让读者回答:
```yaml
purpose: 这份文档解决什么问题
scope: 适用与不适用范围
source_of_truth: 哪些事实由本文权威维护,哪些只引用
owner: 负责判断内容是否仍成立的角色或团队
last_verified: 最后通过代码、运行结果或官方来源核验的日期
update_triggers: 哪些变更发生时必须同步检查本文
verification: 用什么命令或证据判断内容仍有效
```
这不要求所有文件都使用 YAML要求这些信息明确可找到。
### 合格标准
- 写具体规则和可判定结果,不写“注意质量”“合理处理”等口号。
- 关键声明有代码路径、命令、测试、Schema 或官方来源支撑。
- 复制外部内容时标明来源版本和核验时间。
- 一项事实只有一个权威正文,其他位置只链接。
- 每个“必须/禁止”都对应机械检查,或明确说明当前为何只能人工审查。
- 示例只用于消除歧义;示例不得成为第二份规则。
### 不合格信号
- 没有适用范围、Owner 或更新触发条件。
- 只有目录介绍,没有依赖方向、入口或验证方式。
- 把当前 Bug 写成理想行为,或把未来设计冒充当前事实。
- 从代码能直接搜索得到的列表被手工复制并长期维护。
- 同一个状态、错误码或字段定义在多份文档中各写一遍。
## 5. AGENTS.md 标准
### 必填内容
1. 项目一句话目标。
2. Agent 从代码无法自然发现、且几乎所有任务都适用的硬约束。
3. build、test、lint、spec 验证的真实命令。
4. 事实源优先级和按需文档入口。
5. 禁止直接修改的生成物、敏感路径或外部生产边界。
### 禁止内容
- 完整 DTO、Model、路由或迁移操作步骤。
- 领域状态机、接口字段表和第三方协议正文。
- 已完成任务、历史方案、长 Review 清单。
- Agent 能从代码、包管理器或 `--help` 获取的通用知识。
### 尺寸与验证
- 80120 行是目标150 行是软预算。
- 每一行应对大多数任务有价值。
- CI 检查链接有效;验证命令必须在干净环境可运行。
```md
# 项目 Agent 指南
## 项目目标
<一句话>
## 全局硬约束
- <规则;对应检查命令或规则文档链接>
## 验证
- Build: `<command>`
- Test: `<command>`
- Lint: `<command>`
- Spec: `openspec validate --all`
## 事实源
- 当前行为:`openspec/specs/`
- 系统边界:`ARCHITECTURE.md`
- 外部契约:`docs/integrations/`
- 工程约束:`docs/engineering/`
```
## 6. ARCHITECTURE.md 标准
它是系统地图,不是完整设计书。目标是让新 Agent 在几分钟内知道从哪里进入、允许依赖谁、数据如何流动。
### 必填章节
1. **系统职责与非职责**:系统解决什么,不解决什么。
2. **运行单元**API、Worker、定时任务、前端、数据库、缓存、外部系统。
3. **领域/模块地图**模块职责、Owner如有和主要入口路径。
4. **依赖方向**:允许和禁止的跨层、跨模块依赖。
5. **关键数据流**:请求、异步事件、回调、读写路径。
6. **信任与事务边界**:外部输入在哪里解析,事务在哪里开始/结束。
7. **验证方法**:结构测试、依赖检查、启动或 smoke 命令。
8. **详细资料索引**:链接 Specs、工程约束和外部契约不复制正文。
### 写法标准
```md
## 模块:订单
- 职责:创建订单并管理支付前后的状态流转
- 入口:`internal/...`
- 可依赖:共享错误、支付 Port
- 禁止依赖HTTP Handler、具体支付 SDK
- 行为契约:`openspec/specs/orders/spec.md`
- 结构验证:`<command>`
```
### 机械检查
- 用 import/dependency 测试验证禁止依赖。
- 用 smoke 命令验证列出的运行单元确实可启动。
- 用链接检查验证每个引用存在。
- 模块增删、入口迁移或依赖方向改变时触发更新。
## 7. 外部契约文档标准
外部契约记录“第三方实际要求我们怎样交互”。它不是本系统的产品需求,也不是 SDK 使用教程。
### 7.1 目录规则
```text
docs/integrations/<provider>/
├── README.md # 当前接入契约与导航
├── examples/ # 经脱敏、可复现的请求响应样例(需要时)
└── source/ # 无稳定链接的官方原文快照(需要时)
```
官方内容有稳定 URL 时只保存链接和项目所需摘要;不要复制整站文档。
### 7.2 README 必填字段
```md
# <Provider> 接入契约
## 元数据
- Owner: <角色/团队>
- 官方来源: <URL 或 source/ 文件>
- 官方版本/发布日期: <值或 unknown>
- 项目适用环境: <sandbox/production/region>
- 最后核验: YYYY-MM-DD
- 更新触发: SDK升级、官方版本变化、字段/签名/错误码变化
## 接入范围
- 使用能力:<项目实际使用的 API/事件>
- 不使用能力:<容易误用但明确不接入的能力>
## 端点与认证
| 场景 | Method | URL/Topic | 认证 | 超时 |
## 请求契约
| 字段 | 类型 | 必填 | 约束 | 来源章节 | 项目映射 |
## 响应与错误
| 外部状态/错误码 | 含义 | 可重试 | 项目处理 | 告警 |
## 签名与回调
- 签名原文构造:<精确定义>
- 验签步骤:<精确定义>
- 时间窗/重放保护:<规则>
- 回调确认语义:<响应内容和重试条件>
## 可靠性
- 幂等键:<字段与作用域>
- 超时:<连接/请求>
- 重试:<次数、退避、仅哪些错误>
- 限流:<规则>
- 对账/补偿:<触发与入口>
## 安全与数据
- 凭证名称及托管位置:<只写名称,不写密钥>
- 敏感字段:<日志脱敏规则>
- 来源校验:<证书/IP/签名等>
## 验证
- 本地/沙箱命令:`<command>`
- 固定输入:`<fixture>`
- 预期结果:`<literal outcome>`
- 生产人工验收:<只有真实环境才能完成的最小步骤>
```
### 7.3 质量门禁
- 所有项目使用的字段都能追溯到官方来源或经确认的真实样例。
- 签名、金额单位、时间格式、编码、回调确认内容必须写成精确规则。
- 每种外部错误明确:重试、失败、忽略、人工处理中的一种。
- 重试必须同时定义上限、退避和幂等保护。
- 示例必须脱敏,并可被测试或脚本读取;真实密钥不得进入仓库。
- 至少有一个沙箱/契约测试,或明确记录为何只能人工验收。
### 7.4 不合格示例
```md
调用失败时适当重试。
```
问题:没有错误范围、次数、退避、幂等和最终失败动作,无法实现或验证。
合格写法:
```md
仅对连接超时和外部错误 E_TEMP 重试;最多 3 次,间隔 1s/2s/4s
每次复用同一幂等键。三次失败后记录 integration failure 并进入人工对账队列。
```
## 8. 工程约束文档标准
工程约束描述“所有相关代码长期必须保持的性质”。DTO、Model、路由、迁移、注释、依赖边界属于此类而不是 Skill。
### 8.1 一条约束的标准格式
```md
### ENG-<AREA>-NNN<可判定标题>
- 状态active | deprecated
- 适用范围:<路径、语言、组件或操作>
- 规则MUST/MUST NOT <单一可判定约束>
- 理由:<错误成本或架构原因;不复述规则>
- 正例:`<最小示例或现有代码链接>`
- 反例:`<最小示例>`
- 机械检查:`<测试/Lint/脚本命令>``暂为人工:<原因>`
- 例外:<允许条件、批准者、记录位置;无则写“无”>
- Owner<角色/团队>
- 最后验证YYYY-MM-DD
- 更新触发:<框架升级、目录调整、事故等>
```
### 8.2 规则拆分标准
- 一个 ID 只表达一个义务,避免“正确、安全、高性能地处理”。
- 适用范围必须能映射到路径或组件。
- 正例优先链接仓库内稳定实现,不复制大段代码。
- 反例只展示最容易犯且有实际代价的错误。
- 规则能由机器判断时,文档必须链接检查器;检查器才是执行门禁。
- 例外必须有边界和到期/复审条件,不能写“特殊情况除外”。
### 8.3 分类
| 分类 | 应记录 | 首选验证 |
|---|---|---|
| 架构 | 依赖方向、分层、边界解析 | 结构测试、import Lint |
| 数据 | 迁移、主键、金额单位、事务 | Schema/Lint/集成测试 |
| API | 路由注册、错误形状、兼容性 | OpenAPI diff、契约测试 |
| 可靠性 | 幂等、重试、锁、Outbox | 故障注入、集成测试 |
| 安全 | 权限、敏感字段、信任边界 | 安全测试、静态检查 |
| 代码品味 | 禁止无价值抽象、复杂度上限 | Lint + Review |
### 8.4 合格与不合格示例
不合格:
```md
数据库迁移要谨慎,注意兼容旧数据。
```
合格:
```md
### ENG-DB-003新增非空列必须可在线回填
- 适用范围:`migrations/`
- 规则已有数据表新增非空列时MUST 先增加可空列或带兼容默认值,完成回填后再收紧约束。
- 理由:直接增加无默认值的非空列会使现有数据迁移失败。
- 机械检查:`<migration smoke command>`
- 例外:仅空表;必须在 Change design 中提供查询证据。
```
## 9. OpenSpec 生成提案的标准
用户不需要手工填写长表。AI 生成 Change但生成器和 Reviewer 必须遵守质量门禁。
### 9.1 Explore 后才能确定的内容
- 当前行为和证据路径。
- 目标、原因、范围和非目标。
- 角色、资源、前置状态和数据范围。
- 状态、金额、权限、事务、并发、幂等和外部失败边界。
- 已知事实、推断和真正需要人决策的问题。
### 9.2 Proposal
只回答为什么、做什么、不做什么、影响哪些 Capability。不得塞入实现步骤和通用工程规范。
### 9.3 Requirement / Scenario
```md
### Requirement: <一个可观察义务>
系统 SHALL <测试者无需阅读实现即可判定的结果>。
#### Scenario: <场景>
- **GIVEN** <角色、数据、前置状态>
- **WHEN** <一个动作>
- **THEN** <可观察结果>
- **AND** <副作用或必须保持的不变量>
```
门禁:
- 一个 Requirement 一个主要义务。
- Scenario 有具体前置条件、触发和结果。
- 覆盖主流程以及代价最高的权限、失败、边界或重复场景。
- 不使用“体验良好”“正确处理”“高性能”等不可判定词。
- 不写函数名、表名、ORM、缓存或队列选型。
- Brownfield 的错误现状也按 As-Is 写入Spec 描述事实不等于认可设计。
### 9.4 Design
记录本 Change 的架构通道、数据与事务、并发、外部失败、兼容、迁移、发布、回滚和真实取舍。简单且实现显然时保持简短。
### 9.5 Tasks
- 按可独立验证的纵向切片拆分,不按 Model/Service/Handler 水平拆分。
- 每项映射 Requirement/Scenario并包含最小验证命令和预期结果。
- 适用时同时验证响应、持久化、副作用和失败不变量。
- 实施发现 Artifact 与事实冲突时同步修订,不静默跳过或扩大任务。
## 10. Generated docs 标准
仅当内容能稳定再生且 Agent 经常需要查询时创建,例如数据库 Schema、OpenAPI 摘要或配置清单。
每份生成文件顶部必须包含:
```md
<!-- GENERATED FILE: DO NOT EDIT -->
- Source: <代码/Schema 路径>
- Command: `<生成命令>`
- Generator version: <版本>
- Generated at: <时间或来源 commit>
```
CI 重新生成并检查 diff。生成结果没有稳定命令时它就不是 generated doc而是会腐烂的手工副本。
## 11. 唯一执行计划标准
一般功能直接使用 OpenSpec `tasks.md`,不要再建 `docs/exec-plans/`。只有不改变产品行为、跨多天且 OpenSpec 不适配的迁移/治理任务,才使用单独执行计划。
必填内容:
- 目标和完成定义。
- 范围、非目标和受保护路径。
- 前置依赖和顺序。
- 可恢复的任务清单,每项含验证。
- 当前进度,只在这一处更新。
- 关键决策与新发现。
- 回滚条件和命令。
- 最终验收命令及预期结果。
Manager、Executor、Auditor 可以分工但只能共享这一份任务契约Harness state 和报告只是可丢弃证据。
## 12. Agent 可操作环境标准
### 12.1 独立运行
- 每个 worktree/工作区可使用不同端口和隔离的临时数据启动。
- README 提供一条启动命令、一条测试命令和一条重置命令。
- 依赖、种子数据和环境变量有可复制的本地默认值;真实凭证不入库。
### 12.2 浏览器与 UI
有 UI 时Agent 应能:
- 启动应用并通过浏览器/CDP 操作关键流程。
- 保存失败前后的截图或视频。
- 读取 DOM、Console error 和失败网络请求。
- 用稳定测试定位器,而非脆弱坐标。
### 12.3 可观测性
本地至少能按 request/correlation ID 串起:
- 结构化日志。
- 关键指标。
- 跨进程或异步链路追踪(系统存在此类链路时)。
提供最小查询命令。临时本地可观测栈应可一键启动和销毁,运行数据不作为长期事实源。
### 12.4 自主验证闭环
```text
复现问题 → 捕获基线 → 修改 → 运行最小测试 → 启动系统
→ 检查日志/指标/页面 → 对比前后 → 完整门禁 → 提交审查
```
只有产品取舍、不可逆操作、真实生产权限或相互冲突的事实需要升级给人。
## 13. Reliability、Security、Product Sense、Quality Score
这些不是所有项目的固定作业。
### RELIABILITY.md何时创建
当系统出现 SLO、重试、队列、定时任务、降级、容灾或数据修复策略时创建。最低结构关键用户旅程、SLO/错误预算、故障模式、超时重试、幂等与补偿、观测和告警、恢复/演练、Owner。
### SECURITY.md何时创建
当存在统一信任边界、敏感数据、权限模型或外部暴露面时创建。最低结构资产和角色、信任边界、认证授权、数据分类、密钥、审计、主要威胁与控制、验证命令、事件入口、Owner。
### PRODUCT_SENSE.md何时创建
当多个功能反复需要相同产品判断、Agent 经常做出局部正确但产品错误的选择时创建。最低结构:目标用户、核心任务、优先级原则、明确非目标、取舍示例、正反例。具体行为仍归 OpenSpec。
### QUALITY_SCORE.md何时创建
只有团队真的按固定维度定期评分并采取行动时创建。每项必须有定义、证据来源、当前分数、阈值、Owner、改进动作和复评日期没有维护机制就不要创建。
## 14. 文档检查与持续清熵
### 每次变更
- 检查内部链接和引用路径。
- OpenSpec 校验全部通过。
- 生成文档可无差异再生。
- 架构边界检查通过。
- Change 完成后同步/归档 Specs删除被替代的同义说明。
### 周期性 doc-gardening
Agent 定期生成候选清单,人只处理真正的判断点:
- 失效链接、孤儿文件和长期 TBD。
- `last_verified` 过期且影响仍高的文档。
- 与代码、测试或 Specs 冲突的声明。
- 重复规则、重复事实和已被检查器取代的提醒。
- 已完成的临时计划、报告和可丢弃运行证据。
清理优先级:删除 > 合并到权威位置 > 更新 > 新建索引。
### 黄金原则
把反复出现的 Review 意见和事故教训变成最靠近问题的自动化约束测试、类型、Lint、Schema、结构检查或运行时保护。不要持续加长 AGENTS.md。
## 15. Day 0 到稳定期的落地顺序
1. Agent 生成最小可运行仓库、格式化、包管理、CI 和测试骨架。
2. 建立短 `AGENTS.md`、可执行 `README.md` 和一页 `ARCHITECTURE.md`
3. 初始化 OpenSpec第一个真实功能走 Explore → Propose → Review → Apply → Verify → Archive。
4. 打通独立工作区启动、日志读取和最小 smoke 测试。
5. 出现真实第三方时按外部契约模板创建资料。
6. 出现重复工程失误时先加检查器,再补对应约束 ID。
7. 只有触发条件成立时增加 Reliability、Security 等专项文档。
8. 持续删除完成报告、重复事实和过期 Context避免周期性大扫除。
## 16. 最低验收清单
- [ ] 新 Agent 只读 AGENTS.md 就能找到行为、架构、外部契约和验证入口。
- [ ] README 的启动、测试、重置命令在干净工作区可执行。
- [ ] ARCHITECTURE.md 的依赖方向有检查或明确人工门禁。
- [ ] OpenSpec Requirement/Scenario 可观察、可判定。
- [ ] 外部契约的版本、字段、签名、重试、幂等和验证证据齐全。
- [ ] 每条工程约束有范围、规则、理由、检查、例外和 Owner。
- [ ] Agent 能自行读取错误日志;有 UI 时能读取页面和网络失败。
- [ ] 同一事实没有多个权威正文。
- [ ] 文档链接、生成检查、Spec 校验和测试进入 CI。
- [ ] 临时计划、状态和运行证据不会成为第二事实源。
## 17. 参考资料
### 第一方
- [OpenAI Harness Engineering](https://openai.com/index/harness-engineering/)
- [OpenSpec Overview](https://openspec.dev/docs/overview)
- [OpenSpec Writing Specs](https://openspec.dev/docs/writing-specs)
- [OpenSpec Getting Started](https://openspec.dev/docs/getting-started)
- [LongHorizon-Harness](https://github.com/AMAP-ML/LongHorizon-Harness)
### 补充实践
- [AugmentHow to write good AGENTS.md files](https://www.augmentcode.com/blog/how-to-write-good-agents-dot-md-files)
- [BetterClawAGENTS.md best practices](https://www.betterclaw.io/blog/agents-md-best-practices)
- [What goes in AGENTS.md?](https://ro14nd.de/what-goes-in-agents-md/)
- [Domain Expertise Is the New Agentic Coding Moat](https://www.developersdigest.tech/blog/domain-expertise-agentic-coding-moat)
- [OReillyHow to write a good spec for AI agents](https://www.oreilly.com/radar/how-to-write-a-good-spec-for-ai-agents/)