别让 Claude Code 自己审自己:给 Java 项目配置一个独立

本文针对Claude Code自我审查的不足,提出用独立Subagent进行代码审查。通过上下文、职责、权限隔离,重点检查并发、事务、数据库、MQ、接口兼容性等易出问题点,并给出完整配置、调用流程和注意事项,强调业务约束和明确检查项的重要性。
最近使用 Claude Code 开发时,我发现了一个很实际的问题:
同一个 Agent 写完代码以后,再让它检查自己的代码,往往检查不出真正的问题。
它可能会告诉你:
- 代码结构清晰;
- 异常处理完整;
- 测试覆盖充分;
- 实现符合需求;
- 没有发现明显问题。
但当自己重新阅读代码,或者换一个模型 Review 时,却经常能发现:
- 并发场景没有考虑;
- 事务边界不正确;
- 数据库查询存在 N+1;
- MQ 消费没有做幂等;
- Redis 和数据库可能不一致;
- 修改破坏了原有接口兼容性;
- 测试只覆盖了正常路径。
问题并不是模型完全没有审查能力,而是生成者和审查者共享了同一套思路。
它在写代码时已经默认了某些前提,在审查时通常还会沿用这些前提。与其不断要求主 Agent“再仔细检查一遍”,不如直接把生成和审查拆开:
主 Agent:理解需求并修改代码
↓
Review Agent:独立读取 Git Diff
↓
主 Agent:根据审查结果修复
↓
Review Agent:进行第二轮检查
Claude Code 的自定义 Subagent 正好适合完成这件事。
一、为什么不直接让主 Agent Review?
假设我们让 Claude Code 实现一个优惠券领取接口。
主 Agent 采用了下面的流程:
查询库存
↓
判断库存是否充足
↓
库存减一
↓
创建领取记录
实现完成后,我们继续在同一个会话中输入:
检查一下刚才的代码有没有问题。
主 Agent 很可能重点检查:
- 语法是否正确;
- 方法调用是否完整;
- 代码风格是否统一;
- 测试是否能够运行。
但它不一定会重新质疑最初的设计:
查询库存和库存减一是不是原子操作?
两个请求同时查询到库存为 1 时会不会超发?
领取记录创建失败后,库存是否需要回滚?
用户重复请求时是否有幂等控制?
这是因为主 Agent 不只是代码的编写者,也是设计方案的提出者。
让它自我审查,相当于让一个人验证自己的全部假设。即使重新执行一次推理,也容易沿着原来的路径继续检查。
独立 Review Agent 的意义并不是“多调用一次模型”,而是主动制造三种隔离:
- 上下文隔离:Reviewer 不继承主 Agent 完整的生成过程;
- 职责隔离:Reviewer 只负责发现问题,不负责证明实现正确;
- 权限隔离:Reviewer 默认不直接修改代码,只输出审查结果。
Claude Code 的 Subagent 拥有独立上下文窗口、自定义系统提示词、独立工具权限和模型配置,执行完成后只把结果返回给主会话。
二、Subagent 和 Agent Team 应该选哪个?
Claude Code 目前有两种容易混淆的多 Agent 机制:
- Subagent;
- Agent Team。
Subagent 由主 Agent 调用,独立完成任务,然后把结果返回主会话。它适合代码检索、测试执行和代码审查这类目标明确的任务。
Agent Team 则会启动多个相互独立的 Claude Code 实例。它们拥有共享任务列表,还能互相发送消息,更适合多个 Agent 需要讨论和协作的复杂任务。目前 Agent Team 仍属于实验能力,并且会带来更高的 Token 和协调成本。
对于“写完代码后做一次独立 Review”这个场景,使用 Agent Team 明显太重。
一个 Subagent 就足够了:
主 Agent
│
├── 编写代码
│
└── 调用 java-backend-reviewer
│
├── 查看 Git Diff
├── 检查业务风险
├── 检查并发和事务
└── 返回问题清单
三、创建 Java Review Agent
在项目根目录创建下面的文件:
.claude/
└── agents/
└── java-backend-reviewer.md
.claude/agents/ 中的 Agent 只对当前项目生效,并且可以提交到 Git,让团队成员共同使用。
放在下面的位置则会对本机所有项目生效:
~/.claude/agents/
Claude Code 会递归扫描这两个目录,并根据 Markdown 文件中的 name 字段识别 Agent。项目级 Agent 也适合直接纳入版本控制。
将下面的内容写入 java-backend-reviewer.md:
---
name: java-backend-reviewer
description: Review Java backend code changes for correctness, concurrency, transactions, compatibility, security, and maintainability. Use immediately after implementing or modifying Java code.
tools: Read, Grep, Glob, Bash
model: inherit
permissionMode: plan
---
You are a senior Java backend code reviewer.
Your only responsibility is to review the current code changes.
Do not modify files.
Do not implement features.
Do not praise the code.
Do not report generic style suggestions unless they create a real maintenance risk.
## Review scope
First run:
git diff
git diff --cached
git status --short
Focus primarily on code introduced or modified by the current diff.
Read surrounding code when necessary to understand the execution path.
## Review priorities
### 1. Correctness
Check for:
- Incorrect conditions
- NullPointerException risks
- Missing boundary handling
- Incorrect exception behavior
- Resource leaks
- Broken state transitions
- Wrong default values
- Timezone and date handling problems
### 2. Concurrency
Check for:
- Check-then-act race conditions
- Non-atomic stock or quota deductions
- Incorrect lock granularity
- Lock expiration problems
- Missing distributed locking
- Thread-unsafe shared state
- CompletableFuture exception loss
- Unsafe parallel writes
### 3. Transactions
Check for:
- Incorrect @Transactional boundaries
- Self-invocation causing transactions to fail
- Exceptions being swallowed
- Remote calls inside long transactions
- Database changes committed before required validation
- Inconsistent transaction propagation
- Transaction and MQ consistency problems
### 4. Database
Check for:
- N+1 queries
- Missing indexes for new query conditions
- Full-table scans
- Incorrect pagination
- Unbounded result sets
- Duplicate inserts
- Lost updates
- Incorrect optimistic locking
- Excessive queries inside loops
### 5. Redis and cache
Check for:
- Cache penetration
- Cache breakdown
- Cache avalanche risks
- Incorrect expiration strategy
- Database and cache inconsistency
- Non-atomic Redis operations
- Missing Lua scripts where atomicity is required
- Incorrect lock release behavior
### 6. Message queues
Check for:
- Missing consumer idempotency
- Duplicate consumption
- Message loss
- Incorrect retry behavior
- Poison messages
- Ordering assumptions
- Transactional inconsistency
- Missing compensation paths
### 7. API compatibility
Check for:
- Changed response structures
- Changed field semantics
- Removed fields
- New required parameters
- Incorrect HTTP status codes
- Incompatible database migrations
- Changes that may break existing callers
### 8. Security
Check for:
- SQL injection
- Command injection
- Path traversal
- Missing permission checks
- Sensitive information in logs
- Hard-coded secrets
- Unsafe deserialization
- Missing input validation
- Excessive data exposure
### 9. Tests
Check whether the changes include meaningful tests for:
- Normal paths
- Failure paths
- Boundary values
- Concurrent requests
- Repeated requests
- Transaction rollback
- MQ duplicate delivery
- Cache failure
- Backward compatibility
Do not assume that passing tests prove the implementation is correct.
Check whether the tests themselves express the real requirements.
## Output requirements
Only report issues that have concrete evidence.
Each issue must contain:
- Priority
- File and line
- Problem
- Trigger condition
- Consequence
- Suggested fix
Use the following priorities:
- P0: Security issue, data corruption, financial loss, or service-wide failure
- P1: Production bug, concurrency issue, compatibility break, or serious performance problem
- P2: Maintainability problem or missing defensive handling with a realistic trigger
Use this format:
## Review result
### [P1] Non-atomic inventory deduction
**Location:** `CouponService.java:87`
**Problem:** Inventory is queried and updated in two separate operations.
**Trigger:** Two requests read the same remaining inventory concurrently.
**Consequence:** The coupon may be oversold.
**Suggested fix:** Use a conditional update such as
`UPDATE ... SET stock = stock - 1 WHERE id = ? AND stock > 0`,
then check the affected row count.
If no concrete issue is found, output:
## Review result
No concrete issues found in the current diff.
Then list the remaining risks that cannot be verified from the repository.
这份配置没有试图让 Reviewer 检查所有“最佳实践”,而是重点关注 Java 后端中真正可能造成线上问题的内容:
- 并发;
- 事务;
- 数据库;
- Redis;
- MQ;
- 接口兼容性;
- 安全;
- 测试完整性。
四、为什么 Reviewer 不应该直接修改代码?
在配置中,我特意加入了:
Do not modify files.
Do not implement features.
Reviewer 的职责应该是提供证据,而不是一边发现问题,一边根据自己的理解重写代码。
如果允许 Reviewer 直接修改,工作流很容易变成:
主 Agent 写一版
↓
Reviewer 改一版
↓
主 Agent 又改一版
↓
不知道最终代码采用了谁的设计
更合理的职责划分是:
Reviewer 负责发现问题
主 Agent 负责理解问题
主 Agent 根据原始需求完成修改
Reviewer 再次验证修改结果
这样,生成和验证之间才能形成真正的制约关系。
Claude Code 官方给出的 Reviewer 示例同样采用有限工具权限,重点读取 Git Diff,并按照严重程度返回问题,而不是直接编辑项目文件。
需要注意的是,Bash 本身具备执行命令的能力。这里保留它主要是为了执行:
git diff
git status
mvn test
mvn checkstyle:check
因此,提示词中仍然要明确禁止修改文件和执行破坏性命令。
对于要求更严格的项目,还可以删除 Bash:
tools: Read, Grep, Glob
然后在调用 Reviewer 时,把需要审查的文件范围明确告诉它。
五、如何调用 Review Agent?
代码修改完成后,可以直接在 Claude Code 中输入:
使用 java-backend-reviewer 审查当前 Git Diff。
只检查本次修改引入的问题,不要修改任何文件。
也可以使用 @ 显式指定 Subagent:
@"java-backend-reviewer (agent)" 审查当前修改,
重点检查事务、并发、幂等和接口兼容性。
显式 @ 调用可以保证指定 Agent 被执行,而不是让主 Agent自行决定是否委派任务。Claude Code 也支持通过自然语言调用,或者使用 claude --agent <name> 让整个会话以某个 Agent 的身份运行。
我更推荐在每次功能完成后使用下面的指令:
调用 java-backend-reviewer 审查当前 Git Diff。
本次需求:
为优惠券领取接口增加 Redis 预扣库存。
业务约束:
1. 一个用户只能领取一次;
2. 不能超卖;
3. Redis 成功但数据库失败时需要补偿;
4. MQ 可能重复投递;
5. 不能修改现有接口返回结构。
只报告本次修改引入的具体问题。
每个问题必须给出文件位置、触发条件和后果。
不要直接修改代码。
这里有一个关键点:
不要只把代码交给 Reviewer,还要把业务约束交给 Reviewer。
因为很多错误并不是 Java 语法错误,而是代码没有满足真实业务规则。
如果 Reviewer 不知道“一个用户只能领取一次”,它就无法判断幂等逻辑是否完整。
六、完整的双 Agent 开发流程
配置完成后,我更推荐使用下面这套流程。
第一步:主 Agent 只做需求分析
先分析这个需求。
不要修改代码,输出:
1. 当前调用链;
2. 需要修改的文件;
3. 数据库和缓存影响;
4. 事务边界;
5. 并发风险;
6. 测试方案。
先确认方案,避免 Agent 一开始就大面积修改项目。
第二步:主 Agent 实现功能
按照刚才的方案实现。
要求:
1. 不修改无关文件;
2. 不改变现有接口协议;
3. 尽量复用项目已有组件;
4. 补充必要测试;
5. 完成后总结修改内容,但暂时不要 Review。
第三步:调用独立 Reviewer
调用 java-backend-reviewer 审查当前 Git Diff。
重点检查:
1. 并发安全;
2. 事务边界;
3. Redis 与数据库一致性;
4. MQ 消费幂等;
5. 接口兼容性;
6. 测试是否覆盖失败路径。
第四步:让主 Agent 判断审查结果
不要让主 Agent 无条件接受所有 Review 意见,而是要求它逐条判断:
阅读 Reviewer 返回的问题。
对每一项分别说明:
1. 是否成立;
2. 判断依据;
3. 是否需要修改;
4. 准备如何修改。
暂时不要改代码。
因为 Review Agent 同样可能误报。
正确的流程不是:
Reviewer 说什么就改什么
而是:
Reviewer 提出质疑
↓
主 Agent 根据代码和需求验证
↓
确认成立后再修改
第五步:修改并进行第二轮 Review
修复已经确认的问题,并运行相关测试。
不要修改被判断为误报的部分。
修改完成后再次调用:
调用 java-backend-reviewer 进行第二轮审查。
重点确认:
1. 上一轮 P0、P1 问题是否已经解决;
2. 修复是否引入新问题;
3. 是否仍存在未覆盖的失败路径。
最终流程如下:
需求分析
↓
主 Agent 编码
↓
第一次独立 Review
↓
主 Agent判断问题是否成立
↓
修复代码
↓
运行测试
↓
第二次独立 Review
↓
人工确认并提交
七、怎样让 Reviewer 使用另一个模型?
在 Subagent 文件中,可以通过 model 字段指定模型:
model: sonnet
也可以填写完整模型 ID:
model: claude-sonnet-5
或者继承主会话模型:
model: inherit
Claude Code 官方文档显示,自定义 Subagent 支持模型别名、完整模型 ID和 inherit。模型还需要位于当前组织或服务商允许使用的模型列表中,否则会回退到继承的模型。
例如,主 Agent 使用能力更强但速度较慢的模型:
主 Agent:负责需求分析和代码实现
Reviewer 使用另一个更适合代码审查的模型:
---
name: java-backend-reviewer
model: claude-sonnet-5
---
不过需要注意:
配置不同的模型名称,不一定代表真的调用了不同模型提供商。
如果使用的是公司内部 Claude Code 网关,最终能够使用哪些模型,取决于网关暴露的模型 ID和路由规则。
要实现真正的跨模型 Review,例如:
主 Agent:GLM-5.2
Reviewer:DeepSeek-V4-Pro
通常需要满足下面至少一个条件:
- 公司网关允许 Subagent 通过不同模型 ID路由;
- 分别启动两个使用不同配置的 Claude Code 会话;
- 使用外部编排脚本,把 Git Diff 发送给第二个模型;
- 通过 MCP 或自定义工具调用另一个模型服务。
因此,Subagent 首先解决的是上下文和角色隔离。
它是否同时实现真正的模型隔离,需要根据当前模型网关的行为进行验证。
八、Reviewer 最容易产生的三个问题
配置了 Review Agent,并不代表结果一定可靠。
1. 输出大量没有价值的建议
最常见的结果是:
建议增加更多注释。
建议使用更有意义的变量名。
建议进一步优化代码结构。
这些建议可能没有错,但会淹没真正重要的问题。
因此提示词中要明确:
Do not report generic style suggestions unless they create a real maintenance risk.
并要求每个问题必须包含:
- 具体位置;
- 触发条件;
- 实际后果;
- 修改建议。
没有触发条件的问题,通常只是泛泛而谈。
2. 把项目原有问题算到本次修改头上
Reviewer 扫描整个项目后,可能找到大量历史问题。
但当前任务真正关心的是:
本次修改是否引入了新的风险?
所以需要明确要求:
Focus primarily on code introduced or modified by the current diff.
除非历史代码与当前修改形成了新的调用关系,否则不应该成为本次 Review 的重点。
3. 只检查代码,不检查业务
下面这段代码在语法上可能完全正确:
Coupon coupon = couponRepository.findById(id);
if (coupon.getStock() <= 0) {
throw new BusinessException("库存不足");
}
coupon.setStock(coupon.getStock() - 1);
couponRepository.save(coupon);
如果 Reviewer 只关注 Java 规范,它可能认为代码没有明显问题。
但如果业务要求“高并发下不能超卖”,那么这个实现就可能存在严重风险。
因此,每次调用 Reviewer 时,都应该带上最重要的业务不变量:
库存不能为负数。
一个用户只能领取一次。
MQ 消费必须支持重复投递。
接口响应结构不能改变。
没有业务约束的 Code Review,只能检查代码是否“像代码”,无法检查它是否真正正确。
九、为什么专业团队也在拆分 Review Agent?
这种拆分并不只是个人开发者的实验。
Cloudflare 在其内部 AI Code Review 系统中,并没有使用一个大而全的 Reviewer,而是使用多个专业 Reviewer,分别检查安全、性能、代码质量、文档、发布流程和内部规范,再由协调 Agent 对结果去重并判断严重程度。
这套设计反映了一个很重要的方向:
不要让一个 Agent 同时负责所有判断。
复杂系统里的审查可以继续拆分为:
java-correctness-reviewer
java-concurrency-reviewer
database-reviewer
security-reviewer
api-compatibility-reviewer
test-reviewer
不过对于个人项目,没有必要一开始就创建七八个 Agent。
先建立一个可用的 java-backend-reviewer,等实际使用中发现某类问题经常漏掉,再逐步拆分。
例如,如果项目大量使用 Redis 和 RocketMQ,可以后续单独增加:
distributed-consistency-reviewer
它只检查:
- 缓存和数据库一致性;
- Redis 原子操作;
- MQ 幂等;
- 本地事务与消息一致性;
- 重试和补偿;
- 分布式锁。
十、一些实际使用建议
Agent 配置应该提交到 Git
项目级 Agent 放在:
.claude/agents/
这样团队成员拉取项目后,就能使用同一套审查规则。
Reviewer 的提示词本质上也是项目规范的一部分。
提示词不要只写“检查最佳实践”
下面这种定义几乎没有实际价值:
你是一位专业代码审查员,请检查代码质量和最佳实践。
它没有告诉模型:
- 重点检查什么;
- 什么问题不用报告;
- 如何确定严重程度;
- 如何输出证据;
- 如何限制审查范围。
Reviewer 的质量很大程度上取决于检查项和输出结构是否明确。
不要盲目追求一次 Review 发现所有问题
更合理的目标是:
每次 Review 稳定发现一部分高价值问题。
真正的质量保障仍然需要多层机制:
编译
+ 单元测试
+ 集成测试
+ 静态扫描
+ AI Review
+ 人工 Review
+ 灰度和监控
AI Reviewer 应该作为质量防线的一层,而不是替代全部防线。
新建目录后可能需要重启 Claude Code
如果当前会话启动时还不存在 .claude/agents/ 目录,创建第一个 Agent 文件后,当前会话可能无法立即发现它。
这种情况下退出并重新启动 Claude Code即可。官方文档也说明,文件监听只会覆盖会话启动时已经存在的目录。
结语
Claude Code 带来的最大变化,不只是代码写得更快,而是我们开始可以把开发流程拆成多个专业角色。
过去,一个程序员需要同时完成:
分析需求
编写代码
运行测试
检查问题
修改代码
现在可以把它拆成:
主 Agent 负责生成
Review Agent 负责质疑
测试工具负责验证
人负责最终判断
其中最重要的并不是“多用了一个 Agent”,而是建立了一个基本原则:
代码的生成者,不应该成为唯一的验证者。
一个好的 Review Agent 不需要帮我们写更多代码。
它真正应该做的,是在代码进入生产环境之前,多问几个主 Agent 没有问出来的问题。
参考资料
- Claude Code 官方文档:自定义 Subagent、模型配置、权限控制和调用方式。
- Claude Code 官方文档:Agent Teams 与 Subagent 的区别。
- Cloudflare:企业级多 Agent 代码审查系统实践。
- Agentic Code Review 研究:专业 Agent 与人工质量门禁结合的代码审查流程。
AI正从聊天框进化到能执行任务的Agent,通过MCP协议连接外部工具,在软件开发、浏览器和搜索等领域迅速落地。Agent的核心价值是可靠完成任务,而非模拟人类。开发者应关注Agent友好系统、MCP学习、安全及工程化,迎接软件从被动响应到主动行动的变革。
AI Coding 的下一站:从“会写代码”到“能交付任务AI Coding正在从代码生成转向工程执行系统,2026年趋势是Agent化,能理解项目、改文件、跑命令等。未来开发环境将变为人与AI Agent协作,程序员需提升需求拆解、系统设计等判断能力。
AI 爬虫不能再白嫖了?Cloudflare 正在改写开放互联网规则这篇文章也许值得继续阅读。