AI 编程实战:从会话技巧到工程化工作流

写在前面

之前写过两篇工具指南:Claude Code 使用指南讲 CC 的命令、权限与配置,Codex 使用指南讲 Codex 的沙箱、审批与迁移差异。它们回答的是"这个工具怎么用";这篇文章回答另一个问题——工具会用之后,工作该怎么组织。

这段时间我把 AI 编程从"对话式补全"用到"带着多个 agent 做跨周的大任务",被迫沉淀了一套工作流。越用越清楚一件事:AI 的能力上限很高,但可靠性不会随能力免费到来,它需要一套工程化的兜底体系——就像你不会因为某个人很聪明,就把生产库密码交给他。

这套工作流的主线可以概括成三句话:

  • 让事实可查:AI 的每个关键论断,都能被一条命令或一份文档核实;
  • 让边界成文:做什么、不做什么,写在文档里而不是靠默契;
  • 让验证可复现:成功与否由命令输出和测试判定,不由 AI 的自述判定。

全文围绕这三句话展开:先认清失败模式(一),再搭工作台(二),然后把大任务拆成文档工程(三),建立审查与验证的闭环(四),进而做多 agent 分工(五),把经验沉淀为 skill 和自动化约束(六),第七节把全套流程串成可以照做的流水线,最后以一点反思收尾(八)。

适用读者:已经在日常使用 Claude Code、Codex 等 agent 工具,想从"小修小补"进阶到"把大任务托付出去"的开发者。文中以 Claude Code 为主线、Codex 为辅(主要作为交叉审查的另一方出场),概念可以迁移到其他 agent 工具。

一、认清失败模式:幻觉、越界修改与假验证

大多数 AI 编程的挫折,不是因为 AI 笨,而是因为它以三种可预测的方式出错。先认清失败模式,后面的技巧才有落点——所有防线都是修在这三个地方。

1.1 幻觉:一本正经地编造

表现:引用不存在的 API、配置项、参数或文件路径,给出看似合理实则拼凑的用法。幻觉不是偶发 bug,而是语言模型的固有属性——它在"预测合理的文本",不在"陈述事实"。

检测信号:

  • 引用了仓库里不存在的文件或符号(rg 一下就知道);
  • API 签名与官方文档对不上;
  • 语气笃定但给不出出处,或用"应该是支持的"这类含糊措辞。

处置:要求 AI 给出论断的出处——文件路径加行号,或官方文档链接;用发现命令当场核实。预防:改动前先做代码走读(见 3.2),让 AI 的论断锚定在真实代码上,而不是想象上。

1.2 越界修改:顺手多做了一点

表现:任务只要改一个函数,它顺手统一了整个文件的命名;或者顺手格式化整个文件、重排 import、动了没让它动的模块。AI 没有"这不在我的任务里"的本能,边界不清时,它的默认行为是自行补全假设。

检测信号:git diff --stat 里出现超出任务范围的文件;diff 里混着大量与任务无关的格式变更。

处置:先止损,再决定回滚还是修复(见 2.2)——暂停写入、检查 diff,保留已有人工改动;确认本轮改动可以整体丢弃后,再回到干净锚点,重新下一次边界更明确的任务。预防:在方案文档里写明非目标与禁区(见 3.4),用 worktree 分开任务工作目录(见 2.2)。

1.3 假验证:自称通过

表现:声称"测试已通过"“已验证可用”,但实际没跑、跑的是别的命令,或者选择性引用了输出。这不一定是有意欺骗——AI 可能把"应该会通过"的预期当成了事实陈述——但后果一样:你基于一个不存在的绿灯放行了代码。

检测信号:说不出执行了哪条命令、给不出原始输出;执行耗时与任务量不匹配;输出内容与代码现状矛盾。

处置:要求粘贴原始输出,自己复跑同一条命令。预防:验证脚本化(见 4.4),“通过"必须附带命令与输出。

1.4 一张速查表

失败模式检测信号处置预防
幻觉引用不存在的文件/API;语气笃定无出处要出处,用 rg 核实走读先行,论断锚定真实代码
越界修改--stat 超出任务范围;混入格式噪声先保留已有工作,再决定回滚范围非目标与禁区成文;worktree 分目录
假验证无原始输出;耗时异常;输出与现状矛盾复跑同一条命令验证一条命令化,以输出为准

三种失败模式有一个共同的工程问题:模型的回答本身不能证明事实正确、改动合规或验证成功。所以防线的设计思路是改造环境:事实可查(文档与走读)、边界成文(方案文档与隔离)、验证可复现(脚本与测试)。后面各章,就是把这三件事做扎实的具体做法。

二、搭建工作台:worktree、共享文档与会话管理

2.1 给 AI 配三层记忆

AI 的工作质量取决于它记得什么。按生命周期,我把它的记忆分成三层:

