Codex总是改多了?用AGENTS.md和自动测试限制修改边界
一名开发者给Codex的任务只有一句话:
修复优惠券为空时接口报错的问题。
十分钟后,Bug确实修了,但项目里多出了这些变化:
- 一个新的工具函数;
- 两个文件被统一格式化;
- 原有导出名称被调整;
- 测试框架配置被重写;
- 顺手升级了一个依赖;
- 与Bug无关的类型定义也被修改。
每一项单独看都能解释,组合在一起却让代码审查变得很难:到底哪一行是必要修改,哪一行只是AI认为“顺便优化一下”?
这类问题不能只靠一句“不要乱改”解决。
更可靠的工程方法是把控制拆成四层:
仓库长期规则:AGENTS.md
当前任务边界:任务提示
结果正确性:自动测试和静态检查
修改必要性:Git Diff人工审查
AGENTS.md负责告诉Codex这个项目长期怎么工作,任务提示负责限制这一次要做什么,自动化命令负责证明代码能运行,Git Diff负责确认它没有越界。
一、AGENTS.md究竟解决什么问题
根据Codex官方文档,Codex会在开始工作前读取AGENTS.md,并根据所在目录建立一条指令链。
它适合保存长期有效的仓库信息,例如:
- 项目使用什么运行环境;
- 安装、测试和Lint命令是什么;
- 哪些目录不能随意修改;
- 是否允许增加生产依赖;
- 公共接口能否重命名;
- Bug修复必须补什么测试;
- 交付时需要报告哪些验证结果。
AGENTS.md不是每次任务都要重新粘贴的超长提示词,更像是“写给代码代理看的项目协作说明”。
官方文档列出的主要加载逻辑可以概括为:
全局AGENTS.md
↓
项目根目录AGENTS.md
↓
当前路径中的子目录AGENTS.md
Codex从项目根目录向当前工作目录逐层查找规则。同一条规则发生冲突时,更接近当前目录的文件会在合并后出现得更晚,因此优先级更高。
例如:
shop-api/
├── AGENTS.md
├── src/
│ └── pricing.js
└── services/
└── payments/
├── AGENTS.override.md
└── refund.js
根目录的AGENTS.md可以要求所有服务运行通用测试;payments目录中的AGENTS.override.md则可以补充支付业务的特殊限制。
但要注意:AGENTS.md是行为指导,不是操作系统级的权限隔离。
它可以要求Codex不要修改某个目录,却不能代替文件权限、沙箱、审批机制和CI检查。如果某项规则绝对不能被绕过,应当通过权限、测试、Hooks或CI进行机械约束。
Codex官方AGENTS.md说明:
https://learn.chatgpt.com/docs/agent-configuration/agents-md
二、先准备一个可以复现的Bug
下面使用一个不依赖第三方库的Node.js示例。
项目结构:
codex-scope-case/
├── AGENTS.md
├── package.json
├── src/
│ └── pricing.js
└── tests/
└── pricing.test.js
package.json:
{
“name”: “codex-scope-case”,
“private”: true,
“scripts”: {
“test”: “node --test”,
“lint”: “node --check src/pricing.js && node --check tests/pricing.test.js”
}
}
原始的pricing.js:
“use strict”;
function calculateTotal(items, coupon) {
const subtotal = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
const percent = coupon.percent;
return Number(
(subtotal * (1 - percent / 100)).toFixed(2)
);
}
module.exports = { calculateTotal };
有优惠券时,这段代码可以正常运行:
calculateTotal(
[
{ price: 80, quantity: 1 },
{ price: 60, quantity: 2 }
],
{ percent: 10 }
);
// 180
没有传入优惠券时:
calculateTotal(
[
{ price: 80, quantity: 1 },
{ price: 60, quantity: 2 }
]
);
程序会访问undefined.percent,最终抛出TypeError。
问题并不复杂,真正需要控制的是:修复这个Bug时,有没有必要重写价格模块、修改导出形式或者增加一个新的依赖?
答案显然是否定的。
三、给项目写一份能执行的AGENTS.md
可以在项目根目录创建下面这份文件:
Repository expectations
Runtime and commands
- Use Node.js 20 or newer.
- Run
npm testafter behavior changes. - Run
npm run lintbefore reporting completion.
Change boundaries
- Prefer the smallest change that fixes the reproduced failure.
- Do not add production dependencies without explicit approval.
- Do not rename public exports unless the task explicitly requires it.
- Do not modify unrelated files or reformat the whole repository.
Verification and handoff
- Add or update a regression test for every bug fix.
- Report the root cause, changed files, commands run, results, and remaining risks.
- If a required command cannot run, state the exact blocker; do not claim success.
这份规则不长,但每一条都能对应到具体行为。
- 写清楚真实命令
“修改后请测试”太模糊。
Codex可能不知道应该运行哪个测试,也可能只检查代码表面是否合理。写成npm test和npm run lint后,完成条件变得可以执行。
- 写清楚不能顺手做什么
“保持代码整洁”很容易被理解成允许大范围格式化和重构。
“不要修改无关文件”“不要重命名公共导出”“增加生产依赖前需要明确批准”,边界更加具体。
- 要求交付证据
不要只让Codex说“已经修复”。
要求它列出根因、修改文件、运行命令和测试结果,开发者才能快速判断这次交付是否可信。
四、AGENTS.md不能替代当前任务提示
AGENTS.md应该保存长期规则,不适合塞入只针对某一个Bug的临时要求。
这次任务可以这样写:
目标:
修复src/pricing.js在coupon未传入时抛出TypeError的问题。
复现条件:
calculateTotal(items)不传第二个参数时失败。
修改范围:
只修改解决该问题所需的文件。
不要重构价格计算流程,不要新增依赖,不要修改公共导出名称。
验证要求:
- 保留有优惠券时的现有行为;
- 新增“coupon缺失时返回原始小计”的回归测试;
- 运行npm test;
- 运行npm run lint;
- 检查git diff和git diff --check。
交付格式:
说明根因、修改文件、验证结果和剩余风险。
这段提示把官方推荐的四类信息都补齐了:
| 信息 | 本案例内容 |
| Goal | 修复coupon为空时的TypeError |
| Context | src/pricing.js及现有计算行为 |
| Constraints | 不重构、不加依赖、不改公共导出 |
| Done when | 测试、Lint、Diff检查全部完成 |
模糊任务通常会迫使Codex自己补充假设。任务边界越清楚,它越容易把精力放在真正需要修改的位置。
五、什么叫“最小修改”
针对当前Bug,核心修改只需要一行:
修改前:
const percent = coupon.percent;
修改后:
const percent = coupon?.percent ?? 0;
完整结果:
“use strict”;
function calculateTotal(items, coupon) {
const subtotal = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
const percent = coupon?.percent ?? 0;
return Number(
(subtotal * (1 - percent / 100)).toFixed(2)
);
}
module.exports = { calculateTotal };
这个修改保留了原有结构:
- 没有创建新模块;
- 没有修改函数名称;
- 没有修改导出方式;
- 没有引入依赖;
- 没有重写金额计算逻辑;
- 没有处理任务范围外的优惠券校验规则。
这里最后一点很重要。
开发者可能会想到继续限制百分比必须位于0到100之间,但当前需求只是修复coupon缺失。如果项目还没有定义非法百分比应该抛错、截断还是忽略,Codex就不应该擅自决定业务规则。
最小修改不是代码行数越少越好,而是:
每一项修改都能直接对应已确认的需求或验证要求。
六、Bug修复必须有回归测试
测试文件使用Node.js内置的node:test,不需要安装第三方测试框架:
“use strict”;
const test = require(“node:test”);
const assert = require(“node:assert/strict”);
const {
calculateTotal
} = require(“…/src/pricing”);
const items = [
{ price: 80, quantity: 1 },
{ price: 60, quantity: 2 }
];
test(“applies a percentage coupon”, () => {
assert.equal(
calculateTotal(items, { percent: 10 }),
180
);
});
test(“returns the subtotal when coupon is missing”, () => {
assert.equal(
calculateTotal(items),
200
);
});
test(“does not mutate the input array”, () => {
const before = structuredClone(items);
calculateTotal(items, { percent: 10 });
assert.deepEqual(items, before);
});
运行测试:
npm test
验证结果:
tests 3
pass 3
fail 0
再执行语法检查:
npm run lint
本案例中,node --check会分别检查业务文件和测试文件的JavaScript语法。
这组示例已在Node.js环境中实际运行,3项测试全部通过,Lint命令也正常完成。
七、测试通过还不够,必须检查Diff
自动测试回答的是“已覆盖的行为有没有通过”,Git Diff回答的是“究竟改了什么”。
可以依次执行:
git status --short
git diff --stat
git diff – src/pricing.js tests/pricing.test.js
git diff --check
四个命令分别解决不同问题:
| 命令 | 主要用途 |
| git status --short | 查看哪些文件发生了变化 |
| git diff --stat | 快速识别修改规模是否异常 |
| git diff – 文件路径 | 逐行审查核心文件 |
| git diff --check | 检查空白错误和冲突标记等问题 |
理想情况下,这个任务只应该涉及:
M src/pricing.js
M tests/pricing.test.js
如果Diff里出现package.json、锁文件、README或者其他业务模块,就应该停下来追问:
- 这项修改是否解决当前Bug所必需?
- 是否属于AGENTS.md禁止的无关改动?
- 是否需要拆成另一个任务?
- 是否应该回退这部分变化?
Codex本身也提供代码审查能力。在Git仓库中,可以使用/review检查未提交改动、指定提交或相对基础分支的差异。官方说明指出,专用Review流程会报告有优先级的发现,而不会直接修改工作树。
官方代码审查说明:
https://learn.chatgpt.com/docs/code-review
八、把AGENTS.md分层,而不是无限加长
当项目越来越大,根目录AGENTS.md很容易变成几百行的规则集合,最后既难维护,也容易让关键约束被淹没。
更合理的组织方式是:
shop-api/
├── AGENTS.md
├── src/
│ ├── catalog/
│ └── payments/
│ ├── AGENTS.override.md
│ └── refund.js
└── tests/
根目录保留全项目通用规则:
AGENTS.md
- Run npm test after behavior changes.
- Do not add production dependencies without approval.
- Preserve public API compatibility.
支付目录保存局部高风险规则:
src/payments/AGENTS.override.md
- Do not change rounding behavior without a documented business decision.
- Never log card data, tokens, passwords, or complete payment payloads.
- Run npm run test:payments after changing payment logic.
- Every payment-state change requires a rollback or compensation-path review.
这样做有两个好处:
第一,普通模块不会被支付业务的特殊规则干扰。
第二,当Codex在payments目录工作时,更接近该目录的规则会覆盖或补充根目录说明。
官方文档还提到,Codex通常在一次运行或一次TUI会话开始时建立指令链。如果修改了AGENTS.md但当前任务仍在沿用旧规则,可以重新启动会话,在目标目录确认加载情况。
九、哪些内容不应该写进AGENTS.md
AGENTS.md很重要,但并不适合承载所有信息。
不要写临时任务需求
“今天只修复第238号Issue”属于当前提示,不是长期仓库规则。
不要写无法执行的口号
例如:
写出世界上最好的代码。
始终保证百分百没有Bug。
所有代码都必须完美。
这些表达没有可验证标准,无法帮助Codex判断完成状态。
不要堆入大量格式规则
格式、Lint和类型检查能够交给工具时,应尽量由CI和命令执行,而不是让AGENTS.md塞满几十条缩进与换行要求。
不要存放敏感数据
密码、Token、生产数据库地址、用户隐私和内部密钥都不应该写进AGENTS.md或任务提示。
不要把规则文件当权限系统
真正禁止写入的目录应通过沙箱、系统权限、审批策略或自动检查保护。提示词只能降低越界概率,不能提供强制安全边界。
十、把“别改多了”变成可以检查的流程
一套适合真实项目的最小修改闭环,可以固定为:
- 读取AGENTS.md
- 复现问题
- 确认根因
- 提出最小修改计划
- 修改必要文件
- 添加回归测试
- 运行最相关测试
- 运行Lint或类型检查
- 检查Git Diff
- 报告结果和风险
还可以在任务提示中加入下面这段通用要求:
先定位根因并说明最小修改计划,再开始编辑。
修改后运行最相关测试和项目规定的检查命令。
检查git status、git diff和git diff --check。
如果出现与任务无关的文件变化,先停止并说明原因。
最终输出:
- 根因;
- 修改文件;
- 验证命令与结果;
- 未覆盖风险。
这段要求比“认真一点”“不要乱改”“帮我全部检查好”更有效,因为每一步都有可观察结果。
十一、工具订阅不是代码质量保证
AGENTS.md、自动测试和Diff审查解决的是工程流程问题,不是订阅充值问题。无论使用ChatGPT Plus、Codex、Claude Pro、Cursor还是Kiro,工具都不会自动理解每个项目的业务边界,更不会天然替代测试和人工Review。
如果有ChatGPT Plus、Claude Pro、Grok、Gemini Advanced等会员充值需求,可以通过gpt328了解。它是第三方AI会员充值平台,解决的是订阅充值流程问题,不是代码托管、API中转或自动测试平台。使用前应看清套餐说明、账号要求、到账说明和售后规则。
真正决定AI编程结果是否可信的,仍然是上下文、约束、验证和审查。
十二、最终结论
Codex“改得太多”,通常来自四类问题:
- 仓库没有提供长期工程规则;
- 当前任务只写目标,没有写修改边界;
- 测试命令没有进入完成标准;
- 交付前没有审查Git Diff。
AGENTS.md可以让Codex进入仓库时先理解项目规则,但它不是安全沙箱,也不能替代CI。
一套更稳妥的分工是:
| 控制层 | 解决的问题 |
| AGENTS.md | 项目长期如何工作 |
| 当前任务提示 | 这一次允许改什么 |
| 自动测试和Lint | 修改后是否满足已知要求 |
| Git Diff审查 | 是否出现无关或高风险改动 |
| 权限、沙箱和CI | 对关键边界进行机械约束 |
当“不要改多了”被拆成明确规则、验证命令和审查步骤后,Codex的修改才会从“看起来完成了”变成“可以验证、可以解释、可以交付”。
更多推荐


所有评论(0)