双AI交叉审计代码:Claude Code与Codex生产环境实战 20 个模块两个当前最主流的 AI 编程代理同时审计同一批生产代码最后两边都确认存在问题的只有 7 个模块。这个结果我不是意外而是有点发懵的状态。事情是这样的我们一个上线两年多的核心服务准备做一次大版本重构代码走查靠人肉基本走不过来几十万行代码团队就六个人。我当时的想法很直接——既然 Claude Code 和 Codex 都能在仓库里干活让它们俩互相交叉验证一下先替我们把可疑点筛出来再让工程师逐个确认。测完我对AI 审计这件事的认知发生了挺大的变化今天这篇就把整个过程、分歧案例和最终沉淀下来的工作流全部摊开讲。1. 起因我为什么把整个核心模块拆给两个 AI 做审计1.1 审计目标不是找 Bug而是找可疑点正式动手之前我先给团队定了个调AI 审计的产出不是这行代码错了而是这个逻辑值得人工再看一眼。因为大模型本质上是概率推理引擎它不可能证明你的代码没有问题它只能根据训练时见过的缺陷模式给出一批可疑点的候选项。我们的目标很具体在三类问题上做初筛——正确性风险并发、幂等、状态流转、安全风险越权、注入、敏感信息泄露、性能与稳定性风险N1 查询、无界缓存、慢 SQL。风格类问题不在考虑范围内ESLint 和 Prettier 已经管了。1.2 为什么选 Claude 和 Codex而不是其他工具选 Claude Code 和 Codex 的原因很朴素它们是当前跑在自己终端里、能真正读整个仓库、能改文件的 Agent 型工具不是那种粘贴一段代码进去问我这段代码有什么问题的网页版。安装环节其实就有故事可讲。我的开发机是 Windows跑 Claude Code 时直接弹了Claudes workspace requires the Virtual Machine Platform on Windows的报错这是因为新版 Claude Code 在 Windows 上依赖虚拟机平台来跑容器沙箱需要去启用或关闭 Windows 功能里勾上虚拟机平台然后重启不是装好就能用。Codex 那边我用的也是本地 CLInpm install -g openai/codex装完后还要注意 PATHWindows 上很容易报无法将“claude”项识别为 cmdlet这种环境变量问题本质上是 npm 全局目录没有加到全局 PATH 里。还有个小插曲为了统一管理不同模型的 API endpoint我试过 cc-switch 这类切换工具结果遇到local proxy failed while handling codex endpoint /responses的报错。排查了一圈发现是切换后 baseURL 没指向正确的本地代理地址工具本身没问题是配置不匹配。所以后面我干脆放弃了切换工具Claude 和 Codex 各自用官方默认配置避免引入额外变量。1.3 两套眼睛的分工设计我先在代码库根目录建了module_audit/文件夹用来放所有输入输出。审计策略上两个工具跑的是完全一样的模块清单和 prompt 模板——变量全部控制住后面对比才有解释力。也提一个亲测的陷阱有人喜欢在对话里一句帮我审计整个项目就把几十万行代码丢给 AI实测效果非常差。模型注意力会稀释在全局扫描里最终给出的都是注意错误处理加强校验这类正确的废话。必须把审计单元切小按模块或按文件批量喂输出质量才会真正上来。2. 统一口径没有可控变量的对比实验都是玄学2.1 模块拆解与 Prompt 模板我把服务按业务边界拆成 20 个模块每个模块对应一个子目录或一组核心文件。拆分的依据是模块之间能独立理解、单个模块的代码量保持在 500~2000 行左右、模块边界和现有领域模型对齐。太大的模块拆碎太小的模块合并。然后我把审计用的一份系统级 prompt 固定下来两个 AI 用同一份。Prompt 长这样你是资深代码审计工程师。请审计以下模块代码重点关注 1. 正确性风险并发、竞态、幂等性、状态一致性问题 2. 安全风险越权、注入、敏感信息泄露、不安全反序列化 3. 性能风险N1 查询、无界数据增长、阻塞调用、锁粒度 约束条件 - 只报告有具体代码依据的问题禁止泛泛建议 - 每个问题必须引用具体文件和行号 - 没有发现问题的模块必须明确回答 PASS - 如果某个改动会破坏现有行为请显式说明 输出格式 module: [模块名] issues: - severity: critical / warning / info file: [文件路径] line: [行号] issue_type: [bug / security / performance / consistency] description: [问题描述] suggestion: [修复建议]我实测下来必须回答 PASS和禁止泛泛建议这两条约束最能提升报告质量。没有这两个约束AI 会为了表现自己认真读了而硬编出几个低价值问题加了以后它宁可少报也不敢瞎报。2.2 输出格式与共识判定标准两边跑完后我把两份报告合并成一张对比表。合并的时候最麻烦的是怎么算共识。我定的标准是两个 AI 指向同一模块中同一段代码逻辑、并认定同类风险才算达成共识。只提到同一个文件名但指向不同函数、或者一个说并发一个说性能但实际指向不同行号都不算共识。举个例子支付模块两个 AI 都提了回调缺少验签但是一个指的是外层网关没验签另一个指的是业务层收到回调消息后没有校验签名这俩就不算共识因为一个外层网关其实有验签另一个业务层的消息确实没验。这种地方人工判断就非常关键共识判定本身就是在帮我们精确定位理解偏差。2.3 token 消耗和成本经验成本上我也报个实数。20 个模块跑下来Claude Code 输入大约 30 万 token、输出约 4 万 tokenCodex 输入约 40 万 token、输出约 5 万 token。差别来源主要是上下文管理机制不同Claude 会在长上下文里反复读取局部文件Codex 则倾向于把模块内引用到的关联文件也重新放入上下文。整体支出换算成 API 费用大概在几美元到十几美元量级相比人肉代码走查的人力成本基本可以忽略。但要注意的是如果你用 Codex 但接了 DeepSeek 这类第三方模型千万不要拿结果跟官方模型对比。不同模型的上下文行为和工具调用逻辑不一样审计结论的差异会被模型能力差异污染对比就没有意义了。3. 结果概览20 个模块只交叉出 7 个共识区3.1 三类结果的分布跑完后我把所有问题分成了三类共识区7 个模块两个 AI 都独立报告了同类的高危问题人工复核后全部属实。单边区9 个模块只有一边报告了问题另一边要么 PASS要么给的结论完全不同。盲区4 个模块两边都报告了一些问题但指向的位置和风险类型完全对不上人工复核后大部分是 AI 各自的幻觉或偏科。这个分布很有意思共识区只占总数的三成多说明当前主流 AI 审计工具在找可疑点这件事上远没有到可以互相替代的地步。3.2 共识区里那些问题长什么样两个 AI 都确认真实存在的问题几乎都是教科书级别的问题特征非常典型具备公认的缺陷模式、有明确的代码上下文、在一个文件内就能看清逻辑不需要跨服务追踪。举几个有代表性的订单金额计算用了浮点数乘法精度溢出风险两边都标了 critical优惠券领取接口缺少唯一约束并发下会重复发放两边都给出了加唯一索引的建议消息队列消费者没有幂等去重重复投递场景下会重复记账两边都识别了文件上传只校验了 Content-Type 头没校验文件真实内容存在伪造上传风险。所以我的结论是两个 AI 同时确认的高危问题可信度非常高基本可以直接提单。这比任何单个 AI 的报告都可靠得多相当于两个独立经验模型在同一个交叉点上相互验证了。3.3 单边发现的代表性方向单边发现的问题能看出两个工具的性格差异。Claude Code 单边发现的更多集中在数据流跨文件追踪比如一个用户 ID 从接口层传到异步任务里期间没有重新做权限校验、状态机流转缺了某个分支处理。说明 Claude 在处理长链条逻辑时更稳。Codex 单边发现的更多集中在某个具体函数里的边界条件处理比如数组越界、空指针分支没处理、某个 API 调用的参数校验缺失。说明 Codex 在单点爆破上有优势。这么一来我不再看它们谁强谁弱的问题了它们就是两副不同的眼镜各有各的景深。4. 分歧案例拆解四个让我印象最深的拉锯战这一节是全文的重头戏选四个最有代表性的真实复盘把代码、两个 AI 的反应、人工复核结论、最终修复方案原原本本摆出来。4.1 支付回调的幂等性Codex 报了Claude 没报支付回调处理函数简化后长这样async function handlePaymentCallback(payload) { const order await Order.findByOrderNo(payload.orderNo); if (order.paid) { return { success: true, message: duplicated callback }; } await Order.updateStatus(order.orderNo, paid); await pointService.add(order.userId, order.pointAmount); return { success: true }; }Codex 标了 critical先查order.paid再更新的判断和更新不是原子的同一个订单在并发回调下会同时通过if判断导致重复加积分。建议改成UPDATE orders SET paid true WHERE order_no ? AND paid false用更新影响行数做幂等判断。Claude 在这个模块的报告只提到回调缺少验签而这部分其实在网关层有做所以这条属实的共识度并不高。人工复核后采纳了 Codex 的方案。加积分动作也往订单表加了一个point_settled字段做幂等标记并在数据库层加唯一索引兜底。支付这种场景幂等是必须靠数据库约束实现不能靠代码判断实现的这是个血泪教训。4.2 库存扣减的并发两边都报了修复方案完全相反库存扣减的经典写法async function deductStock(skuId, count) { const sku await Sku.findByPk(skuId); if (sku.stock count) throw new BusinessError(stock insufficient); sku.stock - count; await sku.save(); }两个 AI 都确认这是并发超卖问题共识成立。但有趣的来了修复方案完全不同。Claude 建议用版本号乐观锁给skus表加version字段更新时带WHERE version ?更新行数为 0 则重试或报错。Codex 建议引入 Redis 分布式锁用SETNX锁住 skuId 后再执行读改写。我最后选了乐观锁和原子 UPDATE 的组合。原因很具体我们当时的架构里 Redis 的可用性保障还没有做到强一致锁超时和锁误删会引入新的不一致窗口而乐观锁的实现成本极低对库存这种写冲突并不频繁的场景完全够用。这个案例给了一个重要启发AI 共识告诉你这里有问题是极其有价值的但 AI 给的修法经常是通用方案而不是你的系统里最合适的方案需要工程师自己做取舍。4.3 日志脱敏Claude 报了Codex 没报登录成功的日志logger.info(user login success, { userId: user.id, phone: user.phone, idCard: user.idCard });Claude 标了 warning日志里打印了手机号和身份证明文只要日志系统被接入或泄露就是数据合规事故。建议改成只记录 userId 和脱敏后的手机尾号。Codex 在同一个模块里报的是另一个 SQL 慢查询问题日志脱敏完全没有进它的雷达。人工复核时这条被列为高优先级因为日志系统是 ELK 链路一旦日志进了采集器删除成本极高。这个案例说明了安全视角的审计和性能视角的审计在当前 AI 上仍然是两种相当独立的能力别指望一个工具全部覆盖。4.4 幽灵问题Codex 报了一个我查了半天不存在的 Bug这个是最有价值的反面案例。在用户模块Codex 报了一个 critical时间字段 create_time 存在时区转换错误。代码中使用北京时间字符串直接写入读取时按 UTC 解析会导致时间偏差 8 小时。建议统一使用 UTC 存储。我按 Codex 给的行号翻来覆去找了半天发现项目里的 ORM 框架在时间持久化时自动做了时区归一化应用层从来不直接处理时间字符串这个时区转换错误根本不存在。Codex 是看到create_time字段名和时间类型脑补了一个在别的项目里常见的 Bug 模式。这个案子告诉我们AI 报的高危问题里依然存在幻觉。单边报告必须人工复核哪怕它给了非常具体的行号和修复建议。反过来如果某个问题两个 AI 都报了那幻觉的可能性就大幅下降——所以双 AI 交叉验证其实也是在互相抑制幻觉。5. 为什么同一份代码两个 AI 会给出不同的审计结论5.1 训练语料和上下文窗口的差异Claude 在长上下文、跨文件推理和大段自然语言理解上的训练更充足这可能跟它在长文档任务上的侧重点有关所以它比较容易抓住一个数据从 A 方法流到 B 方法再到 C 方法的链条问题。Codex 延续了代码生成模型体系的特点在函数级、短距离的代码缺陷识别上反应更快边界条件的覆盖更细。它更像一个点状扫描器Claude 更像一个链状追踪器。5.2 审计粒度的差异函数级 vs 数据流级看同一份代码Codex 更关注这个函数内部有没有写坏Claude 更关注这个函数的数据从哪来、往哪去、中间有没有断链。这导致了双方的漏报方向不同Codex 容易漏掉跨服务的权限问题Claude 容易漏掉单个函数内的边界 bug。这也是为什么我会明确建议如果你只能用一个 AI 审计优先选 Claude 看安全和一致性选 Codex 看函数正确性和性能细节。但现实中最合理的方案还是双跑加交叉验证。5.3 保守程度与幻觉率的不同我做的另一个小统计在人工复核的 56 个单边报告里Claude 的确有其事率大约在六成多Codex 的确有其事率大约在五成上下。Claude 在不确定的时候倾向于降低严重等级或者在问题描述里加一堆此处可能引发的措辞Codex 倾向于果断下结论但代价是幻觉问题更刺眼。两边做了一个很有意思的对比维度Claude CodeCodex优势区跨文件数据流、权限校验链、状态机完整性函数内边界条件、参数校验、单点 bug长上下文表现稳定给整个模块也能保持聚焦容易在长上下文后段注意力漂移严重等级判断偏保守倾向降级偏果断容易升级幻觉风险较低但会出现措辞化问题更高会编造不存在的具体场景修复建议偏工程化考虑兼容性偏快速直接给最小改动这张表不是结论只是我这次实测的一种倾向。模型版本更新很快你的环境不同结果可能完全不同但两套模型不同偏科这件事短期内大概率不会变。6. 我最终沉淀的双 AI 审计工作流6.1 完整流程从模块拆解到修复闭环这次实验跑完我把流程固化成了团队里的一个标准操作。完整链路是把待审计代码按业务模块拆分成 500~2000 行的审计单元写一份固定审计 prompt内容覆盖正确性、安全、性能三类风险并强制要求输出行号和 PASS 结论用 Claude Code 和 Codex 分别对同一批模块并行跑审计把两份结构化输出合并成一张交叉对比表按共识问题 / 单边问题 / 幻觉问题分档处理人工复核单边高危问题确认后进入修复队列修复完成后让两个 AI 分别对 diff 做一次复检确认没有引入新问题。第 7 步很重要大部分人在审计完之后就收工了但修复本身也是一次代码变更AI 审计的价值在这里可以继续发挥。实测下来复检时 AI 对 diff 的聚焦能力比全量扫描更强因为它们能看到改动前后对比。6.2 分级处理策略我们最终采取的分级策略很直白红色项双 AI 共识 高危直接建工单不等人工复核因为共识高危的人工复核确认率接近百分之百黄色项单边报告 高危必须人工复核同时配合静态分析工具佐证ESLint 的 no-floating-promises、SonarQube 的规则集、npm audit 的依赖漏洞都是好帮手绿色项单边报告 低危不强制修记录到技术债清单里后续统一处理灰色项人工复核确认的幻觉报告标记后直接丢弃但不会删记录方便追溯和评估模型能力变化。6.3 避坑清单和实操建议最后是这次全套跑下来我踩过的坑和沉淀下来的建议每一条都有实际场景支撑Prompt 必须完全统一否则对比没有意义。我第一次跑的时候给 Claude 的 prompt 里多加了重点关注事务一致性给 Codex 的没加结果两边分歧巨大全部作废重跑。不要把整个仓库一次性塞进去。有人会觉得模型上下文窗口越来越大直接整个项目丢给它不就行了实测效果非常差注意力稀释导致报告全是正确的废话。模块化切片是性价比最高的做法。AI 说 PASS 不代表真的干净。我们的 20 个模块里有 4 个两边都 PASS 的模块人工复核时仍然抓出了一个横向越权问题。这说明 AI 审计只能做哨兵不能做裁判。修复建议不能直接 merge。前面提到的库存扣减案例就是最好的证例AI 给出的方案可能完全正确但未必适配你的系统。AI 的建议应该被当作提示而不是答案。保存审计产物便于追溯。每轮审计的输入 prompt、输出报告、人工复核结论都放在仓库的module_audit/目录下。这不仅是过程资产后续也可以拿来做模型能力评估或者喂给下一轮 prompt 做已知问题清单减少重复报告。这次做完整套双 AI 交叉审计我的一个核心体会是不要把 AI 审计想成谁比谁强的问题而要想成它们能互相抑制幻觉、互相补充盲区的问题。共识项可以大胆信单边项要带着问号去查PASS 项永远别轻信。三个臭皮匠顶个诸葛亮两个 AI 加上一个认真的工程师顶得上一个非常靠谱的代码走查专家。