跳到主内容
快讯直播
AI智模界
教程

用 Codex 搭建自动修 Bug 并提交 PR 的智能体

做完这套东西,你会得到一个这样的工作流:在仓库里给某个 Issue 打上一个 auto-fix 标签,几分钟后出现一个 PR——里面有复现 bug 的新测试、一处最小改动的修复、一份机器人自己写的审查意见,以及跑绿的 CI。你唯一要做的事是点开 PR,看一眼 diff,点 Merge 或者打回去。

这篇文章不教你训练模型,只教你把 Codex 这类编码智能体嵌进一个真实仓库的日常流程里。整个过程可以拆成三件事:让仓库变得"机器可读"把权限收紧到刚好够用把"改代码"拆成复现、修复、审查、提交四段并分别约束

前置条件清单

动手前先确认这几件事,缺一件后面都会卡住:

  • 一个有像样测试的仓库。 没有测试的仓库跑自动修 Bug,等于让一个从没读过代码的新人闭着眼睛改生产代码。这是整套方案能否成立的分水岭,比任何提示词技巧都重要。
  • 一条固定的测试命令。 比如 make testnpm testpytest,退出码能反映成功失败,本地和 CI 跑出来结果一致。
  • 能跑 CI 的平台。 下文以 GitHub Actions 举例,其他平台逻辑一样。
  • Codex 的可用访问方式。 Codex 有终端 CLI 和云端任务等多种形态,具体开通方式、可用模型、命令参数请以官方文档为准。
  • 一个专用机器人身份的令牌。 不要用你自己的账号。令牌权限收窄到"可以推分支、可以开 PR",明确不给"直接推 main"的能力。
  • 分支保护规则已开启。 main 分支要求 PR + 至少一个人工 approve + CI 通过才能合并。这是最后一道闸门,必须提前设好。
  • 一个能复现 bug 的最小环境。 最好用容器或 devcontainer 固化依赖,避免"在我机器上能跑"。

第 1 步:把仓库整理成智能体看得懂的样子

智能体不是靠猜,它是靠读。你仓库里如果有一堆同名的测试文件、三套并存的构建脚本、README 里写着过时的跑法,它就会踩坑。

先做三件小事:

1. 统一入口。 把所有构建、测试、lint 命令收进 Makefilepackage.json 的 scripts,智能体只需要记住几条命令。

2. 测试可以单独跑。 支持"只跑某个文件里某个用例",否则智能体改一行代码等十分钟,循环不起来。

3. 跑通一次干净环境。 从空目录 clone 下来,按 README 从头做一遍,把漏掉的步骤补进脚本。

```makefile

Makefile 示例

.PHONY: setup test test-one lint

setup: ## 安装依赖,CI 和本地都用这条

pip install -r requirements-dev.txt

test: ## 跑全部测试

pytest -q

test-one: ## 用法:make test-one T=apps/orders/tests/test_refund.py

pytest -q $(T)

lint:

ruff check . && black --check .

```

第 2 步:写 AGENTS.md,相当于给智能体的入职手册

Codex 会读取仓库里的 AGENTS.md 作为长期指令。这份文件的质量,直接决定它第一稿的准确率。放在仓库根目录,覆盖全仓库的规矩;如果某个子目录有特殊约定,可以在子目录再放一份,就近的那份优先(具体查找规则以官方文档为准)。

写得好的 AGENTS.md 长这样:

```markdown

AGENTS.md

这个仓库是什么

Django + Celery 的订单服务。核心业务代码在 apps/orders/

API 层在 apps/api/,不要跨层直接调用 models。

常用命令

  • 安装依赖:make setup
  • 跑全部测试:make test任何改动之后都必须跑这一条
  • 跑单个测试:make test-one T=<路径>
  • 风格检查:make lint

改代码的规矩

1. 先写一个能复现问题的失败测试,再改实现。顺序不能反。

2. 一次只改一个逻辑点,不顺手重构、不顺手升级依赖、不顺手改格式。

3. 不要修改 apps/*/tests/ 下已有测试的断言来让测试通过。

如果认为测试本身写错了,停下来汇报,不要自己改。

4. 不要新增第三方依赖。确实需要时,先输出理由并停止。

5. 改动超过 5 个文件就停下来汇报,不要继续。

6. 只使用仓库里已有的工具函数和 API,不要凭记忆写不存在的接口。

代码从哪里看起

  • 退款逻辑:apps/orders/services/refund.py
  • 订单状态机:apps/orders/models.py
  • 金额相关工具:apps/common/money.py

不要碰的地方

  • migrations/:除非任务明确要求,否则不生成迁移文件。
  • .env*secrets/:任何情况下都不要读取或修改。

```