层载体生命周期适合放什么
会话记忆上下文窗口最短:compact 折叠、超限截断、新会话不继承当前任务的即时状态
项目记忆CLAUDE.md / AGENTS.md(入库)与仓库同寿,团队共享构建命令、代码约定、仓库礼仪
个人记忆本地共享文档目录(不入库)跨仓库、跨任务长期积累走读笔记、任务文档、个人经验库

前两层两篇指南里讲过,这里重点说第三层。总有一类知识不适合入库:个人笔记、带私有信息的走读、与具体仓库无关的方法论。但它们恰恰是 AI 最需要的背景。我的做法是:在所有仓库之外维护一个公共文档目录,通过 Windows Junction 以 docs-ref/ 的名字接进每个 worktree——本地留存、全局共享、Git 不感知。怎么搭见 2.3 和 2.4。

2.2 git worktree:一任务一分支一目录

多任务并行时,一个工作区只能 checkout 一个分支,几个 AI 任务会互相踩。git worktree 让同一仓库同时挂多个工作目录,各自在不同分支。下面的 Git 命令可在 PowerShell 或 Bash 中执行;先在已有提交的仓库根目录创建任务分支,要求分支名与目标目录尚未被占用:

1
2
git worktree add -b task3/grpc-refactor ../myproject-task3
git worktree list

与 AI 协作时,这套结构特别合身:

  • 一会话一 worktree:每个任务有独立的工作文件、索引和 HEAD,减少直接覆盖彼此改动的机会;但 worktree 不是权限沙箱,agent 仍可能通过路径访问其他目录,分支引用、对象库及部分配置也由各 worktree 共享;
  • 故障范围更清楚:先检查本任务的改动,再决定修复还是从已知提交重新开始。共享文档、数据库和外部服务仍可能受其他任务影响;
  • 为回滚保留锚点:每个验证过的小步保存为提交。git reset --hard 会丢弃已跟踪文件的未提交改动,还可能覆盖挡路的未跟踪文件;无目标参数时只是重置到当前 HEAD,不会自动寻找上一个正确版本。执行前必须核对目录、目标提交和保留范围;混有人工改动时,应先单独保存或只撤销明确属于本任务的部分。

任务完成、需保留的内容已保存后,在主工作目录执行 git worktree remove ../myproject-task3 清理入口。它通常拒绝移除含未提交改动或未跟踪文件的 worktree;不要用强制删除掩盖尚未确认的内容。忽略文件也要单独检查,不能把 Git 状态干净等同于目录里没有需保留的数据。

2.3 共享文档目录:内容一份,入口各建

结构:公共文档目录放在仓库外(比如 D:\docs),每个 worktree 里放一个名为 docs-ref 的 junction 指向它。所有任务共享同一份文档,零复制。

创建 junction 前,确认目标目录 D:\docs 已存在、当前用户有相应访问权限,且 worktree 根目录下尚无 docs-ref 入口。下面二选一:第一条在 PowerShell 执行,第二条在 CMD 执行。

1
New-Item -ItemType Junction -Path ".\docs-ref" -Target "D:\docs"
1
mklink /J ".\docs-ref" "D:\docs"

选 junction 而不是符号链接,是因为它不需要管理员权限或开发者模式,且只能指向本地卷,行为可预期。

然后让 Git 忽略它。先用这条命令定位当前实际生效的 exclude 文件:

1
git rev-parse --git-path info/exclude

在其中加入两行:

1
2
docs-ref/
.ignore

这里有个实测细节:exclude 文件是各 worktree 共用的——在 linked worktree 里执行上面那条命令,解析到的仍是主仓库的 .git/info/exclude(git 2.55 实测;以你机器上这条命令的输出为准)。所以 exclude 只需要配一次,全部 worktree 生效。但 junction 本身是工作目录里的文件,不会自动出现在其他 worktree——每个新 worktree 都要各建一次 junction(以及 2.4 的 .ignore)。

2.4 让 AI 读到被 Git 忽略的文件

docs-ref/ 被 Git 忽略后,文件选择器可能不再列出其中的文件。要区分三件事:Git 是否跟踪、搜索是否列出、工具是否有权读取。下面讨论搜索可见性,不把忽略规则当作访问控制。

Claude Code:设置选择器的 respectGitignore。当前官方设置参考支持把该键写入用户、项目、项目本地或托管设置。只想影响当前项目时,可以在 .claude/settings.local.json 中合并下面的键,不要覆盖文件内已有的其他配置:

1
2
3
{
  "respectGitignore": false
}

终端里的 /config 也提供 Respect .gitignore in file picker 开关,它写入 ~/.claude.json;当前版本只有在设置文件未指定此键时才使用该回退值。旧版本的能力和优先级可能不同,以所用版本的设置参考与实际候选结果为准。

这个开关只影响 @ 选择器的候选列表,不是访问控制:被忽略的文件在权限允许时仍可通过明确路径读取。关闭过滤可能让 .env 等敏感文件进入候选;不再需要时恢复 true。配置后仍应实际搜索目标文档,目录链接能否被遍历是另一项条件。

