ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

双AI互审实战:Claude Code与Codex协作提升代码质量

双AI互审实战:Claude Code与Codex协作提升代码质量 大概三个月前我在做一个订单导出功能的时候Claude Code给了我一版代码逻辑看着没问题单测也过了。但上线前两天我偶然发现一个极端情况下的空指针——那段代码正是Claude自己写、自己检查、自己拍胸脯说没问题的那部分。也就是从那个时候起我开始认真琢磨一件事让同一个AI既当作者又当审稿人它凭什么觉得自己没问题于是我把另一个AI拉进了开发流这个实践后来变成了一个叫 Claude-Codex-coop 的小项目核心思路就一条——让 Claude 和 Codex 互相审查。今天这篇就把整个过程拆开讲环境怎么搭、工作流怎么设计、实测效果如何、自动化怎么落地给想试同样玩法的朋友一条能直接走的路线。1. 为什么我会组一个“双AI审查小组”单模型的自欺时刻1.1 第一个让我警觉的瞬间先还原一下那个空指针场景。当时的需求是导出订单Claude Code 很快写完了主流程还顺手把边界判断也做了。我让它自查它逐行解释最后结论是“逻辑完整没有明显问题”。我当时也信了毕竟每一步解释都说得通。直到我手动构造了一条状态异常的历史订单程序在取退款时间字段时直接崩了而那个字段恰好是上一版需求里被废弃、新模型不了解的旧字段。问题不在Claude“不够聪明”而在于它对自己生成的代码有天然的路径依赖。它知道当初为什么这么写所以审查时不是在“找毛病”而是在“解释合理性”。这个现象在心理学里叫确认偏误放到AI身上也一样模型倾向于维护自己已经输出的内容而不是推翻它。更有意思的是当我随手把同一段代码丢给Codex看时它第一句话就是“这个字段可能在新状态机下不存在”。那一刻我意识到换一个AI其实不只是换一个工具而是换了一套训练数据、一套推理习惯、一套对“好代码”的定义。两个模型彼此不了解对方写代码时的上下文反而成了审查时最大的优点。1.2 为什么“另一个模型”是最合适的审查者很多人会问我自己也能审代码为什么要让AI来审AI我的看法是人工审查和AI互审是不冲突的两道防线但AI互审解决的是“低水平重复劳动”和“惯性思维”这两个问题。先看惯性思维。自己做技术的人都知道自己写的东西最容易“越看越顺眼”错别字、逻辑漏洞都会被大脑自动补全。AI也一样它生成代码时已经建立了一套自洽的假设自审时往往会沿着这些假设去“圆场”。而另一个模型没有这些负担它看到的只有文件和需求文档不知道你当初为什么选这个方案、为什么留这个TODO所以敢于直接指出“这里不合理”。再看效率。人工审查核心模块没问题但每个函数都让人审不现实。双AI互审相当于先让机器把关一轮把明显的逻辑错误、边界问题和安全隐患过滤掉再交给人看的时候人只需要盯业务语义和架构层面的事。而且由于Claude和Codex的模型来源不同它们各自的盲区也不重合——比如某个算法上的偏好、对某些API用法的默认假设——交叉审查正好把两套盲区错开了。用一句话总结就是单AI是作者双AI是作者加编辑。编辑不需要比作者更懂业务但它能发现作者眼皮底下的错字和病句这就已经值回票价了。2. 环境搭建的硬骨头Claude Code和Codex CLI安装配置实录2.1 Claude Code安装异常与登录受限的处理Claude Code 现在的标准安装方式还是npm包命令很简单npm install -g anthropic-ai/claude-code但网上搜这个关键词能搜出一大堆报错其中出现频率最高的是这条error: claude native binary not installed. either postinstall did not run我遇到过两次。第一次是Node版本太旧Claude Code的新版本对Node 18有要求旧版本环境下postinstall脚本直接没执行第二次是npm用了自定义镜像源导致安装过程中下载原生二进制那一步失败了。修复方法并不复杂先确认Node版本然后强制重装一次node -v npm install -g anthropic-ai/claude-code --force如果重装完还是报错就检查npm的registry配置换回默认源再装。另外实在不想全局安装也可以用npx anthropic-ai/claude-code直接跑但这样每次都要重新解析包日常用起来不如全局装舒服。登录这块还有两个常见的坑。一个是组织类账号登录后Claude Code可能直接报“your organization has disabled claude subscription access for claude code”。这个多半是团队管理员在后台限制了订阅使用范围个人账号一般不会遇到。解决办法是切换到自己的账号登录或者用API Key方式接入后者对独立开发者来说更直接export ANTHROPIC_API_KEY你的key claude另一个是Windows环境下的老问题启动时提示“Claudes workspace requires the virtual machine platform on windows. Enable”。Claude Code在Windows上跑代码沙箱依赖虚拟化组件需要到“控制面板-启用或关闭Windows功能”里勾选“虚拟机平台”重启机器后就好了。如果不想动系统设置那就老老实实用WSL2把Claude Code装在Linux环境里体验会顺很多。2.2 Codex CLI组织设置加载失败与自定义模型接入Codex CLI 的安装同样简单npm install -g openai/codex登录时可以用ChatGPT账号也可以用API Key。我自己用下来日常开发建议直接codex login走账号体系配额管理更省心但如果要写脚本自动化调用API Key方式更稳定因为它不受交互登录状态影响。登录后经常有人遇到“codex无法加载组织设置”。这个报错我仔细查过多数情况下是Codex配置文件里缓存了失效的登录态。处理方法也比较粗暴有效清掉~/.codex目录下的auth缓存文件重新codex login一次。如果还是不行检查一下当前网络环境能否正常访问官方API端点这个问题往往出在请求被中间设备拦截而不是Codex本身的问题。还有一个很容易在网上搜到的报错叫cc switch local proxy failed while handling codex endpoint /responses第一次看到这个错的时候我也懵了一下后来排查发现这条报错一般出现在你把Codex的自定义endpoint指向某个本地转发服务、但那个服务没启动或者地址写错的情况下。对策不是去折腾报错文案而是检查三处自定义endpoint是否可达、认证头是否正确携带、响应格式是否符合OpenAI规范。这三处理顺了报错自然消失。Codex CLI还有一个很多人喜欢的点它能通过~/.codex/config.toml接入第三方模型服务。网上讨论比较多的场景是把Codex接到DeepSeek这类兼容OpenAI接口的服务上配置思路其实是通用的我贴一个参考配置不同版本字段略有差异以官方文档为准model deepseek-chat model_provider deepseek [model_providers.deepseek] name DeepSeek base_url https://api.deepseek.com/v1 env_key DEEPSEEK_API_KEY wire_api chat设置好环境变量DEEPSEEK_API_KEY之后codex就会走这个自定义提供方。这样做的实际价值在于你可以让Codex的“审查视角”来自不同的模型底座进一步拉开和Claude Code之间的差异性互审效果会更好成本也可能更低。2.3 两个CLI共存时的配置隔离Claude Code和Codex CLI装在一起默认是不冲突的。Claude的配置在~/.claudeCodex的配置在~/.codex目录天然隔离。真正容易出问题的是环境变量。我一开始图省事把ANTHROPIC_API_KEY和OPENAI_API_KEY一起写进了.bashrc结果Claude Code进程偶尔会读到一个被污染的环境虽然多数时候没事但出了错特别难排查。后来我的做法是写一个.env文件在需要运行某个CLI的终端里手动加载# 跑Claude相关任务 source .env.claude export ANTHROPIC_API_KEYxxx unalias claude 2/dev/null || true # 跑Codex相关任务 source .env.codex export OPENAI_API_KEYyyy另外这两个CLI默认都能读写文件、能执行命令权限相当大。如果只是做互审实验建议把所有工作文件放进一个专门的项目目录里并且不要把生产环境的凭证放到工作目录里。AI能读到的秘密就等于你亲手交出去的秘密这个习惯无论对哪个AI工具都适用。3. Claude-Codex-coop工作流作者、审查者、仲裁者三个角色怎么转3.1 角色分配谁先写、谁来审、如何交替这个项目的核心不是“同时开两个窗口各问一句”而是要让两个模型在一个明确的角色分工下协作。默认工作流我设计为Claude Code先写实现Codex做第一轮审查然后根据审查报告让Claude修复改完再由Codex复核。一轮结束后换位Codex写新功能Claude来审。为什么要交替因为如果长期让Claude写、Codex审Claude会慢慢形成“反正有人兜底”的随手风格审查者也会固定成一套挑刺模式时间长了两个模型可能相互适应让审查流于形式。交替之后双方都要体验“被挑刺”的过程反而会促使它们在写代码时更谨慎。实际跑下来更合理的做法不是所有代码都互审而是按风险等级选模块。我的分级标准是必审数据写入逻辑、权限校验、支付金额计算、状态机迁移选审新增接口、数据库查询、并发处理跳过UI文案、纯格式转换、样板代码这样既保证关键路径的可靠性又不会让token消耗和等待时间变得不可接受。当两个AI意见不一致时需要一个仲裁机制。我的做法是把作者代码、审查报告、双方争议点打包交给其中一方做“法官”角色让它读完整上下文后给出裁决如果仲裁结果和初始审查方向相反我再人工看一眼。这个机制下两个AI会互相约束不太容易出现一边倒的错误结论。3.2 用文件系统做上下文中转需求文档、代码快照、审查报告很多人在用AI协作时喜欢直接把上一轮的对话文本复制粘贴到另一个工具的对话框里这种操作在单轮实验没问题但一旦进入多轮迭代就会遇到上下文爆炸、格式错乱、找不到历史版本的问题。Claude-Codex-coop的解决方案很土但很稳用文件系统做中转站。典型的目录结构长这样project/ docs/ requirements.md change_log.md generated/ impl.py impl_v2.py reviews/ review_by_codex.md review_by_claude.md关键是几个文件各司其职。requirements.md是写给AI看的需求说明里面不能只有一句“做一个订单导出”而是要包含验收标准、边界条件、禁止事项。我第一次写需求文档时只写了功能描述结果Claude生成的代码功能全对但边界全漏。后来我强制自己在文档里加一段“边界与例外”清单比如“当订单状态为已取消时不生成退款链接”“时间字段一律使用ISO 8601格式”审查质量立刻上了一个台阶。change_log.md是作者AI在完成修改后生成的一个简短说明写清楚自己改了什么、为什么改、改了哪些文件。这个文件对审查者极其重要因为审查者在没有上下文的情况下很难区分“这是故意的设计”和“这是不小心的疏漏”。有了change log审查者就能聚焦在“改动是否合理”而不是“为什么这里和昨天不一样”。审查报告由审查方负责输出格式我固定为四条严重级别P0致命/P1严重/P2建议、文件与行号、问题描述、最小修复建议。这两条约定下来之后后续的修复循环和自动化解析都变得非常简单。3.3 审查提示词的关键设计逼AI说“不好”工具链就位后能不能审出真问题很大程度取决于审查提示词。经验是你越允许AI夸你它越会夸你你越逼它挑刺它越能挑出刺来。我使用的审查提示词模板如下你是一名严格的代码审查者。你的任务只有一个找出问题。 不需要称赞任何设计不要输出“整体实现良好”这类话。 请阅读 requirements.md 和 change_log.md对照 impl.py 逐项检查 1. 是否存在需求未覆盖的边界条件 2. 是否存在可能导致异常退出或数据损坏的错误处理缺失 3. 是否存在安全风险注入、越权、敏感数据泄露等 4. 是否存在明显的性能问题或资源泄漏 5. 是否存在命名混乱或逻辑冗余 按要求输出审查报告每个问题都必须给出 - 严重级别P0 / P1 / P2 - 文件与行号 - 问题描述 - 最小修复建议 如果没有任何P0/P1问题仍必须给出至少两个P2级改进建议。“不需要称赞”“必须给出至少两个建议”这两句是关键。没有这两句的时候审查报告经常出现“代码整体质量不错可以合并”这种毫无价值的结论加上之后审查者被迫进入找茬模式哪怕代码确实没大问题也会给出具体可操作的改进点。另一个高阶技巧是让审查者先复述需求再对照代码。在提示词里加一句“在审查前先用100字以内复述你对需求的完整理解”能强制审查者把注意力拉回到“代码是否符合需求”而不是“代码是否自洽”。这个方法在发现“AI和AI之间理解偏差”时特别有效有一种情况是两个模型各说各话最后发现它们对同一句需求的理解根本不一致而复述机制能早早暴露这个问题。4. 实测复盘双AI互审到底抓出了哪些真问题又漏掉了什么4.1 三个真实抓出bug的案例这套工作流跑了一个多月我整理了几个典型的实战案例都是真实抓到过问题、并且成功在测试环境里复现的写出来给大家做个参考。第一个案例是缓存过期判断。当时Claude Code实现了一个带过期时间的缓存工具类逻辑看起来很简单存入时记录expire_at读取时比较当前时间。Codex审查时指出代码里比较用的是“秒”但写入expire_at时用的是“毫秒时间戳”两个单位混用会导致缓存永远不命中或者永不过期。这个问题单看代码很难发现因为两处代码在不同函数里各自都说得通编译也能通过但运行时行为完全错了。属于典型的单位不一致Bug人眼容易看漏机器反而容易抓住。第二个案例来自Codex生成的一个文件导出接口。它实现了鉴权逻辑但Claude审查时发现鉴权token只在接口初始化时读取了一次没有做定期刷新一旦token过期接口就会抛401而且没有重试机制。表面上看这个接口的功能是“导出”鉴权是附属逻辑但实际运行中token轮换是常态这个坑非常隐蔽。Claude给出的修复建议也很具体把token获取放进请求拦截器里每次调用前检查有效期。第三个案例是个性能问题。Claude实现了一个用户列表查询接口查询本身很快但Codex注意到它对每条记录都发了一次额外的数据库查询典型的N1问题。在100条数据时性能尚可到了10000条就会直接超时。审查报告里甚至标出了具体行号修复起来几乎零成本。这类问题让我确定了一点AI互审在“逻辑正确但性能隐患”的发现上比人肉review要稳得多。这些案例的共同点在于问题都在“两行代码之间的隐含关系”上作者写的时候不会觉得有问题人审的时候容易跳过但另一个AI从完全不同的角度切入往往一眼就能看到矛盾。问题描述发现方严重级别修复成本缓存过期时间单位混用秒与毫秒不一致Codex审查Claude代码P1一行改动文件导出鉴权token未刷新过期即失败Claude审查Codex代码P1重构鉴权调用用户列表查询存在N1数据量增大后超时Codex审查Claude代码P2批量查询替换4.2 它们俩都点头、结果还是出错的场景双AI互审也远不是万能的。我踩过最深刻的一个坑是财务相关的金额处理。当时需求文档要求“金额保留两位小数”Claude和Codex在这个表述上没有任何分歧代码也写得干净利落。但上线后对账时发现有一笔订单的金额差了0.01元。原因是业务上要求的是“四舍五入到分”但会计合规场景更合适的做法是“银行家舍入”也就是逢五不总进、看前一位奇偶。我们的需求文档没有写明舍入规则两个AI自然都按普通四舍五入实现。它们双双通过审查因为代码本身的逻辑确实是对的问题出在需求描述和真实业务规则之间的缝隙里。第二个漏网之鱼是并发竞态。一个库存扣减逻辑两轮互审都通过了压测时偶发超卖。深究原因问题出在“先查后改”这个操作序列上两个AI在单线程视角下都没看出问题但并发场景下需要原子操作。后来的解决办法是在审查提示词里加了一条“如果代码涉及共享状态修改额外检查并发安全性。”即便如此这也只能降低概率不能完全消除。这两次经验让我得出了一个重要结论AI互审解决的是“代码和需求的一致性”以及“通用工程缺陷”但它解决不了“需求本身是否反映了真实世界”这件事。所以在后续的审查提示词中我加了一条“如果发现需求文档本身有歧义或可疑之处以P1级别标注并直接提问不要埋头实现。”这一步看起来简单实际上把审查边界推进到了需求层面。4.3 成本和时间的实测数据说一次互审到底烧多少token、花多少时间数据来自我的实际环境一个300行左右的Python模块需求文档600字只审查这一个文件。Claude Code生成实现的消耗大约在8000 token左右Codex审查同样规模的代码大约在9000 token左右一轮往返合计约17000 token。按API按量付费来算单轮成本折合人民币不到几块钱如果用的是订阅套餐这部分成本就更加可以忽略。时间上命令行非交互模式下一次“写出来审一遍”的完整流程大约要3到5分钟如果中间要修复再复审再加一轮。这个成本在我看来完全可以接受尤其是对比它带来的收益——一次互审能拦截掉的可能就是上线事故而事故的修复成本和社会损失远高于这点token开销。当然成本也不是没有优化的空间。我在项目后期做了一个很实用的调整只审diff不审全文件。改动10行就只把改动部分和紧邻的上下文交给审查者而不是把整个5000行项目都塞进去。这样既控制token又加快速度同时还能提升审查的专注度因为审查者不会被无关代码干扰。5. 把互审流程自动化CLI脚本串联与后续扩展5.1 一个可以直接抄走的互审脚本互审如果全靠手动敲命令一次两次还能接受日常开发效率就太低了。所以我把这套工作流压成了一个bash脚本核心逻辑是先让Claude生成实现再让Codex审查发现P0/P1问题就循环修复直到通过或达到最大轮数。#!/bin/bash # coop-review.sh - Claude写Codex审自动迭代 set -u PROJ_DIR${1:-.} REQ$PROJ_DIR/docs/requirements.md IMPL$PROJ_DIR/generated/impl.py REVIEW$PROJ_DIR/reviews/review_by_codex.md MAX_ROUNDS${2:-3} # 第1步Claude Code生成实现 echo [1/$MAX_ROUNDS] Claude Code 开始实现... claude -p 阅读 $REQ将实现写入 $IMPL并在 docs/change_log.md 记录你的设计决策只输出结果摘要。 --output-format text # 第2步循环审查-修复 for (( round1; roundMAX_ROUNDS; round )); do echo 第 ${round} 轮审查开始... codex exec --full-auto 阅读 $REQ、$IMPL 和 docs/change_log.md按模板输出审查报告到 $REVIEW不要称赞代码只列问题。 if grep -qE P0|P1 $REVIEW; then echo 发现 P0/P1 问题Claude 开始修复... claude -p 阅读 $REVIEW 中所有 P0/P1 问题修复 $IMPL不要改动其他逻辑修复后更新 change_log.md并说明每个问题如何解决。 --output-format text else echo 未发现 P0/P1 问题流程结束。 break fi done echo 互审完成最终报告$REVIEW几个细节要注意set -u而不是直接set -e因为grep -q在没匹配到关键词时返回非零如果开着set -e脚本就会在中途退出没法走到“未发现问题”的分支。MAX_ROUNDS一定要限制否则模型可能会陷入无穷尽的“修复-再报错-再修复”循环把token烧光。跑之前建议先在工作目录里准备好docs/requirements.md并确保Claude Code和Codex CLI都能在非交互模式下正常运行。第一次跑的时候有的环境会提示权限确认先手动跑一次claude -p test和codex exec test验证一下。这个脚本虽然简陋但已经是完整的“写-审-修-复审”闭环了。我后来在多个小模块上反复跑过稳定性和结果的一致性都很好基本可以作为日常开发的一个固定工序。5.2 后续扩展方向接CI、PR机器人、多模型仲裁脚本跑通之后能做的事情就多了。我目前正在尝试的几个扩展方向写出来给有同样需求的朋友参考。第一个方向是接入CI。把互审脚本放到GitHub Actions里每次有新提交就自动跑一轮“写-审”闭环审查报告作为构建产物留存。这样代码评审不再是开发完成后的事后诸葛而是每时每刻都在进行的。需要注意的地方是CI环境里要保护好API Key用GitHub Secrets存储别直接写进脚本。第二个方向是做成PR评论机器人。拿到Pull Request的diff文件喂给Codex生成审查意见再把格式化的评论通过GitHub API回贴到PR页面上。这个的价值在于让所有参与人都能看到AI的审查意见而不是只有跑脚本的那个人知道。技术难度不大主要是解析diff格式和调用API的粘合代码。第三个方向是多模型仲裁。目前是Claude和Codex两方互审遇到分歧时由其中一方再当法官。后续我想引入第三个模型做中立仲裁两个模型的审查报告都给它看让它输出最终裁决。这个方向理论上能把双AI互审的盲区再缩小一圈代价是token成本会线性上升。根据我的实测只有当项目进入核心模块的重构阶段时这个成本才值得掏。第四个方向是沉淀审查数据。每一次互审产生的审查报告、修复记录、最终通过的代码都是很好的语料。可以定期统计“哪个模型更常抓到哪种类型的问题”“哪类问题两个模型都总是漏掉”然后反向改进提示词。这套数据驱动的思路越攒越值钱。写在最后的实操体会这套双AI互审流程跑下来我最深的感触是它不会让代码自动变得完美但会非常有效地把“低级错误”和“惯性盲区”挡在上线之前。单模型写代码就像一个人赶稿越写越顺也越写越飘双模型互审就像有个编辑坐在对面看见你把人名写错会立刻拍桌子但它也只能管到你写出来的那些字管不到你选题本身有没有问题。所以别把互审当成银弹把它当成一道低成本高性价比的质量闸门就对了。如果你也想试建议从一个小模块开始先手动跑两轮感受一下两个模型的风格差异再上脚本自动化。等脚本稳定了再慢慢覆盖更多核心代码。整个过程不需要一次到位但跑起来之后你会明显感觉到“写代码”这件事的节奏变了——从一脚油门踩到底变成了边开边看后视镜。这个变化我觉得是值得的。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进