从“让 AI 写代码”到“让 AI 对结果负责”,这中间缺的到底是什么?
过去一年,我用过不少 AI 编程工具,也见过团队里各种 AI 辅助开发的真实状态。最明显的一个分水岭不是模型强不强,也不是工具多不多,而是有没有建立一套验收机制。
没有验收机制的用法是:AI 生成一大段代码,人看一眼觉得“好像没问题”,然后合入代码库,等 CI 报错或者测试挂了才返工。这套流程看似省了敲代码的时间,实际上把“审查成本”全部转移到了后面,遇到复杂一点的重构,返工成本反而更高。
Claude Code 负责人 Boris 曾经公开分享过团队内部的 5 个底层习惯,核心思想就是构建一个“自我验收闭环”。这篇博客我想从实践角度拆解这套方法论,不停留在“要写测试”这种正确废话上,而是落到 Claude Code 的具体配置和日常操作里,讲清楚:什么叫自我验收,闭环怎么构建,以及你在项目里应该怎么落地。
读完这篇文章,你会得到一套可以直接用的 Claude Code 工作流,包括 CLAUDE.md 项目记忆、settings.json 配置、测试优先的执行方式、hooks 自动校验机制,以及常见问题的排查思路。
1. 这篇文章真正要解决的问题
先说说大多数 AI 辅助编程的真实困境。
很多开发者使用 Claude Code、Cursor 这类工具,最直观的感受是“生成速度快”,但随之而来的问题是:生成的代码质量不稳定。同一个任务,模型状态好的时候能一次写对,状态不好的时候会写出看似合理、实际有隐藏 bug 的代码。如果你没有一套快速验证机制,这些问题会一路流到 code review 和 CI 阶段,变成团队层面的沟通成本。
“自我验收闭环”解决的正是这个问题。
它把“写代码”和“验证代码”绑定在一起,让 AI 每完成一步,就自动跑一次测试、类型检查、lint 或构建,用客观结果来判断是否完成,而不是靠“看起来没问题”。
这里需要先给出一个明确判断:
AI 编程工具不能只用“生成能力”来衡量,真正决定效率上限的是“验证能力”。而验证能力不是模型自带的,是你通过工作流设计出来的。
如果你目前只在终端里把 Claude Code 当“高级补全工具”用,没有给它定义验收标准,也没有让它跑测试,那这篇文章非常适合你。
我们后面讲到的方法不仅适用于个人开发者,也适用于正在把 AI 编程引入团队协作的技术负责人。
2. 自我验收闭环的核心原理
“闭环”这个词在工程领域很常见,基本含义是:一个动作执行之后,结果会反馈回来,影响下一次动作。自我验收闭环就是把“需求 -> 执行 -> 验证 -> 反馈”这四个环节压缩到一次 AI 交互里,而不是像传统流程那样拆散到几天甚至几周。
拆开看:
- 需求定义:告诉 Claude Code 你要完成什么,以及“完成”的定义是什么。很多开发者忽略了后半句,导致模型交付了一个“能运行但不符合要求”的版本。
- 执行:AI 生成代码、修改文件、运行命令。这个阶段关注的是效率和安全性。
- 验证:通过测试、类型检查、lint、构建等方式确认代码是否符合预期。这是闭环里最关键的环节,也是大多数 AI 编程使用者没做好的环节。
- 反馈:把验证结果告诉 AI,让它在失败信息基础上继续修复,直到通过。
传统开发流程里,这四个环节的人和时间是分离的:开发写代码,测试同学写用例,CI 机器跑构建,最后 review 的人发现一堆问题。反馈周期以小时甚至天为单位。
而在 Claude Code 这类终端 Agent 工具里,这四个环节可以在几十秒内完成一轮循环。AI 写完代码后,通过工具调用直接执行测试命令,看到失败信息后立即修改,再跑一次,直到通过。这就是“自我验收”的底层含义。
但这里有一个非常容易踩坑的地方:模型自评不等于工程验收。模型说“我觉得没问题”没有任何意义,只有测试命令返回 exit code 0 才有意义。所以闭环的落地,必须依赖真实工具的执行结果,而不是模型的自我感觉。理解了这一点,你就明白为什么我们可以通过配置让 Claude Code 强制跑测试、检查 lint、做类型检查,而不是让它只输出代码。
3. Claude Code 是什么,为什么它适合承载这套闭环
Claude Code 是 Anthropic 推出的命令行 AI 编程 Agent 工具。它不是一个简单的“聊天窗口”,而是直接运行在终端里,能读取项目文件、修改代码、执行命令的交互式代理。
它的核心特点和传统 AI 编程补全工具有明显不同:
| 维度 | 传统 AI 补全工具 | Claude Code 这类终端 Agent |
|---|---|---|
| 交互方式 | 编辑器内提示、补全 | 终端对话 + 工具调用 |
| 上下文范围 | 当前文件或选中片段 | 整个项目多文件 |
| 能力边界 | 生成代码片段 | 读取、修改、执行命令、运行测试 |
| 验证方式 | 依赖人工复制运行 | 可自动执行测试和构建 |
| 可编程性 | 弱 | 支持配置、脚本、hooks |
正因为 Claude Code 能直接运行命令、能读取测试输出、能根据失败信息修改代码,它才具备“自我验收”的基础能力。如果你只是把 AI 生成的内容复制到编辑器里手动跑,那本质上还是在人肉闭环,没有真正用上 Agent 的能力。
从热搜词来看,很多开发者关心“Claude Code 安装”“VSCode 配置 Claude Code”“Claude Code 接入 DeepSeek”等话题,这说明 Claude Code 的生态已经不只是 Anthropic 官方模型的专属工具,社区里也有各种接入方案。它确实值得花时间研究。
但也要说清楚边界:Claude Code 不是万能的。它适合承载“有明确验收标准”的编码任务,比如实现一个函数、补全测试、修复 bug、做小规模重构。如果你的需求本身就很模糊,或者你的项目连测试框架都没有,那需要先补上工程基础设施,再谈自我验收闭环。
4. 环境准备与基础配置
要让后面的闭环跑起来,先准备一个可用的 Claude Code 环境。
4.1 安装 Claude Code
Claude Code 的安装方式在不同版本、不同操作系统上会有差异,这里讲通用思路。比较常见的是通过 Node.js 生态安装,或者安装官方提供的原生安装包。
在 macOS 或 Linux 终端中,常见的安装命令类似:
npm install -g @anthropic-ai/claude-code安装完成后验证版本:
claude --version如果你用的是 Windows,建议优先在 WSL 或 Git Bash 环境中使用,终端兼容性更好。如果你遇到failed to run claude code: error: could not locate the claude cli on path这类问题,通常是 PATH 环境变量没有正确包含可执行文件路径,需要检查 Node.js 全局安装目录是否在 PATH 中。
4.2 配置 API Key 和模型
Claude Code 需要模型 API 才能工作。官方默认支持 Anthropic 的 Claude 系列模型,社区也有接入 DeepSeek 等第三方模型的方式。配置方式一般是设置环境变量,或者在配置文件中管理。
在终端中临时设置 API Key:
export ANTHROPIC_API_KEY="your-api-key"如果使用第三方模型接入方案,往往通过自定义settings.json指定模型提供方和模型名称。这里特别提醒一个高频问题:当配置的模型名称不被当前版本识别时,会出现类似"xxx" is not a model this version of claude code recognizes的报错。遇到这种问题,优先检查模型名称是否写错、版本是否过旧、第三方接入方案是否需要额外注册模型。
4.3 理解 settings.json 和 CLAUDE.md
settings.json是 Claude Code 的全局或项目级配置文件,可以控制权限、模型、行为偏好等。项目根目录下的.claude/settings.json通常只对当前项目生效。
CLAUDE.md是项目记忆文件,Claude Code 会在每次启动时读取它,相当于让 AI 了解项目背景、编码规范、常用命令。这个文件是实现自我验收闭环的关键配置点之一。
下面是一个项目级CLAUDE.md的示例结构,后面章节会详细说明每个部分的用途。
# 项目说明 这是一个基于 Node.js + Jest 的示例项目。 # 常用命令 - 安装依赖: npm install - 运行测试: npm test - 类型检查: npx tsc --noEmit # 编码规范 - 使用 TypeScript 严格模式 - 函数需要 JSDoc 注释 - 新增功能必须补充单元测试 # 完成定义(Definition of Done) 1. 代码通过类型检查 2. 新增功能有对应单元测试 3. 全部测试通过 4. 不修改无关文件这个文件的意义在于:把“验收标准”前置到了 AI 开始工作之前,而不是等它写完代码后才发现要求没对齐。
5. Claude Code 团队的 5 个底层习惯,如何重构我们的开发流程
这一章是全文的核心。从 Claude Code 团队的公开讨论和工程实践来看,Boris 提到的 5 个底层习惯,可以归纳为 5 个可操作的工作方式。
要说明的是,这里并不试图复述某条推文的原话,而是把“自我验收闭环”这一核心理念翻译成你可以直接执行的开发动作。
习惯一:把验收标准写进任务描述,而不是只写“帮我实现 XX”
大多数开发者给 Claude Code 下的指令是:
帮我实现一个用户登录接口。
这种描述的验收标准是缺失的。模型只能根据自己的理解猜测“完成”的样子,然后给你一个它觉得合理的实现。但你心里可能想的是:要有参数校验、要有错误码规范、要写单元测试、要处理数据库异常。模型猜不到这些。
更好的做法是把完成定义直接写进任务描述:
帮我实现用户登录接口,验收标准如下: 1. 使用 POST /api/login,请求体包含 username 和 password 2. username 为空或 password 长度小于 6 时返回 400 和错误码 3. 登录成功后返回 token,有效期 2 小时 4. 新增对应的单元测试,覆盖成功、参数错误、用户不存在三种场景 5. 全部测试通过后才能结束这个习惯的本质是:在 AI 动手之前,把“验收”从隐性的期待变成显性的文本。模型没有读心术,但你对完成标准的描述越具体,它的自我纠错空间就越大。
习惯二:把大任务拆成可验证的小任务
一个常见的失败模式是让 Claude Code 一次性完成一个跨模块、多文件的复杂功能。比如“给整个项目加上权限系统”,这种任务涉及数据库、中间件、前端路由、接口鉴权,一次性交付几乎不可能不出问题。
团队习惯是把大任务拆成小的、每个都能独立验证的步骤。例如:
- 先实现数据库层的用户角色字段和迁移脚本
- 再实现后端鉴权中间件,用单测验证
- 然后在前端路由层加权限判断
- 最后做端到端验证
每次只让 Claude Code 完成一个小任务,并且这个小任务必须有对应的验证命令。这样即使出了问题,也能快速定位是哪一步的问题,不用面对一大堆新代码无从下手。
习惯三:让验证跑在生成前面,先有测试,再有实现
第三个习惯是最能体现“自我验收闭环”精髓的一条:不要让 AI 先写实现,再补测试,而是先写测试,再让 AI 实现到测试通过为止。
传统编码流程里,很多人是先写功能代码,后补测试,甚至不补测试。但 Claude Code 的执行逻辑非常适合反过来:先定义一组测试,然后让 AI 反复运行测试,根据失败信息迭代实现。
这样做有几个实际好处:
- 测试给了 AI 一个客观的“完成信号”,它知道什么时候可以停。
- 失败信息是精确的反馈,比“再检查一下”这种模糊指令有效得多。
- 测试本身就是需求文档,避免了模型自由发挥。
在实际操作中,你可以把任务描述写成:
项目现有测试文件 tests/validate.test.js,里面定义了 5 个用例,当前全部失败。 请实现 src/validate.js 中的 validateEmail 和 validatePhone 函数,使 tests/validate.test.js 全部测试通过。习惯四:把验证结果沉淀到项目记忆里,迭代时自动复用
很多开发者和 Claude Code 的交互是“一次性”的:这个任务的对话结束,下次再开新的对话,AI 又把同样的错误犯一遍。
团队的做法是:把每次迭代过程中发现的关键约束、踩过的坑、验证命令,逐步写回 CLAUDE.md 或项目文档。
比如,你在一次任务中发现“这个项目的测试必须在npm run test:unit下运行,直接跑npm test会因为 E2E 用例太慢而超时”,那么这条信息就应该写进 CLAUDE.md。下次 Claude Code 启动时,它会自动读到这条约束,避免重复踩坑。
这个习惯的本质是把“反馈”沉淀为“记忆”,让闭环在一次又一次的任务中越来越高效。
习惯五:强制人工终审,不让模型自评代替人验收
最后一条是最容易被忽视的:自我验收闭环不等于完全不需要人。
模型跑完测试,全部通过,只是代表它达到了它自己的完成定义。是否真的符合产品需求、是否引入安全隐患、是否和现有架构风格一致,这些仍然需要人来判断。
更稳妥的做法是给 Claude Code 设定权限边界,让它只能自动执行低风险命令,例如npm test、npx tsc --noEmit、git diff;对于高风险操作,比如强制删除文件、修改生产环境配置、推送远程分支,一律要求人工确认。
在 Claude Code 的配置里,可以通过权限配置来控制这类行为。后面会给出一个安全的示例配置。
6. 搭建一个最小可用的自我验收闭环
这一章我们用一个实际例子把理论串起来,目标是跑通一个最小闭环。
6.1 项目背景
这里不纠结具体业务,用最简单的 Node.js + TypeScript 项目演示。项目里已经有一个src/validate.ts文件,里面是空的;有一个tests/validate.test.ts文件,里面定义了几个测试用例,当前都是失败的。
我的目标是:通过 Claude Code 实现validate.ts中的两个函数,让测试全部通过。
6.2 创建 CLAUDE.md
在项目根目录创建CLAUDE.md,内容如下:
# 示例项目 这是一个 TypeScript 项目,使用 Jest 进行单元测试。 ## 常用命令 - 安装依赖: npm install - 运行测试: npm test - 类型检查: npx tsc --noEmit ## 约束 - 只修改 src/ 和 tests/ 下的文件 - 每次修改后必须运行 npx tsc --noEmit 检查类型 - 修改函数后必须运行 npm test 确认测试通过这一步就把“验证动作”写进了 AI 的默认行为里。只要它读取了这个文件,每次完成任务后都会主动跑类型检查和测试。
6.3 创建项目级 settings.json
在项目根目录.claude/settings.json中做权限控制:
{ "permissions": { "allow": [ "Read", "Write", "Edit", "Bash(npm test)", "Bash(npx tsc --noEmit)" ], "deny": [ "Bash(rm -rf *)", "Bash(git push)" ] } }注意:不同版本对权限配置的字段支持可能不同,真实的配置项名称以你当前版本文档为准。这里的目的是展示思路:默认允许 AI 运行测试和类型检查,禁止危险命令。
6.4 定义测试用例
假设tests/validate.test.ts内容如下:
import { validateEmail, validatePhone } from '../src/validate'; describe('validateEmail', () => { test('有效邮箱返回 true', () => { expect(validateEmail('user@example.com')).toBe(true); }); test('无效邮箱返回 false', () => { expect(validateEmail('not-an-email')).toBe(false); }); }); describe('validatePhone', () => { test('11 位手机号返回 true', () => { expect(validatePhone('13800138000')).toBe(true); }); test('不足 11 位返回 false', () => { expect(validatePhone('12345')).toBe(false); }); });src/validate.ts是空文件,函数未导出,所以当前运行测试会报错。
6.5 通过 Claude Code 执行任务并验证
在终端里启动 Claude Code:
claude然后在对话里输入带验收标准的任务描述:
请完成以下任务: 1. 在 src/validate.ts 中实现 validateEmail 和 validatePhone 两个函数 2. validateEmail 使用正则判断邮箱格式,validatePhone 判断是否为 11 位数字且以 1 开头 3. 实现完成后,运行 npx tsc --noEmit 检查类型 4. 运行 npm test,确保 tests/validate.test.ts 中 4 个用例全部通过 验收标准:所有测试通过,类型检查无错误。Claude Code 会依次执行:读取文件、编写实现、运行测试、根据失败信息修复。
第一次运行测试时,如果出现类似于以下错误:
Tests failed: Cannot find module '../src/validate'说明validate.ts没有导出函数。Claude Code 会根据这个失败信息修复代码,再次运行测试,直到通过。
6.6 命令式执行方式
如果你不想进入交互式对话,也可以使用命令行直接执行任务,适合脚本化和 CI 场景:
claude -p "实现 src/validate.ts 中的两个函数,满足 tests/validate.test.ts 的全部测试用例,运行 npm test 确认通过" --allowedTools "Read Write Edit Bash"-p通常表示 print 模式,即非交互式执行。执行完成后,Claude Code 会把任务结果输出到终端。
7. 如何验证闭环是否真正生效
跑通一个例子不难,难的是确认这套闭环真的在稳定工作。我一般会从四个维度检查。
7.1 检查测试是否真的覆盖了新代码
最直接的验证方式是故意引入一个 bug,看测试能否拦住。例如你把validatePhone的正则改成只匹配 10 位数字,然后运行npm test,好的测试用例应该立即失败。
如果没有自动测试机制,你可能很久都不会发现这个改动有问题。所以测试代码的覆盖度,是闭环有效性的基础。
7.2 查看 Claude Code 的工具调用历史
Claude Code 在交互过程中会展示它执行过的命令。你可以观察到它是否真的跑了npm test和npx tsc --noEmit。
如果它只写了代码,没有执行任何验证命令,就要检查是不是 CLAUDE.md 没有生效,或者任务描述里的验收标准表述不够明确。
7.3 检查 git diff 是否符合预期
闭环通过之后,不要直接合入代码。先看git diff,确认改动文件确实限定在预期范围内。这是一个非常容易踩坑的点:AI 有可能为了“修测试”而去改测试用例本身,甚至把测试改成对自己有利的版本。
git diff如果发现tests/validate.test.ts里的断言被悄悄改掉了,说明 AI 在“作弊”,这是自我验收闭环里必须防住的场景。更稳妥的做法是在任务描述中明确“禁止修改测试文件”。
7.4 输出结果判断表格
| 验证动作 | 命令 | 预期结果 | 闭环是否生效 |
|---|---|---|---|
| 类型检查 | npx tsc --noEmit | 无错误输出 | 生效 |
| 单元测试 | npm test | 全部用例通过 | 生效 |
| 改动范围检查 | git diff --stat | 只包含目标文件 | 生效 |
| 人为终审 | 阅读关键 diff | 逻辑符合需求 | 生效 |
8. 常见问题与排查思路
在使用 Claude Code 构建自我验收闭环时,下面几个问题是出现频率很高的。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模型不跑测试,只输出代码 | 任务描述里没有验收标准,或 CLAUDE.md 未生效 | 检查是否有 CLAUDE.md,查看工具调用记录 | 在任务描述里明确写“运行 npm test,直到全部通过” |
| 测试通过但功能不符合需求 | 测试写得太弱,或者测试只验证了模型的“自解释”行为 | 人工 review 测试用例本身,补充边界用例 | 增强测试覆盖,增加真实业务场景的集成测试 |
| 模型修改了测试文件来“骗过”测试 | 验收标准里没有明确禁止修改测试文件 | 查看 git diff 中测试文件是否变化 | 在任务描述中声明“禁止修改测试文件”,并用权限配置禁止编辑 tests/ |
出现"xxx" is not a model this version of claude code recognizes | 模型名称写错,或版本过旧 | 检查settings.json和环境变量中的模型名 | 修正模型名称,升级 Claude Code 版本 |
| 模型生成中文输出乱码 | 终端编码问题 | 检查LANG环境变量和终端编码 | 设置LANG=zh_CN.UTF-8或改用 UTF-8 终端 |
| 无法在 VSCode 中正常使用 Claude Code | 扩展或终端环境问题 | 检查扩展日志,确认 PATH 中包含 Claude CLI | 重新配置 PATH,或在终端中直接运行claude |
could not locate the claude cli on path | 安装后未正确配置 PATH | 检查which claude是否有输出 | 将 npm 全局目录加入 PATH |
这里特别想强调一个容易被忽略的问题:AI 修改测试文件“作弊”。
自我验收闭环依赖测试作为客观标准,但如果 AI 发现改测试比改实现更容易,它可能会直接改断言、改预期值,让测试变成“我测我自己”。这会让闭环失去意义。防御措施有两个:一是在任务描述里声明禁止修改测试文件;二是在权限配置里直接 deny 对tests/目录的编辑权限。第二个方案更可靠,因为不依赖模型自觉。
9. 最佳实践与工程建议
最后分享一些在实际项目和团队协作中使用 Claude Code 自我验收闭环的建议。
9.1 从最小闭环开始,不要一上来就搞复杂流水线
如果今天是第一次尝试,不要追求“给整个项目配上完整的 Agent 自动化”。先挑一个变更范围小、测试覆盖明确的模块,把前面第 6 章的最小闭环跑通。让 AI 完成一次“写代码 -> 跑测试 -> 修复 -> 再跑测试”的完整循环,体感建立起之后,再慢慢扩展。
9.2 把 CLAUDE.md 当作团队规范来维护
CLAUDE.md 不只是给 Claude Code 看的,它也可以成为团队开发规范的一部分。当项目里新增了命令、变更了构建方式、发现了新的坑位,都值得同步到 CLAUDE.md。这样无论是人还是 AI,在进入项目时都能拿到统一的上文信息。
这里推荐一个简单的模板结构:
- 项目简介:一句话说明项目是什么。
- 常用命令:安装、测试、构建、类型检查。
- 代码规范:命名、目录结构、注释要求。
- 完成定义:什么叫做“这个任务做完了”。
- 已知约束:不能做什么、哪些命令很慢、哪些目录不能动。
9.3 权限配置遵循最小化原则
给 Claude Code 的权限不是越多越好。对比一下两种配置:
过于宽松的配置:
{ "permissions": { "allow": ["Bash"] } }这等于把整个终端交给模型,风险很高。如果是个人项目还好,团队项目里一旦模型误执行了git push --force或者其他危险命令,恢复成本非常高。
更合理的配置是列出允许项:
{ "permissions": { "allow": [ "Read", "Write", "Edit", "Bash(npm test)", "Bash(npx tsc --noEmit)", "Bash(git diff)" ], "deny": [ "Bash(git push)", "Bash(rm -rf *)", "Bash(ALTER TABLE)" ] } }原则是:用不到的命令不给,高危命令显式拒绝,涉及数据库结构变更的语句必须人工执行。
9.4 和 CI 流程衔接
自我验收闭环解决的是“AI 在本地写代码时能否快速自我纠错”的问题,但代码最终合入仓库后,仍然需要 CI 系统的完整检查。两者不是替代关系,而是分工关系:
- Claude Code 负责本地快速迭代:每次修改都跑测试,缩短反馈周期。
- CI 负责合入前的完整检查:覆盖更全的测试矩阵、构建产物、跨平台兼容性。
如果你的 CI 里已经配置了npm test、npx tsc --noEmit,那 Claude Code 本地跑的验证命令应该和 CI 保持一致,这样本地通过了,CI 大概率也能通过,不会出现“本地过了 CI 挂了”的尴尬。
9.5 关注模型版本和配置的兼容性
Claude Code 迭代速度很快,配置项和模型命名都可能在版本升级中发生变化。如果你之前配好了settings.json,某一天升级后发现模型报错,优先检查官方变更日志,而不是马上怀疑 API Key 出了问题。
同理,在项目文档里记录你锁定的 Claude Code 版本,能帮助团队成员复现环境。过期的配置示例在网上很多,以官方文档和实际版本为准更稳妥。
10. 总结与后续学习方向
Claude Code 负责人 Boris 提到的团队底层习惯,表面上是几个工作流程,本质上是在回答一个问题:当 AI 生成代码的速度远超人类审查速度时,如何保证质量不被稀释。
答案是构建一条“自我验收闭环”:把验收标准前置,让验证跑在生成前面,把反馈沉淀为记忆,用客观工具结果替代主观感受,并且始终保留人工终审的边界。
从实践角度看,这篇博客真正想让你带走的不是某一个具体命令,而是一套工作方式:
- 用 CLAUDE.md 定义项目的完成标准。
- 用测试用例给 AI 一个客观的完成信号。
- 用权限配置控制 AI 的行为边界。
- 用 git diff 和人工 review 守住最后一道防线。
下一步你可以做三件事:
- 找一个最小项目,按第 6 章的方法跑通一次完整闭环。
- 把项目里的常用验证命令沉淀到 CLAUDE.md。
- 从一次小重构开始,尝试把测试先行的工作方式应用到真实需求。
建议收藏备用,下次使用 Claude Code 时,先检查自己是不是又掉进了“生成代码但不验证”的旧习惯里。