Codex 完整教程:从安装到完成第一个小项目

用一份带有明确缺陷的商品校验器,走完第一次 Codex 协作。所有必要代码都在正文中,十五项测试帮助你独立判断修改是否正确。

你能带走什么
  • 先确认安装、认证和工作目录
  • 先只读调查与计划,再做小范围修改
  • 用测试、样本输出与 diff 验收
  • 掌握单文件回退后再扩展自动化

1. 第一回合,完成一个能自己验收的小项目

打开 Codex 之后,最值得先学会的是怎样把一项任务交代清楚,并判断它交回的结果是否可靠。本文从一份只有几个文件的商品校验器开始:先读代码、定位一个类型缺陷,再做小修改,最后通过测试和差异审阅决定是否接受。你不需要准备大型仓库,也不需要把公司的真实数据交给模型。

练习使用 Python 3 标准库,不需要安装第三方依赖。价格用整数分保存,库存用非负整数保存,零值合法,布尔值不合法。初始实现故意留下一个缺陷,测试已经写好。即使暂时无法调用 Codex,也能手工完成修复与验收;接入 Codex 的步骤会单独标清。

本文以本地 CLI 为操作入口。桌面应用、IDE 扩展和云端任务有各自的界面与运行环境,别把某个界面中的按钮名称直接当作终端命令。安装、登录和命令参数依据官方资料与本机帮助核对;练习的离线验证范围放在文末。

做这次练习时,建议把终端与编辑器并排打开。终端保留每一次测试的原始输出,编辑器负责阅读代码与差异。遇到一个不认识的命令,先读本文说明和本机帮助,再决定是否执行。整段复制提示词很方便,但你仍应知道其中允许修改什么、要求保留什么,以及哪一步会调用模型。

图 01 / 概念示意
从确认安装与登录开始,在独立练习目录建立基线;先只读调查和制定计划,随后做最小修改,再独立测试、审阅差异并决定保留或回退。
每一步留下一个可检查的结果;先学会完成一次小修改,再增加自动化。
查看图示文字说明

从确认安装与登录开始,在独立练习目录建立基线;先只读调查和制定计划,随后做最小修改,再独立测试、审阅差异并决定保留或回退。

2. 安装后,先确认终端实际找到了什么

已有 Node.js 和 npm 的读者可以采用官方 npm 安装方式。只选择一种安装来源,之后也通过同一来源维护。下面的 latest 表示安装当时的最新发布版本,无法保证将来与本文核对版本完全相同;安装后请记录实际版本并查看帮助。官方安装说明与官方 npm 更新示例提供了对应入口。

终端执行 · 需要已有 npm
npm install -g @openai/codex@latest
codex --version
codex --help

没有 npm 也可以从官方 CLI 页面选择安装器。当前文档提供 macOS、Linux 和 Windows 的安装选项。本文的多行 shell 示例统一采用 Bash,适用于 macOS、Linux 或已配置的 WSL 环境;Windows 原生用户请使用官方 Windows 安装与沙箱说明,不要把 Bash 的反斜杠续行直接粘贴到 PowerShell。

当前官方资料明确支持原生 Windows 沙箱,WSL 适合已有 Linux 工具链的工作流。两种环境中的项目路径、Python、Git 和 CLI 安装位置可能不同,尽量让一次练习都在同一环境中完成。Windows 沙箱文档包含平台要求和排障步骤。

本次读取到 codex-cli 0.159.2,表示本文核对的是这台环境中的该版本,并不表示它永远是最新版。若本机帮助与文章不一致,先核对版本及官方发布说明。遇到 command not found,检查安装终端、PATH 和是否需要重开终端,再考虑重新安装。

图 02 / 概念示意
CLI 安装、身份认证、工作目录是三项分别检查的条件。版本帮助输出证明程序可用;登录状态证明所用认证方式;目录与 Git 状态证明当前项目范围。
把安装、认证、目录分开核对,启动失败时也更容易定位。
查看图示文字说明

CLI 安装、身份认证、工作目录是三项分别检查的条件。版本帮助输出证明程序可用;登录状态证明所用认证方式;目录与 Git 状态证明当前项目范围。

