使用 Codex 自动修复一个真实 Bug,并运行测试验证

用 codex exec 修复开源库 humanize 的真实 issue #239:先写失败测试,再让 Codex 定位根因,246 秒完成,源码改动与上游 PR #240 一致。附配置、提示词、用量拆分和踩坑记录。

4 分钟阅读
使用 Codex 自动修复一个真实 Bug,并运行测试验证

先说结果。我们拿开源库 python-humanize/humanize 的真实 issue #239 做实验,用 codex exec 非交互跑了一次:

项目结果
修复前(全量测试)2 failed, 599 passed, 24 skipped
修复后(全量测试)601 passed, 24 skipped
Codex 改动1 个文件、1 行。源码改动与上游 PR #240 一致(上游另外加了 6 条测试,本文只加了 2 条)
耗时246 秒
模型gpt-5.6-sol(通过 AI Code With 接入)
Token共 19 次模型请求,输入累计 517,604(其中缓存命中 484,608),输出 4,572(含推理 987)

过程中踩了三个坑,下文写了每个坑的现象和解决办法。

适合谁

  • 已经在用 Codex CLI,想把"修 bug + 跑测试"交给它、又拿不准效果的开发者;
  • 想在 CI 或脚本里用 codex exec 做无人值守修复的人;
  • 想知道一次真实修复要花多少 token 的人。

准备工作

第 1 步:选一个真实 Bug

issue #239 描述的问题:humanize 4.11.0 里 naturalsize(1.123456789) 返回 1 Bytes,升级到 4.12.0 后变成了 1.123456789 Bytes。

检出修复合并之前的提交,也就是 PR #240 合并提交的父提交:

bash
git clone https://github.com/python-humanize/humanize.git
cd humanize
git checkout a0602c70bb6e11322107b51114333bab7adab021
python3 -m venv ../venv && . ../venv/bin/activate
pip install -e . pytest freezegun pytest-cov
坑 1:只装 pytest、不装 pytest-cov 时,pytest 会直接报 PytestConfigWarning: Failed to import filter module 'coverage',测试根本起不来。原因是这个项目的 pytest 配置引用了 coverage,补装 pytest-cov 就行。

第 2 步:先写失败测试

在 tests/test_filesize.py 的 test_naturalsize 参数表里加两行:

python
        ([1.123456789], "1 Bytes"),
        ([1.123456789, False, True], "1B"),

运行全量测试:

text
$ python -m pytest -q
FAILED tests/test_filesize.py::test_naturalsize[test_args62-1 Bytes] - AssertionError: assert '1.123456789 Bytes' == '1 Bytes'
FAILED tests/test_filesize.py::test_naturalsize[test_args63-1B] - AssertionError: assert '1.123456789B' == '1B'
2 failed, 599 passed, 24 skipped in 0.92s

先提交测试,之后好单独看 Codex 的 diff:git commit -am "test: reproduce #239"。

整件事能验证,前提是先让测试变红,再让 AI 改代码。

第 3 步:配置 Codex 走 AI Code With

Codex 接第三方 provider,把配置写进全局配置 ~/.codex/config.toml,Key 直接用 experimental_bearer_token 写在 [model_providers.<id>] 里,不用设环境变量:

toml
# ~/.codex/config.toml
model_provider = "aicodewith"
model = "gpt-5.6-sol"

[model_providers.aicodewith]
name = "aicodewith"
base_url = "https://api.aicodewith.ai/chatgpt/v1"
wire_api = "responses"
experimental_bearer_token = "sk-你的Key"
改之前先备份原配置:cp ~/.codex/config.toml ~/.codex/config.toml.bak。如果原配置里有 env_key 这一行,记得删掉,不要和 experimental_bearer_token 同时用。本文实验当时是用环境变量传 Key 跑的,写进全局配置后接口、模型都一样。

另一种写法:AI Code With 的接入文档用的是 ~/.codex/auth.json(写入 OPENAI_API_KEY),再在 provider 里设 requires_openai_auth = true,适合日常交互使用。两种写法任选一种,不要混用。

更多字段说明见 Codex config.toml 配置详解。

第 4 步:用 codex exec 非交互修复

提示词原文:

text
tests/test_filesize.py 里有 2 个失败用例,对应 issue #239:naturalsize(1.123456789) 在 4.12.0 返回 "1.123456789 Bytes",期望 "1 Bytes"(gnu=True 时期望 "1B")。请找到根因,做最小修改修复 src/ 下的代码,不要修改测试。修复后运行 python -m pytest -q 确认全部通过,最后用中文简述根因和改动。

命令:

bash
codex exec --sandbox workspace-write --skip-git-repo-check --json "$(cat prompt.txt)" \
  < /dev/null > codex-run.jsonl
坑 2:第一次运行没加 < /dev/null。在脚本或后台环境里,Codex 打印 Reading additional input from stdin... 之后就一直挂着,10 分钟没有任何输出,最后只能手动结束。非交互运行时,记得把 stdin 接到 /dev/null。

第 5 步:Codex 做了什么

从 --json 日志看,它依次做了这几件事:

  1. 读 src/humanize/filesize.py 和测试文件;
  2. 用 git blame 和 git log -p 查历史,发现旧实现用 %d 格式化,后来改成 f-string 时丢了取整语义。这次改动来自上游提交 fb61d5a("Update pre-commit (#226)",2025-01-07);
  3. 做最小修改:
diff
     if abs_bytes < base:
-        return f"{bytes_}B" if gnu else f"{bytes_} Bytes"
+        return f"{int(bytes_)}B" if gnu else f"{int(bytes_)} Bytes"

这处源码改动和上游维护者在 PR #240 里的改动一致。

坑 3:Codex 在沙箱里执行 python -m pytest 时报 command not found(exit 127)。它的 shell 是 bash -lc 登录 shell,没有继承我激活的 venv,而 workspace-write 沙箱默认又禁网,装不了 pytest。它的处理是改用标准库解析测试参数表,逐条断言(128 条通过),并在总结里如实说明全量 pytest 没跑成。
解决办法:在提示词里写明测试命令的绝对路径,比如 ../venv/bin/python -m pytest -q。不管怎样,合并前都要自己再跑一遍测试。

第 6 步:自己验证

text
$ python -m pytest -q
601 passed, 24 skipped in 0.92s

用了多少 token

turn.completed 事件给出的用量:

text
{"input_tokens":517604,"cached_input_tokens":484608,"cache_write_input_tokens":0,"output_tokens":4572,"reasoning_output_tokens":987}

几点说明:

  • 51 万输入 token 是 19 次模型请求的累加,单次最大约 3.4 万;
  • 缓存写入是 0;
  • 输入里 94% 命中缓存。Codex 每次请求模型都会重发上下文,所以这类任务的输入大部分能命中缓存。

常见报错

现象原因处理
Reading additional input from stdin... 后卡住非交互环境下 stdin 没关闭命令末尾加 < /dev/null
沙箱内 python: command not found 或没有 pytest沙箱 shell 不继承 venv,且默认禁网提示词里写 venv 的绝对路径
Failed to import filter module 'coverage'项目的 pytest 配置依赖 coveragepip install pytest-cov
401 UnauthorizedKey 错误,或 ~/.codex/config.toml 里的 Key 没填对见 Codex 401 排查
长时间无响应或超时网络或上游超时见 Codex 超时四类排查

经验总结

  • 先写一个红的测试,Codex 才有明确的目标和验收标准;
  • 提示词里写清"最小修改、不要改测试",本次 Codex 照做了;
  • 它说"测试通过"不能直接当真。--json 日志里每条命令、退出码和 token 都有记录,看完日志再自己复跑一遍。

相关教程

相关文章

Claude Code 怎么控制 Unreal Engine?Epic 官方插件安装、MCP 连接与安全设置(UE5 实操)

Claude Code 怎么控制 Unreal Engine?Epic 官方插件安装、MCP 连接与安全设置(UE5 实操)

Epic Games 在 Anthropic 官方插件市场提供了 Claude Code Unreal Engine 插件 unreal-engine-skills-for-claude-code,配合编辑器里的 Unreal MCP,就能让 Claude Code 直接操作 UE5 编辑器。本文讲清编辑器端要开哪两个插件、怎么启动 MCP 服务、Claude Code 端怎么装插件和验证连接,以及 execute_tool_script 等安全设置。我们用 AICodeWith 的 Key 实测:插件 2.5 秒装好,全程不需要登录 Anthropic 账号。