注意第 3 条和第 5 条。它们是在堵两个最常见的失败模式:改测试让它通过,和改动范围失控

第 3 步:把权限收紧到刚好够用

这一步比提示词重要。给智能体的权限,应该是"能在工作区里读写代码、能跑测试",而不是"能访问网络、能推任何分支、能读环境变量"。

Codex CLI 的配置大致分两块:审批策略(哪些命令需要你确认)和沙箱模式(它能写到哪、能不能联网)。具体字段名和取值以官方文档为准,下面是结构示意:

```toml

~/.codex/config.toml —— 字段名与取值请以官方文档为准

model = "<按官方文档填写你账号可用的模型>"

审批策略:决定哪些操作需要人工确认

无人值守场景通常设成"失败时再上报"或"仅工作区内自动执行"

approval_policy = "on-failure"

沙箱:只允许写当前工作区,不允许写到工作区之外

sandbox_mode = "workspace-write"

[sandbox_workspace_write]

默认断网。需要装依赖时临时打开,装完立刻关掉。

network_access = false

```

再叠加两层隔离:

  • 令牌层:机器人令牌只给仓库的 contents: writepull-requests: write,不要给 admin、不要给 workflows 写权限。
  • 仓库层:main 开启分支保护,要求 PR + CI 绿 + 人工 approve。这样即使智能体抽风想直接推 main,也会被平台挡下来。

一个实用技巧:把"装依赖"和"跑智能体"分成两个阶段。第一个阶段联网装依赖,第二个阶段断网跑智能体。这样它既装得上包,又不会偷偷去查什么东西或者把代码发到外面。

第 4 步:定义输入契约——Issue 模板

自动修 Bug 最大的浪费,是花二十分钟修一个根本没描述清楚的问题。用一个模板把输入卡死:

```markdown

---

name: 可自动修复的 Bug

about: 打上 auto-fix 标签后,机器人会尝试自动修复

labels: ["bug", "auto-fix"]

---

现象

结算页选择"部分退款"后,退款金额显示为 99.99,实际应为 100.00。

复现步骤

1. 创建订单,金额 199.99

2. 发起部分退款,退款比例 50%

3. 查看退款记录金额

期望行为

退款金额 = 100.00(四舍五入到分)

实际行为

退款金额 = 99.99

可能相关的模块

退款计算,apps/orders/services/refund.py

是否稳定复现

是,三次都复现

```

"是否稳定复现"这一栏很有用。不稳定的 bug 不要让机器人碰,它会一路改到把整个模块重写。

第 5 步:第一段——只复现,不修复

这是整套流程里最值钱的一步。 让智能体先把 bug 变成一个失败的测试,确认它真的失败了,再允许它动实现代码。

这样做有两个好处:一是防止它"幻觉式修复"——改了一堆无关代码,测试正好过了;二是失败测试本身就是证明修复有效的证据。

提示词示例(注意把"不许改实现"写死):

```text

你现在的角色是复现者,不是修复者。

任务来源:Issue #1234(内容见文末)

允许你做的事:

  • 阅读仓库里任何文件
  • 在 apps/orders/tests/ 下新增测试文件
  • 运行 make test-one T=<你新增的测试文件>

禁止你做的事:

  • 修改任何非测试文件
  • 修改任何已存在的测试文件
  • 新增第三方依赖

请按顺序完成:

1. 写出一个最小测试,能反映 Issue 里描述的期望行为。

2. 运行它,把完整命令和输出贴出来。

3. 如果它没有失败,说明你没有真正复现,立刻停止并汇报,

不要猜测、不要继续修改代码。

输出格式:

状态:<REPRODUCED | NOT_REPRODUCED>

新增文件:<路径>

运行的命令:<命令>

失败信息摘要:<关键几行>

```