3. 登录方式会影响账户与计费路径

个人交互练习可以运行下列命令,在浏览器中完成 ChatGPT 登录,再检查当前认证方式。登录状态检查不会替你证明每个模型都可用;组织策略、工作区访问和账户使用情况还会影响真实请求。本文不固定套餐价格、额度或默认模型名称。

需要真实账户时由你完成登录
codex login
codex login status

API key 登录使用 OpenAI Platform 的 API 计费与数据处理设置。只有在你已经选择这条路径,并通过可信方式把密钥注入当前环境时,才使用下面的 stdin 形式。不要把密钥字面量写进命令、截图、AGENTS.md 或项目文件,也不要把凭据文件复制到仓库。

可选:已有环境变量时使用;不要把真实 key 粘进命令
printenv OPENAI_API_KEY | codex login --with-api-key

无法完成浏览器回调的远程环境,可在符合账户和组织要求时按官方设备登录流程尝试 codex login --device-auth。设备码应只用于你本人刚启动的登录。遇到认证失败先保留不含秘密的错误文本,确认所选账户和工作区,别通过扩大文件权限解决登录问题。具体方式以认证文档为准。

4. 新建可信目录,避免把原项目带进练习

先在自己的开发目录下新建 codex-catalog-lab。下面使用普通 mkdir;若目录已经存在,停下来换一个新名字,别覆盖之前的工作。在这个目录之外保留密码、环境文件和私人资料。后续命令都从这个目录运行。

Bash · 新目录中执行
mkdir codex-catalog-lab
cd codex-catalog-lab
git init
python3 --version
git --version
pwd

将下一节的四份内容按文件名保存,再添加 AGENTS.md 和 .gitignore。文件名要保留扩展名,使用 UTF-8 文本。Windows 原生环境如果使用其他 Python 命令,需要同步调整后续测试命令和项目约定。先验证解释器能运行项目,再启动 Codex,能少掉一层排障变量。

Git 在这里仅用于记录练习的初始索引和查看差异,不需要配置提交身份。本文不会替你创建提交、推送仓库或部署服务。陌生仓库还可能包含安装脚本、钩子和自动执行配置;本篇只在你逐份读过的教学文件上练习。

5. 完整练习:两条商品与十五个测试

第一份 catalog.py 负责读取 JSON、检查字段并输出问题。is_count 中的 isinstance 判断是人为设置的教学缺陷:Python 的 bool 属于 int 的子类。先保留这一行,观察测试如何暴露它。这个项目只覆盖列出的字段规则,不把重复 SKU、未知字段或业务价格上限纳入此次需求。

catalog.py
"""Small catalogue validator. The starter has one intentional type bug."""
import argparse
import json
import sys
from pathlib import Path


def is_count(value):
    # Exercise bug: bool is a subclass of int in Python.
    return isinstance(value, int) and value >= 0


def validate(products):
    if not isinstance(products, list):
        raise ValueError("top level must be a list")
    issues = []
    for row, product in enumerate(products, start=1):
        if not isinstance(product, dict):
            issues.append({"row": row, "field": "*", "code": "not_object"})
            continue
        sku = product.get("sku")
        if not isinstance(sku, str) or not sku.strip():
            issues.append({"row": row, "field": "sku", "code": "required"})
        for field in ("price_cents", "stock"):
            if not is_count(product.get(field)):
                issues.append({
                    "row": row,
                    "field": field,
                    "code": "non_negative_integer_required",
                })
    return issues


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("input", type=Path)
    args = parser.parse_args()
    try:
        products = json.loads(args.input.read_text(encoding="utf-8"))
        issues = validate(products)
    except (OSError, ValueError) as exc:
        print(f"Input error: {exc}", file=sys.stderr)
        return 2
    print(json.dumps({"checked": len(products), "issues": issues}, indent=2))
    return 1 if issues else 0


if __name__ == "__main__":
    raise SystemExit(main())

将下面的人工样本保存为 products.json。第二条记录把价格写成布尔值 true,它应该被报告为问题。库存 0 则是合法值,修复时不能一并拒绝。