Codex 侧的候选办法:.ignore 反向规则(按路径)。对使用 ripgrep 的搜索,.ignore 可重新包含被 Git 忽略的路径;ripgrep 的优先级为 .rgignore > .ignore > Git 忽略规则。但这不能证明 Codex CLI、IDE 与桌面端的 @ 选择器行为相同。可以在 worktree 根目录的 .ignore 中追加下面两行,保留已有规则,再验证当前客户端是否列出目标文件:

1
2
!/docs-ref/
!/docs-ref/**

Git 不读取 .ignore,所以原有 Git 忽略规则保持生效。可先在 worktree 根目录执行 rg --files --hidden --follow docs-ref,检查输出中是否出现预期文档;--follow 用于遍历链接目录,忽略规则本身不会开启链接遍历。这只能验证该条 ripgrep 命令,随后仍要在 Codex 的 @ 选择器中单独验证。若选择器不支持,直接提供明确的文件路径,请 agent 在权限允许时读取,不要把命令行搜索成功当成客户端验证成功。

两种方案的取舍:

维度CC respectGitignore.ignore 反向规则(须验证客户端)
生效范围当前支持按设置文件选作用域;/config 写全局回退值所在目录及子目录,依赖搜索实现
粒度所选作用域内统一切换按路径精准
安全面敏感文件可能进候选只放开明确列出的目录
维护成本检查作用域与设置优先级本文采用不入库配置,每个新 worktree 建一次

我的用法:优先按路径放开所需文档,客户端不支持时使用明确路径。CC 侧优先限制到所需项目,仍要留意候选范围。敏感文件应移出共享资料目录,并由工具权限与文件系统权限限制访问;不出现在候选中,并不代表 agent 无法读取。

2.5 会话命名:把会话当任务索引

会话是最小的工作单元,也是最容易失控的东西——并行任务一多,/resume 列表里全是分辨不出归属的会话。命令层面两篇指南都讲过(CC 的 /rename 与 claude -n,Codex 的 /rename 与 codex resume),这里只补一条命名约定:

1
{分类号}-{主题}-{性质}

例如 3-gRPC改造-方案讨论、3-gRPC改造-清单执行。它和 3.3 的文档编号共用一套分类号,效果是:会话、文档、worktree、分支四者同名互查——“那个 gRPC 任务的方案会话"不需要回忆,按分类号就能定位。文件名里的 任务N- 只是人类可读标识,不要求与分类号相同。像对待分支一样对待会话:一个会话一个用途,用完归档,不把新话题混进旧会话。

三、大任务的文档工程

单人用 AI 做小任务,对话就是全部;任务一大,对话就不够了——上下文装不下,人记不住,AI 更记不住。这一章是全文的核心:用一套文档体系,把"一个大任务"变成"一串可独立托付的小任务”。

3.1 为什么必须拆

  • 上下文有限:一个跨周的大任务塞不进一个会话;就算塞得进去,经验上长上下文里的指令遵循也会变差,早期的约束到后期就"忘了”;
  • 失败模式被放大:任务越大,幻觉与越界修改的爆炸半径越大,审查也越难——没人审得动一个三千行的 diff;
  • 可验证性:拆小之后,每一份产出都能被独立验证、独立回滚。

拆的粒度用一个标准卡:一个任务 = 一次可验证的交付。写不清验收标准的,说明还拆得不够小。

拆完之后自然形成三层:专题 → 任务 → 清单项。专题是一段时间要完成的一个目标,由几个相关的任务组成;任务就是上面的可验证交付;清单项是任务内的执行步骤。三层的目录与命名规则见 3.5。

3.2 文档三件套:代码走读、实现方案、任务清单

一个专题到任务的完整链路包含三类文档,各管一段。它们是工作流角色,不要求每个任务各复制一套,也不要求物理上放在同一层目录:

代码走读(现状):让 AI 系统性地读代码,产出"这个系统怎么工作"的结构化笔记——模块划分、关键类型、数据流、约定与坑。走读是所有后续工作的地基:它让 AI 的论断锚定在真实代码上(防幻觉),也让人确认"AI 理解的系统"和"实际的系统"是同一个。改既有代码时,明确要求新代码沿用走读确认过的既有风格,而不是它自带的默认品味。重量级专题的走读放在专题级共享;跨专题复用的走读再按主题沉淀进 common/,不在每个任务目录重复复制。

实现方案(设计):改什么、怎么改、分几步、每步的风险与验证方式。AI 起草、人审查拍板(见 4.1)。方案归属具体任务,是任务期间最高权威的文档,后续所有会话以它为准。

任务清单(执行):把方案拆成可勾选的步骤列表,一步一项,写明验证方式,并给每项一个 TODO 编号作为引用键(见 3.6)。每个任务一份独立清单,它是跨会话的进度账本:新会话接手时先读清单,做完一步勾一步;多 agent 协作时它还是共享状态(见第五章)。

三者的关系是一条流水线:走读喂方案,方案拆清单,清单驱动会话。文档在这里的角色,是 AI 的外置记忆——会话会丢,文档不会。

3.3 编号与命名:文件名自带顺序和语义

文档一多,“哪个任务、哪份文档"就成了日常检索。我用一套固定的文件名结构:

1
{分类号}.{序号}.{任务名}-{文档类型}.md

例如:

1
2
3
3.01.任务1-gRPC改造-走读.md
3.02.任务1-gRPC改造-实现方案.md
3.03.任务1-gRPC改造-清单.md

规则只有三条:分类内的序号零填充,让同一分类的文件按字典序排列;任务名人类可读,内容可变——名字里的数字(如"任务1”)只是可读标识,不必与分类号对应;类型后缀区分角色——走读、实现方案、清单、审查是最低词表,实际按需生长:范围清单、技术决策、基线、新旧对比、复审、检查表、操作指南、缺陷与风险登记……词表是开放的,约束只有一条:同一类文档始终用同一个类型词。重量级专题里,分类号对应专题内的阶段或任务目录;轻量专题平铺时,分类号先表示专题本级文档,其后按任务分组,具体见 3.5。若分类号达到两位数,也应统一宽度,例如 03.01,避免字典序把 10 排在 2 前面。结构固定、内容随意——它不是什么规范,只是一个用了就回不去的约定。

编号之外再补一条引用规则:正文互引用《文档标题》,不引用文件名。标题稳定,文件名可能调整;引用与文件名解耦,改名就不破坏引用——对人,索引是稳定的;对 AI,上下文里的锚点也是稳定的。

3.4 任务文档模板:必备章节

方案文档我固定用一套章节骨架:

1
2
3
4
5
6
7
8
9
# {任务名}

## 背景与目标
## 术语说明
## 现状摘要
## 实施方案
## 非目标与禁区
## 验收标准
## 风险与回滚

每个章节都有存在的理由:

  • 背景与目标:为什么做、做成什么样。AI 知道目标,才谈得上取舍;
  • 术语说明:统一叫法。领域词汇的歧义是方案跑偏的隐形原因——同一件事两种叫法,AI 就当成两件事;
  • 现状摘要:引用走读结论,不重复铺陈,控制上下文成本;
  • 非目标与禁区:不做什么、不许动哪些文件和模块。这是最重要的一章:越界修改的根源,就是方案文档只写了"做什么"。AI 对边界靠推断补全,写明禁区是最便宜的预防;
  • 验收标准:怎么算完成,尽量写成可执行的命令或测试(衔接 4.4 与 4.5);
  • 风险与回滚:出问题怎么退回来(衔接 2.2)。

再配一个开工仪式:让 AI 复述任务与边界——用自己的话重述目标、非目标、验收标准,然后你再纠正。理解偏差在第一分钟暴露,成本近乎为零;等到审查阶段才发现,返工的是一个会话的产出。

复述之外,还可以主动考它:针对刚读过的代码或方案提几个具体的问题——这个应答超时之后会发生什么?这个字段由谁负责更新?——回答应当附代码位置或可复现实验,再由人抽查。答错能直接暴露走读或方案的漏洞;答对只是理解的证据之一,不能替代后续验证。

3.5 多任务并行时的文档组织

任务多了以后,目录按三级组织:顶层按模块或域分(如 backside/、edgeside/、common/,common/ 放跨模块的背景知识与手册);域内按专题分目录,专题用 yyyymmdd. 日期前缀——时间是专题的天然排序键,日期即启动日,不需要维护发号器;专题内部再组织任务,内部文档一律不带专题前缀,层级由目录树承担,号段只管局部顺序。一棵真实项目的树:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
docs/
├── backside/                        # 域:按模块/子系统分
│   ├── 20260803.架构升级改造/        # 专题:日期前缀,重量级
│   │   ├── README.md                # 专题索引:目录表 + 每篇一句话 + 阅读顺序
│   │   ├── 0.统筹/                  # 任务拆分、依赖、进度、开放决策——读文档先看这里
│   │   ├── 1.现状基线/              # as-is 盘点:架构、技术债、外部边界——改造前先读
│   │   ├── 2.代码走读/              # 专题内共享走读,按主题分篇
│   │   ├── 3.任务1-gRPC改造/
│   │   │   ├── 3.01.任务1-gRPC改造-实现方案.md
│   │   │   ├── 3.02.任务1-gRPC改造-范围清单.md
│   │   │   ├── 3.03.任务1-gRPC改造-任务清单.md
│   │   │   ├── 方案审查/   代码审查/   images/
│   │   └── 4.任务2-轮询改事件/
│   └── 20260920.重写检测流程/        # 轻量专题:平铺分组,不建任务目录
│       ├── 0.01.检测流程改造总览.md  # 0 总览、1 现状在前,其后一个号段 = 一个任务
│       ├── 1.01.现状问题与目标.md
│       ├── 2.01.任务1-圆心来源改造-实现方案.md
│       ├── 2.02.任务1-圆心来源改造-任务清单.md
│       ├── 3.01.任务2-检测顺序改造-实现方案.md
│       └── 3.02.任务2-检测顺序改造-任务清单.md
└── common/
    └── 设备背景知识手册.md           # 跨模块知识库

六个要点:

  • 号段有分工:重量级专题里,0.统筹、1.现状基线、2.代码走读 是专题本级——统筹是总控账本,现状基线回答"问题是什么",走读回答"机制怎么转",三者各有职责不混写;3 起才是任务目录,文件名 <分类号>.<序号>.任务N-<主题>-<类型>.md。跨专题可复用的走读沉淀进 common/;
  • 结构随规模伸缩:专题小就平铺——专题本级文档(总览、现状)占前几个号段,其后一个号段 = 一个任务,任务的实现方案、任务清单等文档聚在自己的号段内,不建目录。别为三个文档搭八层楼。平铺与目录是同一分组思想的两个粒度:专题长大后,2. 号段原地升级为 2.任务1/ 目录,文件名一个不用改;
  • README 是专题的入口:目录结构表、命名规则、每篇一句话摘要、推荐阅读顺序(统筹→基线→走读→任务)。给 AI 的应该是一张地图,不是一摞手册——新会话先读 README,再按需下钻;
  • 任务目录的根只放三份核心文档:在上例中是实现方案、范围清单、任务清单;专题级共享走读留在 2.代码走读/,不为每个任务重复复制。这三份是脊柱文档,编号连续、常读常改,扫一眼根目录就该能看清任务全貌。审查记录按轮次越积越多,图片是二进制附件,混进根目录会淹没编号的可读性——所以任务目录内固定三个子目录 方案审查/、代码审查/、images/,开工建任务目录时(第七节阶段 1)一并创建。子目录里的文件沿用任务分类号;方案审查与代码审查共用任务内的文档序号和审查轮次序列,不按子目录重新从 01 或 R01 起号;图片按内容描述性命名,不发编号(同 3.6 的发号原则);
  • 资料跟着任务走:厂商 SDK、手册、多版本 DLL 放进消费它的任务目录(必要时再分新版本/老版本);测试日志也归档进任务目录,作为执行证据留存(衔接 4.4);
  • 喂给会话的文档只有两类:本任务的目录 + 用得到的走读。上下文最小化,检索沿目录树下钻。

这棵树加上第二章的 worktree,多任务并行就各有各的领地。

3.6 给会被引用的实体发 ID

文件名解决了文档的检索,但还有一类东西也需要被稳定引用:缺陷、清单项、审查意见——它们出现在对话里、提交信息里、审查回归里。口头说"那个超时的 bug",每次都要重新对齐;给它们发 ID,引用就有了稳定的键。

两层结构:

层格式适用例子
自描述 ID大写缩写 + 连字符 + 零填充序号需要脱离当前文档识别;具体作用域由类型决定BUG-012、TODO-012、DEC-012
局部极简 ID单字母 + 零填充序号作用域已限定的任务或审查记录内意见 F01、轮次 R01

各实体的约定:

  • 缺陷 BUG-012:项目级流水。若项目已有 GitHub Issues、Jira 等任务系统,直接沿用它生成的编号,不再维护第二套 BUG 流水;
  • 清单项 TODO-012:任务级流水,直接写进清单——- [ ] TODO-012 实现 ACK 重试。做完勾选,编号不变;跨任务引用时写 任务3/TODO-012;
  • 审查轮次 R01、R02:按发生顺序编号——3.04.任务1-gRPC改造-方案审查R01.md。编号只表示先后,不把某个序号固定解释成首审、二审或回归;
  • 审查意见 F01、F02:单轮文档内的条目;跨轮引用组合成 R01-F03——“R01-F03 在 R02 确认已修”。从第一条起就固定宽度,避免条目增加后重编号;
  • 决策记录 DEC-012(可选):记录"当时为什么选这个方案"及适用背景。决策失效时追加新记录并关联旧记录,不改写已经被历史引用的 ID。

配套两条原则:

  • 前缀和作用域共同构成命名空间:BUG 按项目编号,TODO 按任务编号,DEC 按约定的决策库编号,F 只在所属轮次内编号,R 只在所属任务内编号。想加新缩写,先写清作用域与唯一发号源;
  • 发号有门槛:只给会被跨文档、跨会话、跨提交引用的实体发 ID,只在文档内部使用的列表用普通有序列表。口诀:流程性实体发号,知识性实体起名(common/ 走读用主题词命名,不编号);被引用才发号,只被阅读不发号。

最后是提交回链:编号的价值一半在发出去,一半在提交信息里——fix BUG-012、fix 任务3/R01-F03、完成 任务3/TODO-012。提交格式仍以仓库约定为准;把作用域一起写上,Git 历史才能成为可靠索引。

四、质量闭环:审查与验证

4.1 方案审查到底审什么

方案是 AI 起草的,但方案审查是人的主场——判断力和品味不能外包。审三层,由浅入深:

  • 命名与概念:新名词是否贴合领域、与既有代码一致。命名歪了,后面所有代码跟着歪,纠错成本随时间上升;
  • 项目结构:改动落在哪个项目、哪一层;依赖方向对不对;是复用了既有抽象,还是在旁边又造了一个;
  • 可行性:方案依赖的关键假设是否成立——真有这个 API?性能量级估过吗?跨平台行为确认过吗?可行性问题在方案阶段发现是改文档,在编码后发现是返工一轮。

审查的输入永远是"方案文档 + 相关走读",不是只看 AI 的一句话总结。

4.2 双模型审查,并且要回归

写和审可以用不同模型。我的常态是 Claude 写、GLM 审,或者反过来;实际使用中,这有时能带来不同视角。但不同模型也会共享盲区,不能保证相互纠错。审查方应独立阅读需求与代码,用文档、源码或实验裁决分歧,不能把两次赞同当成正确性的证明。

审查要输入完整:审查方必须拿到方案文档和完整 diff。知道"意图"才能审出"偏差",脱离意图的审查只能挑表面毛病。

要回归(regression):审查意见修改后,必须让审查方对修改后的版本再确认一次。“改了"不等于"改对了”——修改本身可能引入新问题,也可能改丢了原来的意图。没有回归环节的审查流程,最后一轮意见是没人背书的。轮次与意见可以用 3.6 的记号编号(如"R01-F03 已修,待 R02 回归"),回归对账就有了稳定的引用键。

顺带一提,你正在读的这篇文章就是这么生产的:写作流程定义在 skill 里,两位审阅者读同一版本,任何一方改了正文,另一方就要重新确认——写代码和写文章,质量闭环是同构的。

4.3 diff 纪律

审查代码时的操作顺序,固定成纪律:

  1. 先看 git status --short,再看统计:用状态检查新增与暂存文件,再分别看 git diff --stat 和 git diff --cached --stat。单独的 git diff --stat 只覆盖未暂存的已跟踪改动,会漏掉已暂存变更和未跟踪文件;
  2. 再看分文件 diff:先核心逻辑,后配套变更;分别检查 git diff 与 git diff --cached,未跟踪的新文件直接阅读全文。如果任务已产生提交,还要比较约定的基线与任务分支,不能只检查工作区;
  3. 对大重写保持警惕:AI 偶尔会重写整个文件,而不是打一个精准 patch——内容可能等价,但审查负担完全不同,应当要求它改用最小改动方式重做。

两条配套规则:最小改动原则——只做任务内的变更;禁止顺手格式化——格式化、import 重排、大规模重命名会制造噪声,把真实改动淹掉,审查能力直接归零。格式问题交给格式化工具,单独一次提交。

4.4 验证可复现:一条命令,以输出为准

把 build、test、lint 收进一条命令——make verify、just verify 或一个 verify.ps1,人和 AI 跑同一条。然后是两条铁律:

  • “验证通过"必须附执行证据:命令、工作目录、对应代码版本、退出码及关键输出;长日志保存到可查看的文件或 CI 任务,不能只截取成功片段;
  • 核对测试是否真的运行:退出码为零还不够,要检查用例数量、跳过项和环境。优先看工具执行记录或 CI 原始日志,并不定期自己复跑;AI 在聊天中粘贴的所谓原始输出仍可能不真实。

这也是 1.3 的制度化解法:把"验证"从对话里的一个说法,变成可复现的机器事实。

4.5 从提示词走向测试

前面所有手段解决的是"怎么让 AI 不出错”,这一节换个角度:怎么让"对"的定义本身离开自然语言。

对能自动验证的需求,把人工确认的验收标准写成测试,能减少反复口述的歧义。但测试通过只证明被执行的断言满足了:漏测边界、错误的预期、过度 mock,都会让错误实现变绿;AI 也可能改弱断言、删除或跳过用例。因此,测试本身要对照需求审查,测试的变更与代码的变更享受同等的审查待遇(见 4.3)。

可以采用测试驱动开发(Test-Driven Development,TDD)的节奏:先把确认过的验收标准翻译成测试,确认它因目标行为尚未实现而失败,再实现到通过,最后在测试保护下重构。对接口已经稳定、兼容性要求明确的任务,还可以先确认对外契约,再围绕契约分批补测试与实现;探索性设计则保留从测试反馈中调整接口的空间。AI 可以降低编写测试的成本,但不会自动解决测试设计、遗留系统可测性或维护成本的问题。

边界也要说清:不是所有验收标准都适合完全自动化。视觉回归和架构规则可以覆盖部分 UI 与结构要求,观感、命名和设计取舍仍需要人工判断。测试与审查互补,审查也必须检查行为是否正确。日常沟通仍然重要;适合自动化的核心需求,则尽量沉淀为可执行的规格。

五、多 Agent 分工:写、审、监控

单会话流程跑顺之后,自然的下一步是并行:任务大到一定程度,一个会话串行做不完,就拆给多个 agent。

5.1 三个角色

  • 写码 agent:按任务清单实现,一个任务一个;
  • 审查 agent:对每个完成的步骤,对照方案审 diff;
  • 监控 agent:盯清单进度、汇总状态,发现卡住或跑偏时向人报告。

5.2 模型搭配的原则

  • 写和审尝试不同模型——理由同 4.2,增加视角,但不保证消除盲区;
  • 按难度分配审查与监控——机械进度汇总可以先用成本较低的模型;涉及并发、安全或架构的审查需要足够的推理能力,不能只因它是读取任务就降档。

背后是一个实用原则:按风险和实测效果分配模型。把返工、漏检和人工复核的成本一起计算,再判断便宜模型是否真的省钱。

5.3 让分工成立的前提

纸面上画三个框不难,让它真的工作需要四件事:

  1. worktree 分目录,权限限制写入范围:每个写码 agent 各占一个 worktree;共享资料仍是同一份文件,应明确只读范围与编辑负责人;
  2. 清单统一记账,更新串行化:每个任务的清单由一个负责人更新,其他 agent 提交结果;若必须多方写入,使用带锁或版本冲突检测的任务系统,避免同时覆盖 Markdown。每个完成项关联代码版本和验证记录,勾选本身不构成完成证明;
  3. 审查 agent 必须读方案文档:知道意图才能审出偏差(同 4.2);
  4. 人握着合并权:方案拍板、分歧裁决、最终合并,三个 agent 都不碰主分支。

最后一句实话:多 agent 不是银弹,协调成本真实存在——清单对账、上下文同步、审查排队都是开销。先把单会话流程跑顺(前三章),再逐步并行化;顺序反过来,通常收获一堆混乱。

六、沉淀为基础设施:skill、自动化约束与经验回流

6.1 项目专用 skill:流程写一次,处处调用

识别信号很简单:同类任务第三次出现。发版流程、走读模板、审查清单……凡是每次都要口头交代一遍的流程,都值得写成项目专用 skill——步骤、检查项、输出格式固化成文件,新会话零上下文继承也能按流程走。skill 是三层记忆之外更高一级的形态:不是帮 AI 记住,而是把做法本身固化。比如把 3.4 的文档模板做成 skill,“新任务"一句话就能起全套文档。

6.2 脚本与自动化约束:不靠自觉靠机制

在 CLAUDE.md 里写提交前跑检查,是给模型的指令;把检查接入能阻断操作的 hook 或必需 CI 检查,才能形成可执行门槛。以 Claude Code 为例,PreToolUse 命令钩子可用退出码 2 阻止工具调用,普通非零退出码未必阻断;只打印提醒也不等于拦截,必须实际验证失败路径。

权限 deny 适合拦截已覆盖的调用形式,但命令文本匹配不是完整的安全边界:git push --force 还可能写成 git push -f,或由脚本间接调用。重要限制应结合沙箱、操作系统权限、远端受保护分支和必需检查;本地 Git hook 也可能被跳过。机制能减少越界与漏跑检查,却不能独自判断需求是否正确、测试是否充分。

6.3 经验回流:让踩过的坑只踩一次

新会话不会自动拥有上一会话的完整上下文;Claude Code 的 auto memory 等功能可以保存部分经验,但不能保证所有教训都被准确提取和重新加载。因此,关键规则仍要写成可审查的项目文档或 skill。任务收尾可以固定问一句:

这个任务里学到了什么,是值得写进 CLAUDE.md、skill 或文档模板的?

把答案真写进去。久而久之,CLAUDE.md 会从"仓库说明书"长成"经验库”,skill 从"流程"长成"标准做法"。这本质上是给个人知识做持续集成:知识也要 CI,不回流就腐烂。

七、串起来:一个任务的完整流程

前面六章是零件,这一节把它们装配成一条可以照着做的流水线。先补一条贯穿全程的会话纪律:

对跨天、跨会话的大任务,我默认采用一种工作类型一个会话,换类型就换会话。走读、方案、执行、审查尽量不挤在同一条会话里;同一次审查及其回归可以留在原审查会话中,便于逐项对账。理由有三:

  • 上下文干净:每个阶段的会话只装载该阶段需要的材料——走读会话读代码,方案会话读走读,执行会话读清单。前序阶段的探索、试错和被废弃的方案,不会作为噪声留在后续阶段的上下文里;
  • 状态主要从文档继承:新会话的第一件事是读文档(走读/方案/清单),必要时再追溯旧会话。文档是筛选过的结论,闲聊是未过滤的过程;
  • 上下文故障隔离:执行会话跑偏,不会把试错过程带进方案讨论。重开会话只能清理上下文;已经产生的文件、提交和外部副作用,仍要按 2.2 的边界检查和恢复。

会话名要认真起——它不是标签,是跨会话的检索索引(见 2.5)。阶段正好落在命名约定的"性质"字段里:3-gRPC改造-走读、3-gRPC改造-方案、3-gRPC改造-执行、3-gRPC改造-审查。命名后的会话列表可以辅助定位阶段;任务清单仍是进度的唯一账本。

完整流程如下(单人、多会话形态;多 agent 扩展见第五章):

#阶段会话(性质字段)输入产出关键纪律
1准备—(主工作区)任务描述worktree、分类号、任务目录一任务一分支一目录(2.2);任务目录及子目录见 3.5
2走读…-走读代码库走读笔记;可复用部分沉淀进 common/论断锚定真实代码;已有走读先复用(3.2)
3方案…-方案走读 + 任务描述方案文档按 3.4 模板起草,非目标与禁区必填
4方案审查…-审查方案 + 走读方案审查/ 中的审查记录、修订后的方案人主审(4.1);修订后要再确认
5拆清单…-方案 续 或新会话定稿方案任务清单一步一项,写明验证方式(3.2)
6执行…-执行清单 + 方案代码、检查点、执行证据逐步做、逐步验证;按仓库规则和授权保存提交(4.4、2.2)
7代码审查…-审查diff + 方案代码审查/ 中的审查记录status → 统计 → 分文件(4.3)
8回归审查会话续修订后的 diff确认结论“改了"不等于"改对了”(4.2)
9合并清理人 + 主工作区任务分支合并、worktree 清理合并权始终在人(5.3)
10复盘回流…-复盘全程记录CLAUDE.md / skill 更新知识也要 CI(6.3)

四条使用说明:

  • 循环在 6-8:执行、审查、回归转圈,直到清单清空,不是线性走一遍就完;
  • 阶段 4 与 7 独立阅读完整输入;条件允许时换模型,增加观察角度,但仍靠证据裁决(4.2);
  • 阶段 6 可按清单分批换会话:一个会话做一两项、勾掉、结束,下个会话从清单接着来——清单就是为此设计的进度账本;
  • 多任务并行时,整套流程在每个 worktree 里分别运转,降低工作文件互相覆盖的概率;共享引用、配置和外部服务仍需协调(第二章)。

第一次看会觉得这套流程很重。它的适用边界和整篇文章一致:小修小补不需要它,对话就够了;跨会话、跨天、需要审查和回归的大任务,才值得把流水线立起来。

八、瓶颈的转移:读、判断与克制

最后把视角拉远一点。AI 把"写代码"的成本打了下来,但总成本没有消失,而是转移了:

  • 读:审查大 diff 成为日常,4.3 的纪律不是讲究,是生存技能;
  • 判断:方案拍板、可行性裁决、命名品味——AI 给选项,你给结论;
  • 决策:什么托付、什么不托付,成了每小时都在做的选择。

四条收尾的克制:

  1. 能验证的才敢托付。你对一个领域建立不起验证手段(测试、走读、基准),AI 在这个领域的产出对你就全是黑箱——先造验证,再谈托付;
  2. 看懂的才敢合入。是否采用 AI 写的代码,取决于你有没有看懂它——看不懂的代码,无论谁写的,都是负债;
  3. 不是什么都值得 agent 化。一行改动、需要精密品味的重构、解释成本高于动手成本的活,自己写更快;反过来,模板化、重复且容易验证的工作——例如已有范式下的样板代码、测试骨架和接口文档——通常更适合优先尝试,产出仍要按领域风险验证。AI 是工具箱里最锋利的一件,不是唯一一件;
  4. 吞吐可能放大返工。如果单位产出的缺陷率不变,产出越快,单位时间累积的缺陷也越多;这不是固定倍数的性能结论。纪律的价值随吞吐放大,而不是消失。

还有一条,关于你自己:先自己思考,再和它讨论。执行的过程也是思考的过程,把方案直接外包出去,省下的思考会变成后面付的利息,用得越久越明显。先形成自己的判断,再让 AI 参与验证、修正和优化——与它讨论的往返本身,才是学习和手感留存的地方。

这套做法可以视为 Harness Engineering(本文暂译为“驾驭工程”) 的一种个人工作流实践。这里的 harness 不只是一轮“写代码—跑测试—修错误”,还包括 agent 能看到的上下文、可调用的工具、权限与沙箱、任务状态、审查、验证和恢复机制。全文的 worktree、文档三件套、清单、审查回归、测试与 hook,分别补上这些环节。它们提高结果可验证、可追踪和可恢复的程度,却不保证每次自动得到正确答案;复杂任务仍需要人定义意图并处理无法机械判定的取舍。

这套工作流如果只用一句话总结:把"信任 AI"换成"验证 AI"。工程手段不生产信任,它生产可验证性——而可验证性,才是你敢把大任务交出去的全部底气。

参考资料