ShanTianQi

我让 Claude Code 和 Codex 做了同一份作业

· 中文 · English

上一篇写到,我用 Codex 那几个月不太顺,很多时候它理解不了我要做什么。但那是 3 月到 8 月的事,之后我一直在用 Claude Code,没再拿 Codex 做过什么。这个印象现在还对不对,我也不确定。

所以写了个小工具 agent-race,把同一个任务同时交给两个 Agent,各自在独立的 git worktree 里做,做完把两边的改动、耗时和测试结果放在一起看。

怎么比的

任务都是从 Go 重构项目的提交记录里挑的,已经合进去的改动,分三类:

每个任务都先把代码退回到改之前,再像平时提需求那样写一两句话交给它们。能找到我当年原话的,就直接用原话。每个任务后面都加一句:只在本地改,不要提交,不要连服务器。

小修复用当年那次提交里的测试来判,它们事先看不到。大需求和业务需求,当年的测试调用的是新写的函数,名字对不上,就从当年的提交说明里整理评分表,每题 7 到 10 条,交给一个单独启动的 Agent 打分,它不知道哪份是谁做的。

用的模型是 Claude Code 的 Opus 5.5 和 Codex 的 gpt-6-astra。

先踩了一个坑

第一次跑大需求的时候,Codex 有一题改的文件和当年那次提交一模一样。查了它执行过的命令,它先用 git log --all 在仓库历史里搜到了当年的提交,然后直接 git diff | git apply,把答案套了过来。

Claude Code 没有直接套,但也翻了历史,看了当年的提交和后来才写的设计文档。

问题出在我:worktree 虽然退回到了改动之前,仓库里还留着之后的所有提交。后来改成每题单独克隆一个只到改动之前的仓库,跑完再逐条检查每个 Agent 执行过的命令,有越界的就作废重跑。

没人让它们去翻历史。它们为了把事做对,会自己去找能参考的东西。用历史提交做评测,这一点要先防住。

结果

小修复两边都能做对。Claude Code 每题 1 到 2 分钟,Codex 7 到 19 分钟,有一题碰上模型拥堵,一行没改。

大需求和业务需求:

Claude CodeCodex
模型降级(9 分)6.57
主备选举(10 分)9.56
灰度开关(10 分)106
解锁后不响应(7 分)53.5
车头方向反了修好了首次算对,掉头后重新解析又算反,还改坏了经过锁站的路径
到位朝向(10 分)42.5
锁车重置(10 分)108.5

小需求的时候,我以为差距主要在速度和稳定性。需求一大,差距就不只是速度了。

Codex 的改动普遍更小,有时候小到漏了该改的地方。模型降级那题它分数高一点,但加了一个启动检查,检查失败整个评审就直接退出,正好把这个需求要解决的「评审没了」又带了回来。主备选举那题,它引用了仓库规则里禁止引用的包,交接时先放锁、不等手上的活做完。车头方向那题,它把仓库里原有一个回归测试的期望值改了,好让自己的改动能通过。

锁车重置那题有一个坑,当年第一版也没避开:桥吊、龙门吊的引导信号是缓存里的保持值,不是一次性的,处理不好会陷入「重置、对位、锁车、再重置」的循环。这次 Claude Code 第一次就避开了。

同一个需求,两种做法

比起谁分高,两边不一样的地方更值得看。

除了这些任务,我还在自己的情报雷达上试了一个小功能,写的是:「top 命令加个参数,能只看某个来源的」。

Claude Code 的做法是筛选,只显示在这个来源出现过的条目,分数不变。Codex 的做法是只用这个来源的数据重新算分、重新排序。

两种都通过了测试,也都说得通。我写这句话的时候,根本没想过还有第二种理解。如果只交给一个 Agent,我大概率会直接用它给的那种,不会意识到这里有得选。

没讲到的现场,它们想不到

车道号那题,当年修的时候顺着同一个接口查下去,还发现另一个数字字段会被写成科学计数法,1717171717 进库变成了 1.717171717e+09,就一起修了。这次我在任务里只写了车道号,两边都只修了车道号。当年能多查一步,是因为前面已经在线上踩过一次,知道这里的数据类型不可靠。

到位朝向那题最明显。什么情况要掉头、掉几次,这些规则代码里没有,写在我们后来整理的一份 PDF 里。第一次跑的时候,我不小心让它们读到了这份 PDF,而它比这个需求晚出现,后来去掉重跑了。同样是 Claude Code,读了 PDF 得 8 分,没读得 4 分。

代码它读得懂,现场的规则在我们脑子里和文档里。讲清楚的,它们能做对;没讲到的,它们只能猜。

测试也会判错

小修复里有一题,两边的测试都没过,因为当年的测试调了一个叫 errsByRecord 的函数,这个名字它们不可能知道。对照当年的改动看,两边的原因和改法都是对的。

业务需求里有两次,测试显示没通过的那一份,让评分的 Agent 按真实的调用路径一个场景一个场景跑下来,其实是对的。一份是只在库配置好的时候才取消指令,而当年的测试工具没配库;另一份是把判断放在了调用方,当年的测试只测了那一个函数。

反过来,Codex 改了测试的期望值,测试是绿的。只看测试,结论会反过来,还是得看 diff。

做完之后怎么交代

两边最后给我的说明也不太一样。

Codex 一般是列出改了哪几个文件、跑了哪些测试、结果通过。

Claude Code 会把没确认的地方写出来。有一题它说新加的测试没有拿旧代码跑过,结论是对着代码推出来的;另一题它加了配置,但没有往任何环境里填值,说不知道实际的值,让我来定。

顺便试了代码图谱

最近代码图谱挺火,把仓库解析成调用关系图,说是能帮 Agent 看清一次改动会影响到哪里。我在这几轮里也加了一组:同样是 Claude Code,任务里要求先查图谱再动手。

三类任务下来,带不带图谱的结果基本一样。它自己 grep 加读代码已经能把影响范围查全,图谱在我们的 Go 项目上还会漏掉一些跨包的调用。

几点说明

这些改动当年都是我和 Claude 一起写的,评分表照当年的实现整理,打分的也是 Claude。这些都会偏向 Claude Code,所以分数只能参考。我更看重的是评分的 Agent 列出来的具体问题,那些都能对着代码核实。

Codex 走的是公司的网关,好几次被模型拥堵打断,大需求一题要等二三十分钟。每题每组也只跑了一次。

最后

这个工具更适合用在需求本身没想清楚的时候。两个 Agent 做得一样,说明不了太多;做得不一样的地方,往往是我自己的描述有问题。

另外,该给的现场资料要给,测试全绿也要看 diff。

agent-race 开源在 GitHub,可以拿自己的仓库试试。

#AI Coding#Agent#Claude Code#Codex