products.json
[
  {"sku": "NOTEBOOK", "price_cents": 1200, "stock": 4},
  {"sku": "MUG", "price_cents": true, "stock": 0}
]

保存完整的 test_catalog.py。它同时检查合法输入、零值、负数、布尔值、缺失字段和 CLI 退出码。测试自己构造临时输入,只使用标准库;真实 Codex 的只读调查阶段先不运行这些需要临时文件的测试。

test_catalog.py
import json
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path

from catalog import validate


def item(**changes):
    return {"sku": "MUG", "price_cents": 1200, "stock": 3, **changes}


def count_issue(field):
    return [{
        "row": 1,
        "field": field,
        "code": "non_negative_integer_required",
    }]


class CatalogueTests(unittest.TestCase):
    def test_valid(self):
        self.assertEqual(validate([item()]), [])

    def test_zero_is_valid(self):
        self.assertEqual(validate([item(price_cents=0, stock=0)]), [])

    def test_negative_price(self):
        self.assertEqual(validate([item(price_cents=-1)]),
                         count_issue("price_cents"))

    def test_negative_stock(self):
        self.assertEqual(validate([item(stock=-1)]), count_issue("stock"))

    def test_boolean_price(self):
        self.assertEqual(validate([item(price_cents=True)]),
                         count_issue("price_cents"))

    def test_boolean_stock(self):
        self.assertEqual(validate([item(stock=False)]), count_issue("stock"))

    def test_float_price(self):
        self.assertEqual(validate([item(price_cents=12.5)]),
                         count_issue("price_cents"))

    def test_string_stock(self):
        self.assertEqual(validate([item(stock="3")]), count_issue("stock"))

    def test_missing_stock(self):
        product = item()
        del product["stock"]
        self.assertEqual(validate([product]), count_issue("stock"))

    def test_blank_sku(self):
        self.assertEqual(validate([item(sku=" ")]),
                         [{"row": 1, "field": "sku", "code": "required"}])

    def test_non_object(self):
        self.assertEqual(validate([42]),
                         [{"row": 1, "field": "*", "code": "not_object"}])

    def test_top_level(self):
        with self.assertRaises(ValueError):
            validate({"sku": "MUG"})

    def run_cli(self, text):
        with tempfile.TemporaryDirectory() as directory:
            path = Path(directory) / "input.json"
            path.write_text(text, encoding="utf-8")
            return subprocess.run(
                [sys.executable, "-B", "catalog.py", str(path)],
                text=True, capture_output=True, check=False,
            )

    def test_cli_valid(self):
        result = self.run_cli(json.dumps([item()]))
        self.assertEqual(result.returncode, 0)
        self.assertEqual(json.loads(result.stdout),
                         {"checked": 1, "issues": []})

    def test_cli_finding(self):
        result = self.run_cli(json.dumps([item(stock=-1)]))
        self.assertEqual(result.returncode, 1)
        self.assertEqual(json.loads(result.stdout)["issues"],
                         count_issue("stock"))

    def test_cli_bad_json(self):
        result = self.run_cli("{")
        self.assertEqual(result.returncode, 2)
        self.assertIn("Input error:", result.stderr)


if __name__ == "__main__":
    unittest.main()

最后保存 .gitignore。它让 Python 缓存和后续日志不混入代码差异,但忽略规则不会阻止 Codex 读取这些文件,也不能保护秘密。

.gitignore
__pycache__/
*.pyc
runs/

6. 先跑一次失败,把起点留下来

先把下面这份 AGENTS.md 放到项目根目录,它明确输入规则、测试命令与任务范围。内容简短即可;此时还不需要覆盖团队未来可能遇到的全部工作。

AGENTS.md
# Catalogue practice

Use Python 3 standard library only. Work from the repository root.
The input is a JSON list. Each row is an object with a non-empty sku.
price_cents and stock must be non-negative integers; booleans are invalid.
Zero is valid. The CLI exits 0 for clean data, 1 for findings, 2 for bad input.