如果返回 NOT_REPRODUCED,直接终止这一轮,在 Issue 里留言说明复现失败。别让它硬猜,猜出来的修复一定不可靠。

第 6 步:第二段——最小改动修复,循环跑测试

复现成功之后进入修复。这里的核心约束是"一次一个逻辑点 + 每次改完都跑全量测试",避免它一口气改五个地方,测试过了也不知道是哪个改动生效的。

```text

你现在的角色是修复者。

输入:上一步新增的失败测试文件(已在工作区),Issue #1234 的原始描述。

目标:让 make test 全部通过,且改动尽可能小。

工作方式(必须遵守):

1. 先读懂失败测试断言的期望行为,再看 apps/orders/services/refund.py

里对应的实现,用一句话说明你认为问题出在哪。

2. 只改一个逻辑点。改完立刻运行 make test

3. 如果测试仍然失败,回退这一处改动,重新分析,不要叠加第二处改动。

4. 最多尝试 5 轮。第 5 轮还不过,停止,输出:

  • 你已经排除的假设
  • 你怀疑但没验证的方向
  • 需要人补充的信息

5. 不要修改测试文件里的断言。不要为了让测试通过而吞掉异常、

加 try/except 兜底或返回默认值。

全部通过后,输出:

  • git diff --stat 的结果
  • 你改了哪几处、每处为什么改
  • make test 的最终输出摘要

```

第 4 条那个"最多 5 轮"很关键。没有上限的循环会烧掉大量时间和额度,而且往往在第 3 轮之后就开始瞎改了。

第 7 步:第三段——换个身份审查 diff

让同一个上下文继续审自己的代码,效果很差,它会觉得自己的改动理所当然。要么开一个全新的会话,要么明确让它切换成"只读审查者",且不给它修改权限。

```text

你现在的角色是严格的代码审查者。你只能读,不能改任何文件。

给你一份改动 diff 和一份已经跑绿的测试结果。

请逐条检查并输出结论:

1. 改动范围:有没有改到与 Issue 无关的文件?

2. 测试诚信:有没有修改已有测试的断言?有没有跳过、

xfail 或者注释掉测试?

3. 异常处理:有没有新增的 try/except 把异常吃掉?

有没有为了通过测试而返回默认值、空列表或 None?

4. 边界情况:金额、时间、分页、空集合、并发这几类,

这次改动会不会引入新的边界问题?

5. 可读性:命名是否与周围代码一致?有没有引入新的重复逻辑?

6. 有没有引入新的依赖或新的外部调用?

最后给出一行结论:

VERDICT: APPROVE

VERDICT: REQUEST_CHANGES

如果是后者,列出必须修改的点,按严重程度排序。

```

审查环节不需要 100% 正确,它的作用是把明显的问题挡在人工 review 之前,让人类看到的是一个已经过一遍筛子的 PR。

第 8 步:第四段——生成 PR

前三步都过了,再走提交。用脚本固化,别让智能体自由发挥 git 命令。

```bash

#!/usr/bin/env bash

.github/open_pr.sh

set -euo pipefail

ISSUE_NUMBER="${ISSUE_NUMBER:?需要设置 ISSUE_NUMBER}"

BRANCH="auto-fix/issue-${ISSUE_NUMBER}"

每次都从最新的 main 拉分支,避免在旧代码上做修复

git fetch origin main

git checkout -b "$BRANCH" origin/main

git add -A

git commit -m "fix(orders): 修正部分退款金额四舍五入错误 (#${ISSUE_NUMBER})"

只推 PR 分支,绝不推 main

git push -u origin "$BRANCH"

gh pr create \

--base main \

--head "$BRANCH" \

--title "fix: 修正部分退款金额四舍五入错误 (#${ISSUE_NUMBER})" \

--body-file .github/pr_body.md \

--label "auto-fix" \

--reviewer "your-team"

```

.github/pr_body.md 里建议固定写清楚三件事:关联的 Issue机器人自审的结论人工需要重点看的地方。比如:

```markdown

关联

Closes #1234

改动说明

  • 新增失败测试:apps/orders/tests/test_refund_rounding.py
  • 修改:apps/orders/services/refund.py 中部分退款金额的计算顺序,

改为先四舍五入到分再做比例运算

自审结论

VERDICT: APPROVE

审查者已确认:未改动已有测试、未新增异常兜底、改动 2 个文件

请人工重点确认

  • 金额精度策略是否符合财务口径(是否应该用 Decimal 而不是 float)

```

