一套系统,让 AI 写代码的速度变成生产力
英文标题:一套系统,让 AI 写代码的速度变成生产力
AI 生成代码的速度已经超过了人阅读代码的速度。 继续把“逐行看懂全部实现”当作主要质量控制手段,只会让人越来越跟不上 Agent。 但问题不只是代码生成太快。每次启动新会话,Agent 还需要重新理
77.0 KB
183.2 KB
97.9 KB
79.8 KB
73.4 KB
85.6 KB
85.6 KB
147.1 KB
6.5 KB
中文
AI 生成代码的速度已经超过了人阅读代码的速度。 继续把“逐行看懂全部实现”当作主要质量控制手段,只会让人越来越跟不上 Agent。
但问题不只是代码生成太快。每次启动新会话,Agent 还需要重新理解项目是什么、当前走到哪一步、哪些决定已经确认、什么可以做、什么不能做。如果这些信息只存在于对话和人的记忆里,模型越快,跑偏也越快。
所以,AI 原生开发真正需要的不是一个更长的提示词,而是一套完整的开发系统。它至少包含两个互相咬合的闭环:
- 上下文闭环 :把规则、规格、状态和历史决策写进仓库,让冷启动的 Agent 能恢复工作上下文;
- 执行闭环 :把需求做成可执行事实,把功能拆成可运行的小版本,再通过真实反馈、阶段门禁和回滚机制控制风险。
前者决定 Agent 是否知道该怎么做,后者决定它做出来的东西是否真的可用。只有两个闭环同时存在,代码生成速度才会成为生产力,而不是失控的放大器。
从结构上看,这两个闭环保存的是两类不同的关系。上下文闭环保存项目事实及其关系 :哪些规格有效、哪些决定已被替代、当前处于什么状态;执行闭环保存任务关系 :谁依赖谁、什么条件允许推进、哪些工作可以并行、失败后应该回到哪一层、哪些节点必须由人放行。前者外部化“当前哪些事实有效”,后者外部化“接下来应该做什么,以及满足什么证据才能推进”。
一、先把模型当作近似无状态函数
设计 AI 原生项目时,一个有用的工程假设是:把模型当作近似无状态函数。 不依赖它记得上一次会话,也不依赖“我们之前说好了”这种口头上下文。每次调用都假设它需要从当前可见材料重新理解任务。
这个假设会直接改变仓库的职责。仓库不再只是存放代码,也要承担 Agent 的操作环境和长期记忆。 你希望 Agent 稳定知道的事情,都应该有明确、可读取、可追溯的载体:
- 项目是什么、使用什么技术栈;
- 各类文件承担什么职责;
- 当前需求处于哪个阶段;
- 哪些规格和契约已经确认;
- 修改某个模块前必须读取什么;
- 完成后运行哪些验证;
- 过去做过哪些决定,哪些旧决定已经失效;
- 哪些动作必须等待人的确认。
围绕这些问题,可以把 Agent 的工作环境分成四类能力:
| 层级 | 解决的问题 | 常见载体 |
|---|---|---|
| 角色与规则 | Agent 应该怎样工作、不能越过什么边界 | System Prompt、AGENTS.md、Rules |
| 外部连接 | Agent 如何访问 Issue、构建、数据库和内部文档 | MCP、受控工具 |
| 任务流程 | 某类任务应该按什么步骤完成 | Skills、脚本、模板 |
| 能力分发 | 一组规则、流程、连接和资源如何安装复用 | Plugin、工具包 |
Rules 负责保底线,Skills 负责提供可重复的做法,Hooks 和脚本负责把关键约束变成机器可以执行的检查。三者不能互相替代: 只有规则,Agent 每次都要重新摸索;只有流程,它可能高效地做错事;只有脚本,又无法处理需要解释和判断的部分。
二、仓库要同时保存事实、过程、状态和经验
AGENTS.md 是索引,不是百科全书
根目录的 AGENTS.md 应该优先回答冷启动时最实际的问题:
- 当前运行环境、技术栈和开发工具;
- 项目地图,以及各类文件的职责;
- 修改某个模块前必须读取的规格和依赖;
- 实现、测试、验收和提交命令;
- 阶段门禁,以及必须等待人的节点;
- 测试报告和垃圾数据清理要求。
它适合做高频红线和导航入口,不适合塞进几千行细节。 架构、测试、安全、Git 等规则按主题分片,处理特定任务时再加载对应文件。这样既减少无关上下文,也避免同一条规则在多处出现不同版本。
过程文档与长期事实必须分开
本轮需求的讨论过程和长期有效的项目契约,生命周期不同,不应该混在一起。
workflow/保存本轮需求如何确认、考虑过哪些方案、为什么选择当前方案;specs/保存长期有效的 API、数据库、模块需求和验收标准;tasks/保存从规格拆出的当前执行单元;memory/保存跨需求仍有价值的决策和架构记录。
过程文档用于留痕,规格文件用于约束当前实现。需求完成后,讨论稿可以归档,但生效的契约必须继续随代码演进。 如果两者混放,Agent 很容易把过期方案当成现行事实。
关键事实还应登记来源:
- 需求来自用户原话、PRD、现有 API、设计稿,还是测试和日志,都应该能追溯;
- 没有来源时也应明确写出“暂无可用来源”,而不是让 Agent 自行补全。
来源登记的价值,是迫使不确定性显式暴露,避免一个看似合理、实际不存在的前提污染后续设计和实现。
决策记录需要废止机制
memory/decisions.md 可以记录简单决定,重要架构选择进入 memory/adr/。但决策记录不能只增不改。新决定覆盖旧决定时,必须明确写出覆盖关系。
否则仓库会同时保留两条互相冲突的“有效决定”,冷启动的 Agent 只能随机选一条。长期记忆需要谱系:当前规则是什么、它替代了什么、为什么替代。
状态应有唯一的机器来源
需求阶段可以由 workflow-state.json 或同类状态文件表达,例如:
需求讨论 → 方案选择 → 实现就绪 → 开发 → 验收 → 提交
这条线性状态机只是最小示例。复杂任务出现并行、条件分支、汇聚和回退时,可以把流程扩展为任务图,让依赖、触发、回退和审批成为显式关系;但无论流程形态如何,当前状态仍应由唯一、可审计的机器来源表达。
状态文件负责告诉脚本和 Hook 当前允许发生什么;对应的 Markdown 文档负责让人理解为什么推进。阶段切换最好保留用户确认原话,而不是只写一个 confirmed: true。这样,“用户已确认”是一条可审计的证据链,而不是 Agent 自己生成的布尔值。
一个最小可用的 AI 原生仓库骨架可以是:
my-project/
├── AGENTS.md # 总规则、项目地图、命令和门禁索引
├── workflow-state.json # 当前阶段的唯一机器状态源
├── workflow/ # 本轮需求和方案的确认过程
├── specs/ # 长期有效的契约与验收标准
├── tasks/ # 当前可执行任务
├── memory/
│ ├── decisions.md
│ └── adr/
├── rules/ # 按测试、安全、Git 等主题分片
├── .agents/
│ ├── skills/
│ ├── hooks/
│ └── mcp.json
├── frontend/ # 业务代码,内部结构沿用项目既有规范
├── backend/
├── tests/ # 契约、集成和端到端验证
└── scripts/ # 状态、契约和统一检查入口
这不是要求所有项目复制同一棵目录树。目录多不多不重要,重要的是:新会话只读仓库,就能回答“现在进行到哪、为什么这么做、接下来该做什么”。
三、把设计和需求做成可执行事实
仓库提供了工作环境,下一步是让需求成为 Agent 可以比较和验证的目标。
对于界面型产品,第一步不应是让 Coding Agent 直接写正式代码,而是先做一份可运行的 UI/UX 原型。原型交付的不只是截图,还应该尽量包含:
- HTML,用来表达页面和交互关系;
- CSS,用来固化颜色、字号、间距和尺寸规范;
- React 等组件代码,用来表达界面拆分方式;
- 模拟数据,用来表达页面依赖的数据结构;
- 权限、空状态、禁用状态和异常反馈等关键业务状态。
业务规则如果只写在长篇说明里,Agent 很容易遗漏或产生多种解释。 能够通过模拟数据和交互状态表达的规则,尽量直接放进原型;文字负责边界和例外,原型负责让主要规则可以被看见、被操作。
此时设计稿不再只是“参考图”,而是实现的直接参照物。 Coding Agent 面对的是一套可运行、可截图比较、可由人实际操作的目标。
需求评估技能适合通过连续追问和多模型讨论把问题想透,但生成的访谈记录和方案推演往往很长,很难直接交给人阅读或交给 Agent 实现。因此,PRD 在保留完整过程材料之外,还应附一份极简简报,只回答三个问题:产品解决什么问题、这一版做什么、暂时不做什么。简报是需求收敛后的确认文档,应放进仓库,让没有参与讨论的人也能快速理解并复述最终结论。
四、第一版要足够小,而且必须能运行
原型确认后,再把功能交给 Coding Agent 实现。第一版只做一个很小的可运行切片,例如一个主界面和一条核心交互,而不是一次生成十个页面。
功能越少,Agent 同时理解的状态、依赖和异常分支越少,结果通常越稳定。 一次生成十个页面,看起来节省了十轮沟通,实际上会把十组错误叠在一起,让人无法判断问题来自需求、设计、架构还是实现。
每个版本至少满足三个条件:
- 可以独立运行。 人能亲手操作,而不是只看代码或截图;
- 只增加一种主要不确定性。 新页面、真实数据接入和架构调整尽量不要同时发生;
- 有明确验收边界。 做到了什么、哪些能力暂时不做,都写进当前规格。
这条流程表面上比“一次生成完整产品”慢,实际上压缩了每轮的不确定性。问题更容易定位,失败更容易回退,Agent 也不会在错误方向上高速堆积实现。
五、问题要回到产生它的那一层
人在运行版本时,通常会发现两类问题:
- 实现问题 :崩溃、状态错误、性能异常,或者代码没有遵守已经确认的设计;
- 设计问题 :信息层级不清、交互不自然、布局在真实数据下失效。
实现问题直接在代码层修复。设计问题则先回到设计工具修改原型,再让 Agent 比较设计稿变化并更新实现。
为了省事直接在正式代码里修设计问题,会让设计和产品逐渐分叉:设计稿展示旧产品,代码里堆着只有当前维护者知道的例外。后续每一轮迭代都要为这份分叉付费。
设计问题改设计,规格问题改规格,实现问题改代码。前面改对了,后面再同步跟进。不要想通过在代码实现层打补丁的方式解决所有问题。
这也是 workflow/ 与 specs/ 分开的实际意义:讨论过程解释决策,长期规格约束实现,代码负责兑现规格。三层各自承担不同责任。
六、用硬门禁控制 Agent 的推进速度
Coding Agent 最危险的时候往往不是做得慢,而是推进得太快。 它可以在几分钟内完成实现、测试、重构和下一阶段拆解,而人还没有判断上一阶段的方向是否正确。
仅在提示词里写“请等待确认”并不稳定。 阶段推进应该同时依赖三类证据:
- 对应文档已经存在,内容达到当前阶段要求;
- 状态文件允许进入下一阶段;
- 需要人工判断的节点保留了用户确认原话。
Rules 告诉 Agent 不应该越界,Hook 和脚本让越界动作无法通过。比如需求没有确认就不生成实现计划,方案没有选定就不拆任务,验收没有通过就不提交或发布。
门禁也保护人的注意力。人不必追踪 Agent 的每个动作,只需要在需求、设计、可运行版本和最终验收几个关键节点做判断。Agent 负责高速探索和执行,人负责方向、边界和放行。
七、用反馈回路代替全面逐行审核
当 AI 写得比人读得快,质量控制的重点要从“我是否理解每一行”转向两个问题:
- 系统是否满足已经确认的规格;
- 失败时能否及时暴露、定位和回退。
有效的测试必须回答两个问题:这次实现是否符合规格,有没有破坏原有功能? 一个更稳的流程是:
Spec → Task → 可观察验收条件 → AI Coding → Run & Check
↑ │
└── Fix & Retry ┘
是否先写测试,应由风险和行为稳定程度决定。AI 很容易快速生成只覆盖 happy path、断言又很弱的自证式测试,因此不必教条地追求所有代码都测试先行。但每一项关键行为都必须有与风险相称的验证:
- 复杂、稳定的核心逻辑 :用单元测试保护边界、状态转换和关键不变量;
- 模块与真实依赖的协作 :用集成测试验证数据库、服务和外部接口;
- 前后端共享契约 :用契约测试检查 Schema 和字段一致性;
- 关键用户路径 :用 E2E 或浏览器自动化运行真实流程;
- 界面和体验问题 :由人实际操作,结合截图和可观察状态验收;
- 测试本身的可信度 :必要时用变异测试、故障注入或真实缺陷反证弱断言。
单元测试、集成测试和 E2E 不是只能选择一种。项目越接近核心业务、逻辑越复杂,越需要核心单测;越是跨模块、CRUD 和页面密集场景,越应该提高集成、契约和 E2E 的比重。测试结构应该跟随风险,不应该为了倒置或遵守某种金字塔而倒置。
静态检查、类型检查和测试输出还要让 Agent 自己看见。反馈越短、错误越具体,它在当前会话内闭环的问题越多。关键用户路径必须真的运行过,不能只因为代码编译或测试文件存在,就推断功能已经可用。
测试跑完也不代表工作结束。Agent 应该生成可复查的报告,并清除测试产生的账户、文件和数据库记录。普通规格和决策继续使用 Markdown;需要展示趋势、分布和多维指标的测试或分析报告,可以使用 HTML 和图表。格式不是重点,重点是结果可审计、环境可恢复。
在资源有限的本地环境里,完整浏览器回归和重型设计工具同时运行可能影响反馈速度。可以在普通迭代里使用浏览器插件或轻量交互验证,把完整 Playwright 回归留给关键节点。工具可以替换,真实用户路径不能省略。
八、稳定之后,再做性能审查和结构调整
功能通过验收后,不一定马上进入下一个需求。让 Agent 审查性能和结构,通常比继续堆功能更划算,但前提是已有可运行版本作为参照。
例如在一个 macOS 项目的第一版中,界面使用 NSScrollView。内容增多后内存明显上涨,性能分析建议改用 NSTableView。这类问题很难在静态设计稿里发现,必须先有真实数据和可运行版本,才能判断是否值得改变实现。
重构也要控制风险。对于边界明确但历史复杂的模块,与其原地拆除并重建,不如先新增一个实现,验证后再替换旧路径。这样可以比较新旧结果,失败时快速退回,也避免同时破坏现有行为和参照物。
但并行实现只是迁移手段。替换完成后必须删除旧路径,不能让两套实现永久共存。
每个有文件变化的实现任务还应该留下清晰边界:编辑前识别已有和并发变更,只包含当前任务相关修改,运行与改动相称的验证,再形成独立提交或等价的可回退记录。不要擅自推送、改写历史,也不要为只读任务创建空提交。
九、用“新会话六问”检验系统是否真的成立
目录存在不等于系统有效。 最直接的测试,是启动一个没有口头背景的新会话,让 Agent 只读仓库,然后回答六个问题:
- 项目是什么;
- 当前需求走到哪一步;
- 现在被允许做什么;
- 应该按什么流程做;
- 做完如何验证;
- 本轮经验应该写到哪里。
如果六个问题都能回答,说明事实、过程、状态和经验已经形成闭环。答不上来时,不要继续堆更多抽象目录,而要定位究竟缺少哪类信息:项目索引、当前状态、有效契约、验证入口,还是决策记录。
我在 travel-ledger-admin 项目中用这六问检查过一套类似骨架。项目地图、阶段状态、允许动作、默认流程和经验回写都能从仓库恢复,但验证仍是最弱的一环:契约检查没有机器兜底,根目录缺少完整的 E2E 链路,CI、可观察性和测试规则也不完整。
这个结果反而说明,最容易被省略的并不是目录和文档,而是“让结果真正可验证”的最后一公里。 状态机、规格和 Rules 可以阻止 Agent 胡乱推进,却不能证明产品已经工作。最终仍然要回到可运行版本、真实用户路径、可观察反馈和可恢复环境。
收个尾
AI 原生开发不是让 Agent 替人连续写更多代码,而是重新划分人、模型和仓库的责任:
- 仓库保存事实、过程、状态和经验;
- Agent 负责高速探索、实现、验证和修复;
- 人负责需求、设计、边界和阶段放行;
- 测试、Hook、状态机和提交记录提供可观察、可审计、可回退的反馈。
这套系统可以压缩成一句话:
把仓库变成 Agent 可以恢复上下文的工作环境,把目标做成可执行的事实,把功能拆成可运行的小版本,把问题送回正确的层,再用门禁和反馈回路控制推进。
只要上下文闭环和执行闭环同时成立,代码生成得再快,也不会快到失控。
English / 原文
AI 生成代码的速度已经超过了人阅读代码的速度。 继续把“逐行看懂全部实现”当作主要质量控制手段,只会让人越来越跟不上 Agent。
但问题不只是代码生成太快。每次启动新会话,Agent 还需要重新理解项目是什么、当前走到哪一步、哪些决定已经确认、什么可以做、什么不能做。如果这些信息只存在于对话和人的记忆里,模型越快,跑偏也越快。
所以,AI 原生开发真正需要的不是一个更长的提示词,而是一套完整的开发系统。它至少包含两个互相咬合的闭环:
- 上下文闭环 :把规则、规格、状态和历史决策写进仓库,让冷启动的 Agent 能恢复工作上下文;
- 执行闭环 :把需求做成可执行事实,把功能拆成可运行的小版本,再通过真实反馈、阶段门禁和回滚机制控制风险。
前者决定 Agent 是否知道该怎么做,后者决定它做出来的东西是否真的可用。只有两个闭环同时存在,代码生成速度才会成为生产力,而不是失控的放大器。
从结构上看,这两个闭环保存的是两类不同的关系。上下文闭环保存项目事实及其关系 :哪些规格有效、哪些决定已被替代、当前处于什么状态;执行闭环保存任务关系 :谁依赖谁、什么条件允许推进、哪些工作可以并行、失败后应该回到哪一层、哪些节点必须由人放行。前者外部化“当前哪些事实有效”,后者外部化“接下来应该做什么,以及满足什么证据才能推进”。
一、先把模型当作近似无状态函数
设计 AI 原生项目时,一个有用的工程假设是:把模型当作近似无状态函数。 不依赖它记得上一次会话,也不依赖“我们之前说好了”这种口头上下文。每次调用都假设它需要从当前可见材料重新理解任务。
这个假设会直接改变仓库的职责。仓库不再只是存放代码,也要承担 Agent 的操作环境和长期记忆。 你希望 Agent 稳定知道的事情,都应该有明确、可读取、可追溯的载体:
- 项目是什么、使用什么技术栈;
- 各类文件承担什么职责;
- 当前需求处于哪个阶段;
- 哪些规格和契约已经确认;
- 修改某个模块前必须读取什么;
- 完成后运行哪些验证;
- 过去做过哪些决定,哪些旧决定已经失效;
- 哪些动作必须等待人的确认。
围绕这些问题,可以把 Agent 的工作环境分成四类能力:
| 层级 | 解决的问题 | 常见载体 |
|---|---|---|
| 角色与规则 | Agent 应该怎样工作、不能越过什么边界 | System Prompt、AGENTS.md、Rules |
| 外部连接 | Agent 如何访问 Issue、构建、数据库和内部文档 | MCP、受控工具 |
| 任务流程 | 某类任务应该按什么步骤完成 | Skills、脚本、模板 |
| 能力分发 | 一组规则、流程、连接和资源如何安装复用 | Plugin、工具包 |
Rules 负责保底线,Skills 负责提供可重复的做法,Hooks 和脚本负责把关键约束变成机器可以执行的检查。三者不能互相替代: 只有规则,Agent 每次都要重新摸索;只有流程,它可能高效地做错事;只有脚本,又无法处理需要解释和判断的部分。
二、仓库要同时保存事实、过程、状态和经验
AGENTS.md 是索引,不是百科全书
根目录的 AGENTS.md 应该优先回答冷启动时最实际的问题:
- 当前运行环境、技术栈和开发工具;
- 项目地图,以及各类文件的职责;
- 修改某个模块前必须读取的规格和依赖;
- 实现、测试、验收和提交命令;
- 阶段门禁,以及必须等待人的节点;
- 测试报告和垃圾数据清理要求。
它适合做高频红线和导航入口,不适合塞进几千行细节。 架构、测试、安全、Git 等规则按主题分片,处理特定任务时再加载对应文件。这样既减少无关上下文,也避免同一条规则在多处出现不同版本。
过程文档与长期事实必须分开
本轮需求的讨论过程和长期有效的项目契约,生命周期不同,不应该混在一起。
workflow/保存本轮需求如何确认、考虑过哪些方案、为什么选择当前方案;specs/保存长期有效的 API、数据库、模块需求和验收标准;tasks/保存从规格拆出的当前执行单元;memory/保存跨需求仍有价值的决策和架构记录。
过程文档用于留痕,规格文件用于约束当前实现。需求完成后,讨论稿可以归档,但生效的契约必须继续随代码演进。 如果两者混放,Agent 很容易把过期方案当成现行事实。
关键事实还应登记来源:
- 需求来自用户原话、PRD、现有 API、设计稿,还是测试和日志,都应该能追溯;
- 没有来源时也应明确写出“暂无可用来源”,而不是让 Agent 自行补全。
来源登记的价值,是迫使不确定性显式暴露,避免一个看似合理、实际不存在的前提污染后续设计和实现。
决策记录需要废止机制
memory/decisions.md 可以记录简单决定,重要架构选择进入 memory/adr/。但决策记录不能只增不改。新决定覆盖旧决定时,必须明确写出覆盖关系。
否则仓库会同时保留两条互相冲突的“有效决定”,冷启动的 Agent 只能随机选一条。长期记忆需要谱系:当前规则是什么、它替代了什么、为什么替代。
状态应有唯一的机器来源
需求阶段可以由 workflow-state.json 或同类状态文件表达,例如:
需求讨论 → 方案选择 → 实现就绪 → 开发 → 验收 → 提交
这条线性状态机只是最小示例。复杂任务出现并行、条件分支、汇聚和回退时,可以把流程扩展为任务图,让依赖、触发、回退和审批成为显式关系;但无论流程形态如何,当前状态仍应由唯一、可审计的机器来源表达。
状态文件负责告诉脚本和 Hook 当前允许发生什么;对应的 Markdown 文档负责让人理解为什么推进。阶段切换最好保留用户确认原话,而不是只写一个 confirmed: true。这样,“用户已确认”是一条可审计的证据链,而不是 Agent 自己生成的布尔值。
一个最小可用的 AI 原生仓库骨架可以是:
my-project/
├── AGENTS.md # 总规则、项目地图、命令和门禁索引
├── workflow-state.json # 当前阶段的唯一机器状态源
├── workflow/ # 本轮需求和方案的确认过程
├── specs/ # 长期有效的契约与验收标准
├── tasks/ # 当前可执行任务
├── memory/
│ ├── decisions.md
│ └── adr/
├── rules/ # 按测试、安全、Git 等主题分片
├── .agents/
│ ├── skills/
│ ├── hooks/
│ └── mcp.json
├── frontend/ # 业务代码,内部结构沿用项目既有规范
├── backend/
├── tests/ # 契约、集成和端到端验证
└── scripts/ # 状态、契约和统一检查入口
这不是要求所有项目复制同一棵目录树。目录多不多不重要,重要的是:新会话只读仓库,就能回答“现在进行到哪、为什么这么做、接下来该做什么”。
三、把设计和需求做成可执行事实
仓库提供了工作环境,下一步是让需求成为 Agent 可以比较和验证的目标。
对于界面型产品,第一步不应是让 Coding Agent 直接写正式代码,而是先做一份可运行的 UI/UX 原型。原型交付的不只是截图,还应该尽量包含:
- HTML,用来表达页面和交互关系;
- CSS,用来固化颜色、字号、间距和尺寸规范;
- React 等组件代码,用来表达界面拆分方式;
- 模拟数据,用来表达页面依赖的数据结构;
- 权限、空状态、禁用状态和异常反馈等关键业务状态。
业务规则如果只写在长篇说明里,Agent 很容易遗漏或产生多种解释。 能够通过模拟数据和交互状态表达的规则,尽量直接放进原型;文字负责边界和例外,原型负责让主要规则可以被看见、被操作。
此时设计稿不再只是“参考图”,而是实现的直接参照物。 Coding Agent 面对的是一套可运行、可截图比较、可由人实际操作的目标。
需求评估技能适合通过连续追问和多模型讨论把问题想透,但生成的访谈记录和方案推演往往很长,很难直接交给人阅读或交给 Agent 实现。因此,PRD 在保留完整过程材料之外,还应附一份极简简报,只回答三个问题:产品解决什么问题、这一版做什么、暂时不做什么。简报是需求收敛后的确认文档,应放进仓库,让没有参与讨论的人也能快速理解并复述最终结论。
四、第一版要足够小,而且必须能运行
原型确认后,再把功能交给 Coding Agent 实现。第一版只做一个很小的可运行切片,例如一个主界面和一条核心交互,而不是一次生成十个页面。
功能越少,Agent 同时理解的状态、依赖和异常分支越少,结果通常越稳定。 一次生成十个页面,看起来节省了十轮沟通,实际上会把十组错误叠在一起,让人无法判断问题来自需求、设计、架构还是实现。
每个版本至少满足三个条件:
- 可以独立运行。 人能亲手操作,而不是只看代码或截图;
- 只增加一种主要不确定性。 新页面、真实数据接入和架构调整尽量不要同时发生;
- 有明确验收边界。 做到了什么、哪些能力暂时不做,都写进当前规格。
这条流程表面上比“一次生成完整产品”慢,实际上压缩了每轮的不确定性。问题更容易定位,失败更容易回退,Agent 也不会在错误方向上高速堆积实现。
五、问题要回到产生它的那一层
人在运行版本时,通常会发现两类问题:
- 实现问题 :崩溃、状态错误、性能异常,或者代码没有遵守已经确认的设计;
- 设计问题 :信息层级不清、交互不自然、布局在真实数据下失效。
实现问题直接在代码层修复。设计问题则先回到设计工具修改原型,再让 Agent 比较设计稿变化并更新实现。
为了省事直接在正式代码里修设计问题,会让设计和产品逐渐分叉:设计稿展示旧产品,代码里堆着只有当前维护者知道的例外。后续每一轮迭代都要为这份分叉付费。
设计问题改设计,规格问题改规格,实现问题改代码。前面改对了,后面再同步跟进。不要想通过在代码实现层打补丁的方式解决所有问题。
这也是 workflow/ 与 specs/ 分开的实际意义:讨论过程解释决策,长期规格约束实现,代码负责兑现规格。三层各自承担不同责任。
六、用硬门禁控制 Agent 的推进速度
Coding Agent 最危险的时候往往不是做得慢,而是推进得太快。 它可以在几分钟内完成实现、测试、重构和下一阶段拆解,而人还没有判断上一阶段的方向是否正确。
仅在提示词里写“请等待确认”并不稳定。 阶段推进应该同时依赖三类证据:
- 对应文档已经存在,内容达到当前阶段要求;
- 状态文件允许进入下一阶段;
- 需要人工判断的节点保留了用户确认原话。
Rules 告诉 Agent 不应该越界,Hook 和脚本让越界动作无法通过。比如需求没有确认就不生成实现计划,方案没有选定就不拆任务,验收没有通过就不提交或发布。
门禁也保护人的注意力。人不必追踪 Agent 的每个动作,只需要在需求、设计、可运行版本和最终验收几个关键节点做判断。Agent 负责高速探索和执行,人负责方向、边界和放行。
七、用反馈回路代替全面逐行审核
当 AI 写得比人读得快,质量控制的重点要从“我是否理解每一行”转向两个问题:
- 系统是否满足已经确认的规格;
- 失败时能否及时暴露、定位和回退。
有效的测试必须回答两个问题:这次实现是否符合规格,有没有破坏原有功能? 一个更稳的流程是:
Spec → Task → 可观察验收条件 → AI Coding → Run & Check
↑ │
└── Fix & Retry ┘
是否先写测试,应由风险和行为稳定程度决定。AI 很容易快速生成只覆盖 happy path、断言又很弱的自证式测试,因此不必教条地追求所有代码都测试先行。但每一项关键行为都必须有与风险相称的验证:
- 复杂、稳定的核心逻辑 :用单元测试保护边界、状态转换和关键不变量;
- 模块与真实依赖的协作 :用集成测试验证数据库、服务和外部接口;
- 前后端共享契约 :用契约测试检查 Schema 和字段一致性;
- 关键用户路径 :用 E2E 或浏览器自动化运行真实流程;
- 界面和体验问题 :由人实际操作,结合截图和可观察状态验收;
- 测试本身的可信度 :必要时用变异测试、故障注入或真实缺陷反证弱断言。
单元测试、集成测试和 E2E 不是只能选择一种。项目越接近核心业务、逻辑越复杂,越需要核心单测;越是跨模块、CRUD 和页面密集场景,越应该提高集成、契约和 E2E 的比重。测试结构应该跟随风险,不应该为了倒置或遵守某种金字塔而倒置。
静态检查、类型检查和测试输出还要让 Agent 自己看见。反馈越短、错误越具体,它在当前会话内闭环的问题越多。关键用户路径必须真的运行过,不能只因为代码编译或测试文件存在,就推断功能已经可用。
测试跑完也不代表工作结束。Agent 应该生成可复查的报告,并清除测试产生的账户、文件和数据库记录。普通规格和决策继续使用 Markdown;需要展示趋势、分布和多维指标的测试或分析报告,可以使用 HTML 和图表。格式不是重点,重点是结果可审计、环境可恢复。
在资源有限的本地环境里,完整浏览器回归和重型设计工具同时运行可能影响反馈速度。可以在普通迭代里使用浏览器插件或轻量交互验证,把完整 Playwright 回归留给关键节点。工具可以替换,真实用户路径不能省略。
八、稳定之后,再做性能审查和结构调整
功能通过验收后,不一定马上进入下一个需求。让 Agent 审查性能和结构,通常比继续堆功能更划算,但前提是已有可运行版本作为参照。
例如在一个 macOS 项目的第一版中,界面使用 NSScrollView。内容增多后内存明显上涨,性能分析建议改用 NSTableView。这类问题很难在静态设计稿里发现,必须先有真实数据和可运行版本,才能判断是否值得改变实现。
重构也要控制风险。对于边界明确但历史复杂的模块,与其原地拆除并重建,不如先新增一个实现,验证后再替换旧路径。这样可以比较新旧结果,失败时快速退回,也避免同时破坏现有行为和参照物。
但并行实现只是迁移手段。替换完成后必须删除旧路径,不能让两套实现永久共存。
每个有文件变化的实现任务还应该留下清晰边界:编辑前识别已有和并发变更,只包含当前任务相关修改,运行与改动相称的验证,再形成独立提交或等价的可回退记录。不要擅自推送、改写历史,也不要为只读任务创建空提交。
九、用“新会话六问”检验系统是否真的成立
目录存在不等于系统有效。 最直接的测试,是启动一个没有口头背景的新会话,让 Agent 只读仓库,然后回答六个问题:
- 项目是什么;
- 当前需求走到哪一步;
- 现在被允许做什么;
- 应该按什么流程做;
- 做完如何验证;
- 本轮经验应该写到哪里。
如果六个问题都能回答,说明事实、过程、状态和经验已经形成闭环。答不上来时,不要继续堆更多抽象目录,而要定位究竟缺少哪类信息:项目索引、当前状态、有效契约、验证入口,还是决策记录。
我在 travel-ledger-admin 项目中用这六问检查过一套类似骨架。项目地图、阶段状态、允许动作、默认流程和经验回写都能从仓库恢复,但验证仍是最弱的一环:契约检查没有机器兜底,根目录缺少完整的 E2E 链路,CI、可观察性和测试规则也不完整。
这个结果反而说明,最容易被省略的并不是目录和文档,而是“让结果真正可验证”的最后一公里。 状态机、规格和 Rules 可以阻止 Agent 胡乱推进,却不能证明产品已经工作。最终仍然要回到可运行版本、真实用户路径、可观察反馈和可恢复环境。
收个尾
AI 原生开发不是让 Agent 替人连续写更多代码,而是重新划分人、模型和仓库的责任:
- 仓库保存事实、过程、状态和经验;
- Agent 负责高速探索、实现、验证和修复;
- 人负责需求、设计、边界和阶段放行;
- 测试、Hook、状态机和提交记录提供可观察、可审计、可回退的反馈。
这套系统可以压缩成一句话:
把仓库变成 Agent 可以恢复上下文的工作环境,把目标做成可执行的事实,把功能拆成可运行的小版本,把问题送回正确的层,再用门禁和反馈回路控制推进。
只要上下文闭环和执行闭环同时成立,代码生成得再快,也不会快到失控。