Run: python3 -B -m unittest -v
For the type-bug task, edit catalog.py only. Keep tests and fixtures unchanged.
Do not install dependencies or use the network for this exercise.
Do not commit, push, or deploy. Report actual commands and exit codes.

由你在终端执行测试和样本程序。-B 用来避免产生 Python 字节码缓存,测试自身仍会在系统临时目录创建并清理少量文件。每条命令之后立刻读取退出码,避免后面的命令覆盖它。

Bash · 人工运行基线
python3 -B -m unittest -v
printf "tests exit=%s\n" "$?"
python3 -B catalog.py products.json
printf "catalog exit=%s\n" "$?"

预期测试报告 Ran 15 tests 和 FAILED (failures=2),失败的是 test_boolean_price 与 test_boolean_stock,测试进程退出码为 1。初始样本程序却输出 checked 为 2、issues 为空,退出码为 0。这组冲突证明了待修复的漏报,不能把它误当成环境安装失败。

确认文件内容后,将五个明确文件加入 Git 索引,保存这一轮的比较基线。git add 在这里不创建提交,也不上传任何内容。初始 git status 会显示 A,表示文件已暂存;git diff 暂时应没有内容,因为工作区与索引相同。

仅限这个新建的练习仓库
git add catalog.py test_catalog.py products.json AGENTS.md .gitignore
git status --short
git diff --check
git diff -- catalog.py

本练习后续的 git diff 比较工作区与索引。不要在验收前重新 git add 修改后的文件,否则会改变这个比较起点。日常有提交历史的仓库可以采用自己的分支和提交检查点流程,同时保留已有未提交改动。

基线还提供了一种很实用的判断方式:同一组测试必须在修复前揭示缺陷,在修复后接受正确实现。如果两边都通过,测试可能没有覆盖问题;如果两边都失败,需要重新检查预期规则或环境。这次故意让测试先失败,是为了留下可重复对照的起点。

图 03 / 概念示意
products.json 是人工构造的两条商品输入;catalog.py 读取并校验;test_catalog.py 包含十五个确定性测试;AGENTS.md 约定工作方式。初始代码故意漏掉布尔值检查,修复后输出一条真实问题。
小项目刻意保留一个容易验证的缺陷,输入、规则和期望结果都在页面中给全。
查看图示文字说明

products.json 是人工构造的两条商品输入;catalog.py 读取并校验;test_catalog.py 包含十五个确定性测试;AGENTS.md 约定工作方式。初始代码故意漏掉布尔值检查,修复后输出一条真实问题。

7. 第一项 Codex 任务只读,先让它说清问题

下面这条命令会真正启动 Codex,并可能产生模型使用量。它在当前目录启动只读会话,审批策略为 never,超出当前权限的命令直接失败,不通过审批扩大权限。启动后先查看 /status 和 /permissions 展示的当前环境,再输入后面的调查任务。

启动真实交互会话 · 本文未执行模型调用
codex --sandbox read-only --ask-for-approval never
输入给 Codex 的第一条任务
只读检查这个练习项目,不改文件,不运行测试,不联网。
阅读 AGENTS.md、catalog.py、test_catalog.py 和 products.json。
找出为什么布尔值价格没有被拒绝,给出文件、函数和条件表达式。
说明零值应该如何处理,并列出你尚未验证的部分。

值得接受的回答应指向 is_count,说明 bool 与 int 的类型关系,并把判断依据对应到两个失败测试。只说类型校验有问题还不够;要求具体位置,才能让你判断它是否读过当前文件。若它声称测试通过,追问实际命令和输出,因为这个阶段并未要求它执行测试。

只读限制针对本地执行能力,仍然允许读取可访问内容并将相关上下文交给模型。MCP、连接器与其他工具还有各自权限,不能仅靠 read-only 推断它们全都只读。本练习无需外部工具,保持能力范围简单。

图 04 / 概念示意
只读调查使用 read-only 加 never,阻止通过审批扩大本地命令权限;编辑阶段使用 workspace-write 加 on-request,允许工作区写入并可能请求审批。任务文本限定 catalog.py 只是协作约定,真正的文件边界由权限系统执行。
读写限制、是否请求审批、哪些内容可以发送给模型,是需要分别考虑的事情。
查看图示文字说明

