Codex 项目上下文怎么写:用 AGENTS.md、任务边界和验收条件提高代码修改质量
Codex 项目上下文怎么写:用 AGENTS.md、任务边界和验收条件提高代码修改质量
摘要:Codex 能读取项目文件、修改代码并运行命令,但它并不会自动知道团队约定。本文用一个小型 Node.js 项目说明:如何编写
AGENTS.md、怎样描述任务边界、如何设置验收条件,以及怎样检查 Codex 生成的修改。示例不依赖特定业务代码,可以按需替换。
先说结论
让 Codex 更稳定地完成项目任务,重点不是把提示词写得特别长,而是补齐三类信息:
- 项目事实:目录结构、技术栈、常用命令和代码约定。
- 任务边界:允许修改什么,哪些文件不能动。
- 验收条件:需要运行哪些测试,怎样才算完成。
其中,AGENTS.md 适合保存跨任务复用的项目规则;具体需求仍应写在当前任务中。两者分开后,项目规则不必反复粘贴,临时要求也不会污染长期配置。
本文适合已经有代码仓库,希望用 Codex 辅助理解项目、修复缺陷或完成小型功能的开发者。
Codex 在项目里能做什么
OpenAI 将 Codex 定义为面向软件开发的编码智能体。根据使用环境和已授予的权限,它可以阅读仓库、编辑文件、执行命令和运行测试。
这并不意味着它能自动理解所有业务背景。对于一个陌生仓库,下面这些信息通常无法只靠文件名准确推断:
- 哪个目录是正式实现,哪个目录只是历史代码;
- 团队使用哪套格式化和测试命令;
- 能否增加新的依赖;
- 哪些公共接口必须保持兼容;
- 修改完成后需要提供什么验证证据。
如果这些条件没有提前说明,模型可能做出“代码能运行,但不符合项目约定”的修改。
准备一个用于演示的小项目
下面使用一个简单的 Node.js 目录作为示例:
demo-service/
├─ AGENTS.md
├─ package.json
├─ src/
│ ├─ price.js
│ └─ index.js
└─ test/
└─ price.test.js
为了让示例可以直接运行,package.json 使用 Node.js 自带的测试工具:
{
"name": "codex-context-demo",
"private": true,
"type": "module",
"scripts": {
"test": "node --test",
"lint": "node --check src/price.js && node --check test/price.test.js"
}
}
src/price.js 中有一个计算折后价的函数:
export function calculateFinalPrice(price, discountRate) {
return price * (1 - discountRate);
}
当前实现没有校验参数。如果传入负数价格,或者把折扣比例写成 1.5,函数仍会返回结果。我们的任务是补充参数校验,同时不改变正常输入的返回值。
这个示例很小,但它包含真实项目里常见的几个问题:修改范围、异常类型、兼容性和测试。
AGENTS.md 应该写哪些内容
AGENTS.md 可以理解为交给 Codex 的项目说明。它适合记录相对稳定、多个任务都会使用的规则,而不是塞入某一次需求的全部细节。
可以先从下面这份精简模板开始:
# Project instructions
## Project overview
- This is a Node.js service using ES modules.
- Source code is in `src/`.
- Tests are in `test/`.
## Commands
- Run tests: `npm test`
- Run lint: `npm run lint`
## Code conventions
- Keep exported function names backward compatible.
- Use `TypeError` for invalid argument types.
- Use `RangeError` for values outside the accepted range.
- Do not add dependencies unless the task explicitly requires it.
## Change boundaries
- Do not edit generated files.
- Keep changes limited to files related to the current task.
- Do not rewrite unrelated code for style consistency.
## Verification
- Run the relevant tests after editing.
- Report the files changed and the commands executed.
- If a command cannot run, explain why instead of claiming it passed.
这份文件不长,但每一项都能影响最终结果。
1. 项目概况
项目概况只需要写会改变执行方式的信息。例如运行环境、主要语言、源码目录和测试目录。没有必要把 README 全部复制进来。
2. 常用命令
明确测试、格式化、类型检查和构建命令,可以减少 Codex 猜测命令的情况。命令必须与仓库实际配置一致。
3. 代码约定
这一部分适合写能够判断对错的规则,例如异常类型、接口兼容性和依赖策略。“代码要优雅”过于抽象,不如写成可检查的条件。
4. 修改边界
边界能够防止任务范围扩大。修复一个函数时,通常不需要同时整理整个目录、升级依赖或重写公共接口。
5. 验证方式
验证规则决定 Codex 在修改后做什么。最好要求它列出实际运行的命令,并区分“测试通过”和“未能执行”。
当前任务应该怎么描述
有了 AGENTS.md,还需要为本次修改写清楚任务。下面是一份可以直接改写的提示:
检查 `src/price.js` 中的 `calculateFinalPrice`。
目标:
- 拒绝非数字参数。
- `price` 必须大于或等于 0。
- `discountRate` 必须位于 0 到 1 之间,包含边界值。
边界:
- 不修改函数名称和参数顺序。
- 不增加第三方依赖。
- 不修改与本函数无关的文件。
验收:
- 为有效输入、类型错误和范围错误补充测试。
- 运行相关测试。
- 最后说明修改了哪些文件、运行了什么命令,以及测试结果。
开始修改前,先阅读现有实现和测试,再给出简短计划。
这段任务描述没有规定每一行代码应该怎么写,而是定义了结果和边界。模型仍有实现空间,但完成标准是明确的。
一个可能的修改结果
根据前面的要求,函数可以改成:
export function calculateFinalPrice(price, discountRate) {
if (typeof price !== "number" || typeof discountRate !== "number") {
throw new TypeError("price and discountRate must be numbers");
}
if (!Number.isFinite(price) || !Number.isFinite(discountRate)) {
throw new RangeError("price and discountRate must be finite");
}
if (price < 0) {
throw new RangeError("price must be greater than or equal to 0");
}
if (discountRate < 0 || discountRate > 1) {
throw new RangeError("discountRate must be between 0 and 1");
}
return price * (1 - discountRate);
}
对应的测试可以覆盖正常输入和边界条件:
import test from "node:test";
import assert from "node:assert/strict";
import { calculateFinalPrice } from "../src/price.js";
test("calculates the final price", () => {
assert.equal(calculateFinalPrice(100, 0.2), 80);
});
test("accepts boundary discount rates", () => {
assert.equal(calculateFinalPrice(100, 0), 100);
assert.equal(calculateFinalPrice(100, 1), 0);
});
test("rejects non-number arguments", () => {
assert.throws(
() => calculateFinalPrice("100", 0.2),
TypeError
);
});
test("rejects negative prices", () => {
assert.throws(
() => calculateFinalPrice(-1, 0.2),
RangeError
);
});
test("rejects discount rates outside the accepted range", () => {
assert.throws(
() => calculateFinalPrice(100, 1.1),
RangeError
);
});
这里需要注意:示例代码展示的是一种实现思路,不代表所有项目都应该使用相同的异常类型。项目已有约定应优先于通用示例。
为什么要让 Codex 先检查再修改
如果任务一开始就要求“直接修复”,模型可能在没有了解上下文时做出假设。更稳妥的流程是:
- 阅读相关实现、测试和项目规则;
- 说明问题位置和计划修改的文件;
- 完成最小范围修改;
- 运行与修改相关的验证命令;
- 检查差异并总结仍未验证的风险。
“先检查”并不是要求输出很长的分析,而是避免在错误假设上继续写代码。对于不熟悉的仓库,这一步尤其重要。
如何检查 Codex 生成的修改
AI 编程助手生成的代码仍然需要人工复核。建议至少检查下面五项。
检查 1:修改范围是否合理
查看实际差异,确认没有改动无关文件、生成文件或锁文件。如果任务只是修复参数校验,却出现大范围格式化,就应该先缩小修改。
检查 2:公共接口是否保持兼容
检查函数名、参数、返回值和异常行为。某些异常变化也可能影响调用方,不能只看测试是否通过。
检查 3:测试是否真的运行
“建议运行测试”不等于“测试已经通过”。结果说明中应包含实际命令和退出结果。如果受到环境限制,应明确标记未验证项。
检查 4:是否引入新的安全问题
重点检查文件路径、外部输入、命令执行、权限变化和敏感信息。不要把密钥、令牌或真实生产数据直接写进提示词、测试和提交记录。
检查 5:是否存在过度实现
一个小需求不一定需要新框架、新依赖或大规模重构。修改越大,评审和回归成本通常越高。
常见的无效写法
只写“帮我优化代码”
“优化”可能指性能、结构、可读性或安全性。没有指标时,模型只能猜测。
可以改为:
只优化 `parseConfig` 的错误处理。
保持函数签名不变,不增加依赖。
为缺失字段和非法 JSON 增加测试。
把所有规则塞进一次提示词
长期项目规则反复粘贴,容易出现不同版本。相对稳定的内容放在 AGENTS.md,当前需求留在任务提示中,更容易维护。
要求修改,但不给验收条件
没有测试、类型检查或构建要求,结果只能停留在“看起来正确”。至少应指定一个与任务相关的验证命令。
要求一次修改整个仓库
范围过大时,问题定位和结果评审都会变难。可以先让 Codex 梳理影响范围,再拆成几个可独立验证的小任务。
一份通用的 Codex 任务模板
下面的模板适合修复缺陷、增加小功能或进行局部重构:
任务:
[用一句话说明要解决的问题]
背景:
- 相关文件:[路径]
- 当前行为:[实际情况]
- 期望行为:[目标情况]
边界:
- 允许修改:[文件或目录]
- 不允许修改:[公共接口、配置、依赖等]
- 不执行破坏性操作。
验收:
- [必须通过的测试或检查]
- [需要覆盖的边界条件]
- [最终需要提供的结果说明]
执行方式:
1. 先阅读相关文件并给出简短计划。
2. 完成最小范围修改。
3. 运行验证命令。
4. 检查差异并汇报未验证风险。
常见问题
AGENTS.md 是不是写得越详细越好?
不是。它应该保留长期有效、能改变执行结果的规则。重复说明、过期命令和互相冲突的要求会增加理解成本。可以从一页以内的版本开始,再根据真实失误补充。
每个子目录都需要 AGENTS.md 吗?
不一定。只有当子目录存在明显不同的构建方式、测试命令或修改边界时,才值得增加局部说明。具体作用域和优先关系应以当前 Codex 版本的官方文档为准。
Codex 运行测试后还需要人工检查吗?
需要。自动测试只能覆盖已编写的断言,不能自动证明需求完整、接口兼容或不存在安全风险。人工评审和版本控制仍然是必要环节。
可以让 Codex 直接修改生产环境吗?
不建议把生产环境作为默认操作对象。应优先在受控仓库、分支或隔离环境中完成修改和验证,并按照团队发布流程上线。
总结
Codex 的使用效果,很大程度取决于项目上下文是否清楚。AGENTS.md 负责提供可复用的项目规则,当前任务负责说明本次目标,验收条件负责判断工作是否真正完成。
如果准备在现有仓库中使用 Codex,可以先做一件小事:选一个边界清楚的函数,写明允许修改的文件和测试命令,完成一次“检查—修改—验证—复核”的闭环。这个过程比一次性添加大量规则更容易发现哪些上下文真正有用。资料来源于环球巴士
更多推荐

所有评论(0)