第 9 步:接上 CI,让它无人值守地跑

把上面几段串成一条流水线,用 Issue 标签触发:

```yaml

.github/workflows/auto-fix.yml

name: auto-fix-bug

on:

issues:

types: [labeled]

jobs:

fix:

if: github.event.label.name == 'auto-fix'

runs-on: ubuntu-latest

timeout-minutes: 30

permissions:

contents: write # 推分支需要

pull-requests: write # 开 PR 需要

issues: write # 回写评论需要

steps:

  • uses: actions/checkout@v4 # 版本号以该 Action 的官方页面为准

with:

fetch-depth: 0

阶段一:有网,装依赖

  • name: 安装依赖

run: make setup

阶段二:跑智能体,这一步建议断网(以官方文档为准配置沙箱)

  • name: 调用 Codex 非交互模式

run: |

具体子命令与参数以官方文档为准

codex exec "$(cat .github/prompts/fix.md)"

env:

OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

ISSUE_NUMBER: ${{ github.event.issue.number }}

ISSUE_BODY: ${{ github.event.issue.body }}

阶段三:提交 PR

  • name: 提交 PR

run: bash .github/open_pr.sh

env:

GH_TOKEN: ${{ secrets.BOT_TOKEN }}

ISSUE_NUMBER: ${{ github.event.issue.number }}

```

注意 permissions 是收窄的,timeout-minutes 是防止卡死烧额度,if 条件是防止别人随便打个标签就触发。

常见坑与排错

改测试让它通过。 最典型的翻车方式。表现是 diff 里出现了对已有断言的修改,或者新增了 @pytest.mark.skip。处理办法:AGENTS.md 里明文禁止 + 审查环节专门检查 + CI 里加一条"测试文件是否被修改"的检查,改了就让人工确认。

改动范围失控。 一个改金额四舍五入的任务,最后动了 12 个文件,顺带升级了依赖。处理办法:AGENTS.md 写死文件数量上限,审查环节第一项就查范围。

反复修不好,越改越乱。 设置最大尝试轮数,超了就停下来输出"已排除的假设",而不是继续瞎试。有时候停下来交给人的信息,比它再跑十轮有用。

测试不稳定(flaky)。 智能体会追着一个随机失败的测试乱改。动手前先把 flaky 测试隔离掉,或者用固定随机种子。

依赖装不上。 八成是环境没固化。用容器或 devcontainer 把依赖钉死,make setup 一定要能在干净环境里跑通。

幻觉 API。 它会写 import 一个仓库里根本不存在的模块。AGENTS.md 里要求"只用仓库里已有的工具函数",审查环节检查新增的 import。

权限给太大。 有人图省事直接给了个人账号的完整令牌。一旦智能体理解错了任务,它能做的事情远超你的预期。永远用最小权限的机器人身份。

密钥泄露。.env 放进工作区,智能体读进去之后可能写进日志或提交。.gitignore 要好,AGENTS.md 里把 .env* 列为禁区,CI 里注入密钥而不是放文件。

上下文太长。 仓库很大时它会迷路。AGENTS.md 里写清"代码从哪里看起",把关键文件路径列出来,比让它自己 grep 整个仓库高效得多。

下一步建议

先用这套流程跑最低风险的任务:lint 报错修复、类型标注补全、依赖小版本升级、日志信息补充。这些任务有明确的"对错"判定,不需要业务判断,跑通了再上真正的 bug 修复。

跑顺之后可以做三件事:一是把成功的案例回填进 AGENTS.md——每次它犯的错,都变成一条新规矩;二是加度量,记录"自动 PR 的合并率"和"人工在合并前需要改多少行",前者衡量可靠性,后者衡量质量,比看它跑了多少次有用得多;三是扩展到更复杂的任务,比如给已有功能加测试、把重复代码抽出来,但每次扩展前都问自己一句:这个任务的正确性能被测试验证吗?

最后提醒一句:无论自动化做得多好,main 分支的合并权永远保留在人类手里。这套系统的定位是把你从"找问题、写复现、搭架子"里解放出来,而不是替你做决定。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。