只读调查使用 read-only 加 never,阻止通过审批扩大本地命令权限;编辑阶段使用 workspace-write 加 on-request,允许工作区写入并可能请求审批。任务文本限定 catalog.py 只是协作约定,真正的文件边界由权限系统执行。

8. 确认一个短计划,再允许修改一个文件

在同一个只读会话里,先要求给出三步以内的修复计划,列出要改的文件、可能影响的行为和验收命令。对这个缺陷,计划应围绕整数类型判断,保留非负条件、现有输入输出结构以及三个退出码约定。无需引入新库或重写整个校验器。

继续只读讨论计划
先给修复计划,暂不修改。
范围只含 catalog.py 的整数判断,保留零值合法、负数非法。
保持测试与 products.json 不变,说明怎样验证没有扩大改动。

认可计划后退出只读会话,从同一目录启动允许工作区写入的新会话,并把已确认的范围重新写清。这里使用新会话,使启动参数容易复查;它不会自动继承上一会话的全部上下文。

允许编辑 · 逐条审阅额外权限请求
codex --sandbox workspace-write --ask-for-approval on-request
输入给 Codex 的修改任务
修复 catalog.py:price_cents 与 stock 必须是非负整数,拒绝 bool。
只改 catalog.py,保留零值合法,不改测试、样本和项目规则。
先读 AGENTS.md;若你发现需要扩大范围,停下来解释。
运行 python3 -B -m unittest -v,报告实际输出与退出码。
不要安装依赖、联网、提交、推送或部署。

workspace-write 提供的写入能力大于一个文件名单,提示词只是任务约定。你仍需检查最终差异和新增文件。on-request 表示模型可以提出审批请求,也不会保证每条命令都先问你。若组织强制使用 permission profiles,应按管理员配置运行;当前文档说明 profiles 与旧 --sandbox 配置不能随意混用。权限与执行边界有更完整的说明。

图 05 / 概念示意
调查阶段输出文件与失败证据;计划阶段输出最小修改及验证方法;编辑阶段只改 catalog.py;验收阶段由人重新运行测试并阅读差异。任何一步扩大范围都先停下确认。
先确认原因和计划,再给编辑许可;验收交给真实命令和逐行阅读。
查看图示文字说明

调查阶段输出文件与失败证据;计划阶段输出最小修改及验证方法;编辑阶段只改 catalog.py;验收阶段由人重新运行测试并阅读差异。任何一步扩大范围都先停下确认。

9. 由你复跑测试,再逐行看 diff

Codex 返回后,在自己的终端重新执行下列命令。测试成功只覆盖现有断言,git diff --check 只检查空白等问题;二者都无法单独证明业务逻辑完整。还要读实际差异,确认程序没有通过删测试、跳过断言或修改输入来获得绿灯。

人工验收 · 仍在练习根目录
git status --short
git diff --name-only
git diff --check
git diff -- catalog.py test_catalog.py products.json AGENTS.md
python3 -B -m unittest -v
printf "tests exit=%s\n" "$?"
python3 -B catalog.py products.json
printf "catalog exit=%s\n" "$?"

修复后的测试应报告 Ran 15 tests 和 OK,退出码为 0。样本程序应报告第二条记录的 price_cents,退出码为 1,含义是成功找出了数据问题。把校验器的业务退出码与 Codex 自身进程是否完成分开看,避免看到 1 就判断这次修复失败。

人工 fixture 的修复后期望输出
{
  "checked": 2,
  "issues": [
    {
      "row": 2,
      "field": "price_cents",
      "code": "non_negative_integer_required"
    }
  ]
}

最小修复可以把 is_count 改为下面的函数。这里提供的是人工参考答案,离线演示按这一行修改验证,不能把它写成 Codex 已替你完成的实测记录。没有模型访问的读者可手动替换同名函数,再复跑以上命令。

人工参考修复 · 替换 catalog.py 中的同名函数
def is_count(value):
    return type(value) is int and value >= 0

此时 git diff --name-only 应只列出 catalog.py;git status 中该文件可能显示 AM,表示索引保存了初始文件,工作区又产生修改。其他初始文件继续显示 A 属于预期。额外注意 ?? 开头的未跟踪文件,它们的完整内容不会自动出现在普通 git diff 中,需要单独打开查看。

可以再逐项对照最初需求:函数返回值是否仍为布尔结果,两个数字字段是否共用修复后的判断,负数和缺失字段是否仍然被拒绝,JSON 输出结构有没有变化。若实现扩大成一套新框架,要求解释必要性,并优先保留能满足当前验收的较小改动。

图 06 / 概念示意
测试日志应显示十五个测试从两失败变成全通过;代码差异应只涉及布尔值判断;样本输出应精确报告第二条价格字段;工作区状态应说明所有修改与新增文件。模型总结只帮助导航。
正确结果、充分测试、范围受控和完整差异共同支撑一次验收。
查看图示文字说明

测试日志应显示十五个测试从两失败变成全通过;代码差异应只涉及布尔值判断;样本输出应精确报告第二条价格字段;工作区状态应说明所有修改与新增文件。模型总结只帮助导航。

10. 不接受这次修改时,精确恢复目标文件

先保存要讨论的差异,再决定是否恢复。下面命令把 catalog.py 的当前 diff 写到仓库外,并从索引恢复该文件,会丢弃这个文件尚未暂存的修改。只在你确认基线没有被重新暂存、差异已保存、目标文件确实可以丢弃时执行;已有业务仓库不要照搬。

可选回退演练 · 确认要丢弃 catalog.py 的本轮修改后执行
patch_file=$(mktemp ../catalog-repair.XXXXXX)
git diff -- catalog.py > "$patch_file"
printf "Saved: %s\n" "$patch_file"
git restore --worktree -- catalog.py
git diff -- catalog.py
python3 -B -m unittest -v

按照本文索引基线,恢复后 diff 应为空,两个布尔值测试重新失败。这是回退成功的预期结果。若要继续后面的扩展练习,重新应用人工参考的一行修复并确认十五项测试通过。git restore 不负责清理新增文件;用状态清单逐个检查,别用整仓库强制重置或清理命令处理未知改动。git restore 官方文档解释了工作区与索引来源。

11. 把已经验证的项目约定留在 AGENTS.md

前面那份 AGENTS.md 已经足够支持首次协作。后续只有在项目的真实流程改变时,才补充测试命令、目录职责和完成条件。把临时需求留在当前任务里,避免每次进入项目都加载一长串过时规则。规则应能被命令和文件验证,不能只写认真检查之类的泛泛要求。

Codex 启动时会组合全局与项目指令,并沿项目根目录到当前工作目录查找适用文件;同层存在 AGENTS.override.md 时有优先关系。发现错误指令时,先检查启动目录、全局文件和嵌套覆盖文件,再重新启动会话确认。具体顺序见官方 AGENTS.md 文档;多层仓库实例见本站AGENTS.md 专题。

12. 重复检查用 Skill,外部资料再考虑 MCP

当同样的改后检查重复出现,可以把流程写成项目 Skill。新建 .agents/skills/catalog-check/SKILL.md,保存下面完整内容。name 与 description 是识别入口,正文只描述一次明确工作。启动后可通过 /skills 或 $catalog-check 选择它;若新内容没有出现,重新启动 Codex 核对目录。

.agents/skills/catalog-check/SKILL.md
---
name: catalog-check
description: Review this catalogue validator after a local code change.
---

1. Read AGENTS.md and the current diff. Do not change files.
2. Run python3 -B -m unittest -v from the repository root if permitted.
3. Check that zero is accepted and booleans are rejected.
4. Report changed files, test exit code, and any unverified behavior.
5. Do not commit, push, deploy, or install packages.

Skill 适合复用检查顺序,AGENTS.md 适合项目常驻约定。Skill 里的脚本也需要阅读和审查,不能因它带着一个熟悉名称就直接信任。加载规则与组织分发方式见官方 Skills 文档和本站Skills 专题。

当前 Python 练习完全不需要 MCP。以后需要查外部资料时,可以先了解官方只读文档服务。下列命令会新增一项本机 MCP 配置;先确定自己确实要添加,再手动执行。mcp list 能确认配置存在,真实连接还要在会话里调用文档工具验证。

可选扩展 · 会修改本机 Codex 的 MCP 配置
codex mcp add openaiDeveloperDocs \
  --url https://developers.openai.com/mcp
codex mcp list

文档服务提供搜索与页面内容,不会代你调用 OpenAI API。连接具有写入能力的其他服务时,要重新确认收件人、数据范围和动作许可。本文没有实际新增或连接 MCP;配置依据官方 Docs MCP,更多连接与排障细节见MCP 专题。

图 07 / 概念示意
AGENTS.md 保存项目常驻约定;Skills 保存按需使用的可复用流程;MCP 连接外部工具与数据。练习项目本身无需 MCP,扩展时从只读文档服务开始了解配置与权限。
把稳定约定、可复用流程、外部连接分开维护,避免第一天就堆满配置。
查看图示文字说明

AGENTS.md 保存项目常驻约定;Skills 保存按需使用的可复用流程;MCP 连接外部工具与数据。练习项目本身无需 MCP,扩展时从只读文档服务开始了解配置与权限。

13. 稳定后,再把同一件事放进无头流程

交互中能够稳定完成的任务,可以用 codex exec 接入脚本。先恢复到测试全通过的状态,保存下面的 check.prompt.txt 和 run-review.sh。提示文件只请求代码审查,不让模型运行测试;脚本会在模型调用结束后独立执行本地测试。

check.prompt.txt
只读审查 catalog.py、test_catalog.py 与 products.json。
检查整数规则是否拒绝布尔值、是否保留零值。
不要编辑文件,不要运行测试,不要联网或使用外部工具。
报告具体文件、函数、条件表达式和仍需人工验证的事项。
run-review.sh
#!/usr/bin/env bash
set -eu
mkdir -p runs
run_dir=$(mktemp -d runs/check.XXXXXX)
if codex -a never exec --sandbox read-only --color never --json \
    -o "$run_dir/final.txt" - < check.prompt.txt \
    > "$run_dir/events.jsonl" 2> "$run_dir/stderr.log"; then
  if [ ! -s "$run_dir/final.txt" ]; then
    printf 'Missing final message: %s\n' "$run_dir" >&2
    exit 2
  fi
else
  status=$?
  printf 'Codex exit %s; logs: %s\n' "$status" "$run_dir" >&2
  exit "$status"
fi
python3 -B -m unittest -v
printf 'Review files: %s\n' "$run_dir"

运行 bash run-review.sh 会真实调用模型。每次创建新的日志目录,分别保留 JSONL 事件、stderr 和最终回复,避免把上次的结果误认为本次输出。--json 改变的是事件输出格式,-o 保存的是最终消息;final.txt 有内容也不能证明意见正确,还需要你阅读。脚本本身不提交或发布改动。

接入 CI 时,先让现有流水线独立运行这十五个测试,固定 Python 版本和可信输入来源。以后增加模型审查,将凭据放在 CI 的受控秘密管理中,限制仓库 token 权限,并让测试验收与模型生成分开。不要把陌生 PR 的代码、规则文件或脚本直接放到持有模型密钥的执行环境中。

超时、结构化输出、重试、隔离 job 和远程密钥管理都有额外细节。本篇只给出到自动化的入口;可继续看官方非交互模式与本站Codex 无头与 CI 完整专题。实际采用前,先在受控环境运行一次并检查日志,不要把本篇离线结果当成远程 CI 已验证。

14. 常见卡点,按证据逐层排查

看到 Permission denied,先看被拒的是哪个路径、哪条命令,以及当前是否仍在只读会话。测试需要临时文件时,可由你在可信练习目录运行,或采用适当的工作区权限。不要添加绕过审批与沙箱的危险参数来掩盖目录或配置错误。组织管理的机器还可能有无法由用户覆盖的策略。

出现 Ran 0 tests,通常先检查是否在项目根目录、test_catalog.py 是否保存正确、命令是否原样执行。出现 ModuleNotFoundError: catalog,也先核对文件名和执行位置。真实测试日志应该能看到十五项测试名称;仅收到一句全部通过时,要求补充日志并自己再跑。

测试变绿但样本仍不对,打开测试与 fixture 的差异,确认断言没有被删除、expected 没有被改成错误值。类型缺陷修复后,价格 true 应被拒绝、价格 0 和库存 0 应保持合法。若连最小例子都解释不清,先停下扩大任务。

执行中断以后,文件可能已经部分修改。先查看状态和差异,再决定继续会话、给出更小任务或恢复文件。恢复聊天上下文不会自动回滚代码,也不会让此前执行的外部动作消失。遇到认证、限流或网络错误时,按对应错误修复账户和连接,不要反复在未知工作区状态下重跑修改任务。

向同事或工具求助时,整理一份最小复现:系统与 CLI 版本、当前目录、执行命令、退出码,以及删去敏感信息后的错误行。只提供与故障有关的文件片段。完整环境变量、认证文件和私人目录不属于排障必需资料;日志在转发前也应检查是否夹带这些内容。

图 08 / 概念示意
命令找不到时看 PATH 与安装位置;认证失败看登录方式与组织要求;权限拒绝看路径和沙箱;测试未运行检查目录、解释器和命令;输出与测试不一致时保存现场并独立复查。
排障从能看到的错误和文件状态开始,避免先扩权限或反复重跑。
查看图示文字说明

命令找不到时看 PATH 与安装位置;认证失败看登录方式与组织要求;权限拒绝看路径和沙箱;测试未运行检查目录、解释器和命令;输出与测试不一致时保存现场并独立复查。

15. 本文验证到了哪里

2026 年 10 月 5 日核对了官方文档及本机 CLI 0.159.2 的 help/version 输出。Python 3.12.14 下,人工教学 fixture 的十五项测试先出现两个预期失败,人工一行修复后全部通过;样本 CLI 从漏报变为报告一条问题。Git 索引比较、差异检查与单文件恢复也在新临时仓库中实际验证。

示例 Python 已做语法检查,Bash 脚本通过 bash -n;无头脚本另以模拟 CLI 检查控制流,不代表真实模型执行。八张图片均为概念信息图,已检查文字与布局。本次未执行真实登录、安装、模型请求、MCP 连接或远程 CI,也未提交、推送或部署代码。网页未做浏览器整页实测。

完成这次练习后,你应该能说明输入是什么、预期行为是什么、改了哪些行、哪些测试实际运行过,以及怎样撤回本轮改动。带着这套证据习惯进入真实项目,任务范围可以逐步扩大,验收标准仍然保持具体。

16. 参考资料

  1. Codex CLI 安装与入口
  2. npm 安装与版本确认示例
  3. 认证方式与凭据处理
  4. Windows 原生沙箱
  5. 权限配置与边界
  6. AGENTS.md 指令发现
  7. Skills 编写与加载
  8. OpenAI Docs MCP
  9. 非交互执行
  10. Git 单文件恢复
没有模型额度也能跟着做吗?

可以完成所有 Python 离线练习、人工一行修复、Git 差异与恢复。Codex 对话和无头命令需要可用认证与模型服务,本文没有承诺任何账户的额度。

为什么修复后 catalog.py 反而退出 1?

本练习把 1 定义为发现输入数据问题。第二条商品价格是 true,修复后应被正确报告;单元测试全部通过时,测试进程仍退出 0。

git diff 为空,是否说明没有任何新文件?

普通 git diff 不展示未跟踪文件的完整内容,也不展示索引中已暂存的差异。结合 git status --short 检查;本练习要求保留初始索引,暂不重新 git add 修复结果。

要先安装 MCP 和 Skills 才能用 Codex 吗?

这个本地练习无需外部服务。先完成一轮可验收修改;当相同检查需要复用时再写 Skill,需要外部资料或工具时再评估 MCP。

能把这套步骤直接用于陌生仓库吗?

先审查仓库中的指令、脚本和执行配置,并设计隔离。本文采用自己逐份阅读的教学文件,不提供运行不可信仓库的完整安全方案。