# 34 · 综合实战：从零给一个 TODO 小工具加功能、提交一次

> 📚 **系列导航**：上一篇〔[33 Windows 使用要点](33-windows.md) 〕把 Windows 上那些容易卡住人的坑——路径、换行符、终端、沙箱差异——一条条理顺了。这一篇咱们不讲新功能，干一件更过瘾的事：**把前面三十几篇学的零件，亲手装成一台能跑的整机**。下一篇〔[35 命令与配置速查表](35-cheatsheet.md) 〕再把全篇用到的命令、配置键一张表收口，当工具书随时翻。

兄弟们，今天不上课，咱们一起从零做一个小东西。

我手上有个特别朴素的命令行小工具——一个 Python 写的 `todo.py`，能添加待办、列出待办，就这俩功能。它有个明摆着的缺：**加完的事，干完了删不掉，只能眼睁睁看着列表越堆越长**。这一篇，咱们就把「删除待办」这个功能，从交代背景、派活、设权限，一路做到自验证、`git` 提交，**完整走一遍**。

说白了，前面每一篇都在讲「某一个零件怎么用」——`AGENTS.md` 怎么写、提示词怎么提、权限怎么设、`git` 怎么交给它。单看都懂，但真到一个活儿上，**这些零件该按什么顺序、怎么咬合**，很多人是懵的。这一篇就是那张「装配图」。

我不会假设你电脑上正好有这个项目。**第 01 节我带你两分钟把这个 `todo.py` 建出来**，之后每一步你都能照着我敲，敲完亲眼看到结果。这是一篇「跟做」文章，不是「看懂」文章——光看不动手，等于没读。

**看完这一篇，你会拿到：**

- 一条从零到提交的完整开发链路，每一步都对应前面某一篇，串成肌肉记忆
- 一份能照抄的 `AGENTS.md`、一句结构完整的派活提示词、一组真实命令和预期输出
- 「让 Codex 自己跑测试验证、不达标不收工」的实操姿势
- 一次干净的 `git` 提交是怎么交给 Codex、又由你把最后那道关的
- 一张「这一步在用哪一篇的本事」的全链路对照表，以后做任何项目都能套

> ⚠️ 下文凡涉及具体命令、参数、默认行为，都以 Codex [官方文档](https://developers.openai.com/codex) 为准；模型名、界面文案这类会随版本变的东西，看到时以你本地 `codex --help` 和实际显示为准，本篇不写死。

---

## 01 先把「靶子」立起来：两分钟造一个 TODO 小工具

动手之前得有个东西给你动。咱们先手工把这个项目建出来——**之后所有操作都围着它转**。

**类比：这就像装修前先得有套毛坯房。** 没有房子，再好的设计图也落不了地。这个 `todo.py` 就是咱们的毛坯房，简单到一眼能看完，但「该有的墙都在」——有数据、有功能、有入口，正好够咱们加一面新墙（删除功能）。

找个空目录，建一个 `todo.py`，内容如下（Mac / Linux / Windows 都一样，Python 3 即可）：

```python
# todo.py
import sys

TODOS = []

def add(item):
    TODOS.append(item)
    print(f"已添加：{item}")

def list_todos():
    if not TODOS:
        print("（暂无待办）")
        return
    for i, item in enumerate(TODOS, 1):
        print(f"{i}. {item}")

def main():
    if len(sys.argv) < 2:
        print("用法：python todo.py [add <内容> | list]")
        return
    cmd = sys.argv[1]
    if cmd == "add":
        add(" ".join(sys.argv[2:]))
    elif cmd == "list":
        list_todos()
    else:
        print(f"未知命令：{cmd}")

if __name__ == "__main__":
    main()
```

建好后，先跑一下确认它活着：

```bash
python todo.py add 买咖啡豆
python todo.py list
```

预期输出：

```text
已添加：买咖啡豆
（暂无待办）
```

注意这里有个「特性」——**因为每次运行都是新进程，`TODOS` 这个内存列表跑完就清空了**，所以 `list` 那次看到的是空的。这不是 bug，是咱们这个最小靶子的设定，先记着，第 05 节验证时会用到它。

最后，把它纳入 Git 管理（后面要提交）：

```bash
git init
git add todo.py
git commit -m "init: TODO 小工具初版"
```

预期会看到 Git 报告创建了一次提交。**靶子立好了，下面正式开工。**

> 💡 一句话总结：先两分钟手工造出 `todo.py` 这个最小可跑的靶子并纳入 Git，之后每一步才有的放矢。

---

## 02 第一步：写 `AGENTS.md`，把项目背景一次交代清

Codex 一进项目是张白纸——它不知道这是什么项目、用什么跑、有什么规矩。**开工前的第一件事，是给它一份「交接清单」。** 这正是〔[11 项目说明书 AGENTS.md](11-agents-md.md) 〕讲的事。

**类比：这像给临时来帮忙的工人贴一张「现场须知」。** 工人手艺再好，进了你家不知道「电闸在哪、哪面墙不能砸、干完往哪倒垃圾」，照样帮倒忙。`AGENTS.md` 就是贴在门口那张须知，Codex 每次开工先看一眼。

在项目根目录建 `AGENTS.md`，照抄这份（短、准、只写它真用得上的）：

```markdown
# TODO 小工具

一个命令行待办工具，纯 Python 3 标准库，无第三方依赖。

## 运行
- 添加：`python todo.py add <内容>`
- 列出：`python todo.py list`

## 约定
- 只用标准库，不要引入任何第三方包
- 新功能要保持现有命令行风格（`python todo.py <命令> <参数>`）
- 改完必须能用上面的命令跑通

## 测试
- 如果还没有测试文件，用标准库 `unittest` 新建 `test_todo.py`
- 改完跑：`python -m unittest`
```

记住〔11 篇〕里那个血泪教训——**别把唯一有用的规矩埋进一百行废话里**。我去年就吃过这亏，`AGENTS.md` 写了一百四十行，最关键的「禁用 npm」埋在第 140 行，Codex 注意力早被前面稀释没了，转头就 `npm install`。所以这份须知我刻意压到二十行内：**每一条都是它干这个活儿真会用到的，没有一句公司简介、产品愿景。**

> 💡 一句话总结：开工第一步先写一份短而准的 `AGENTS.md`，把「怎么跑、什么规矩、怎么测」交代清，且只写它真用得上的，别注水。

---

## 03 第二步：派活——目标 + 范围 + 约束 + 验收，一句说全

须知贴好了，该派活了。**派活的质量，直接决定它干出来的活儿有多准。** 这是〔[13 提示词写法](13-prompting.md) 〕的核心：一句信息量约等于零的「加个删除功能」，它只能脑补；把「四件套」说全，它才不跑偏。

**类比：派活像给装修工下一张工单。** 你不能只说「这屋子改一下」，得写明：改成什么样（目标）、动哪面墙别动哪面（范围）、用什么材料（约束）、怎么算验收合格（验收）。四样缺一样，工人就得自己猜，猜错了你返工。

进项目，起一个 Codex 会话：

```bash
codex
```

然后把这句结构完整的需求递给它（你可以直接复制）：

```text
给 todo.py 加一个「删除待办」功能。

目标：支持 `python todo.py done <序号>`，按 list 显示的序号删除对应待办。
范围：只改 todo.py，新增/修改测试文件；不要动命令行整体风格，不要引第三方包。
约束：序号从 1 开始；序号越界或非数字时，打印友好提示而不是报错崩溃。
验收：用 unittest 覆盖「正常删除、越界、非数字」三种情况，`python -m unittest` 全绿。
```

对比一下下面这两种派法，差距一目了然：

| ❌ 含糊派活 | ✅ 四件套派活 |
| --- | --- |
| 「加个删除功能」 | 明确命令格式 `done <序号>` |
| 没说改哪、动多大 | 框定范围：只动 `todo.py` + 测试 |
| 没说边界怎么处理 | 约束：越界 / 非数字要友好提示 |
| 没说怎么算做完 | 验收：三类用例 + `unittest` 全绿 |

我自己的真实体感：**把验收标准写进提示词，是性价比最高的一句话。** 上个月我让 Codex 给一个解析脚本加容错，头一回偷懒没写验收，它「改完了」但对空文件直接崩；我把「空文件、超大文件、乱码三种都要不崩」补进去重派，一次过。**Codex 的天花板，常常是被你自己的提问方式锁死的。**

> 💡 一句话总结：派活用「目标 + 范围 + 约束 + 验收」四件套把话说全，缺哪件它就在哪件上脑补；尤其把验收标准钉进去，等于告诉它「不达标不收工」。

---

## 04 第三步：设好权限——让它能改文件、能跑测试，但别放飞

派活之前（或会话刚起时），得先想清楚：**这个活儿要让 Codex 动多大？** 这是〔[15 权限、沙箱与审批](15-permissions.md) 〕的事——沙箱管「它能碰到哪」，审批管「动手前问不问你」。

**类比：这像给上门的工人发一张门禁卡。** 卡的权限给小了，他改个东西还得一遍遍喊你开门，烦；给大了，他能溜达进你卧室翻抽屉，悬。咱们这个活儿，**要的就是「能在这个项目目录里改文件、跑测试，但出不了这扇门」**。

对这个活儿，推荐用「工作区可写 + 按需审批」这一档——它能在项目目录里自由改文件（含新建、删除）、能跑 `python -m unittest`，但碰到改工作区之外的文件、联网这类越界动作会停下来问你。命令行临时指定：

```bash
codex --sandbox workspace-write --ask-for-approval on-request
```

或者写进 `~/.codex/config.toml` 当默认（TOML 格式的配置文件）：

```toml
# ~/.codex/config.toml
sandbox_mode = "workspace-write"
approval_policy = "on-request"
```

三个关键默认值，照〔15 篇〕记牢，省得踩坑：

| 你以为 | 实际默认（workspace-write 下） |
| --- | --- |
| 它能随便联网下包 | 网络默认是**关**的 |
| `.git` 目录随它改 | `.git` 默认**只读保护** |
| 改哪儿都行 | 只在**工作区目录内**可写 |

千万别学我当年图省事把 `sandbox_mode` 写成 `danger-full-access` 当全局默认。去年冬天我在一个**没初始化 Git 的临时目录**里让它「清下没用的文件」，完全访问模式下没有「工作区」这道圈拦着，它直接在我家目录里翻了起来——我按 `Esc` 打断那一下，后背是凉的。**这个 TODO 小项目用 `workspace-write` 足够，缰绳别松到不该松的地方。**

> 💡 一句话总结：开工前先用沙箱和审批划好圈——`workspace-write` + `on-request` 让它能改能测但出不了项目门；`danger-full-access` 这种全局默认是给自己挖坑。

---

## 05 第四步：让它自验证——跑测试，不达标不收工

Codex 说「改完了」不等于真做对了。**最省心的做法，是让它自己跑测试、自己确认绿了再交给你**——你只当最后的验收员。这一步把〔13 篇〕的「验收标准」真正落了地。

**类比：这像让厨师出菜前自己先尝一口。** 你在派活时（第 03 节）已经写明「`python -m unittest` 全绿」算验收，等于提前递了把尺子。现在 Codex 改完会照这把尺子自测：跑测试 → 看是否全绿 → 没绿就接着改。你不用盯着它每一步，**等它端上来的是「已经尝过、确认没问题」的成品**。

因为咱们派活时就把测试要求写进了 `AGENTS.md`（第 02 节那个 `## 测试`）和提示词里，Codex 通常会主动新建 `test_todo.py` 并在改完后跑一遍。它跑测试时，你大概会在终端看到类似：

```text
...
----------------------------------------------------------------------
Ran 3 tests in 0.003s

OK
```

看到 `OK` 和 `Ran 3 tests`，说明它给的三类用例（正常删除、越界、非数字）都过了。**万一它没主动跑，你补一句催它**：

```text
跑一下 python -m unittest，把结果贴出来；有不过的就改到全绿再停。
```

这里有个〔01 节〕埋的细节要提醒你：因为 `TODOS` 是内存列表、进程结束就清空，**你没法用「先 add 再 done 再 list」这种命令行手测真正验证删除逻辑**——`list` 永远是空的。所以这个功能的正确验证方式只能靠单元测试，在同一个进程里 `add` 完 `done` 再断言。这恰恰说明：**该让它跑测试自验的活儿，别用肉眼手测糊弄过去。**

我自己有个铁规矩：**凡是让 Codex 改了逻辑，结尾必带一句「跑测试并贴结果」。** 有一次我嫌麻烦没让它跑，肉眼看 diff 觉得没问题就合了，结果一个边界把生产环境搞挂了——从那以后，「自验证」这一步我一次都不省。

> 💡 一句话总结：派活时把测试命令写进验收，改完让 Codex 自己跑 `unittest` 跑到全绿再交付；内存态、状态类逻辑尤其要靠测试验，别用肉眼手测蒙混。

---

## 06 第五步（可选）：什么时候该叫子代理或 MCP 来搭把手

这个 TODO 小活儿，一个主代理顺手就干完了，**用不上子代理，也用不上 MCP**。但综合实战（capstone）的意义是让你认得「什么时候该升级装备」，所以这节讲清边界——**知道何时不用，和知道何时该用，一样重要**。

**类比：这像装修时要不要叫外援。** 给一面墙刷漆，你自己一人就办了；可要是十个房间同时刷、还得查一份外部的色卡标准，那就该喊帮手、该翻资料了。子代理是帮手，MCP 是资料通道。

两条判断线，记住就行：

- **活儿「又多又能并行」→ 叫子代理。** 比如不是删一个功能，而是要把二十个文件里的过期写法批量改掉。这时让主代理拆活、多个子代理并行去改，比一个人顺序磨快得多。怎么拆、怎么派，看〔[21 子代理](21-subagents.md) 〕——记住它的脾气：**只有你开口它才拆，不会自作主张。**
- **活儿「要够外部世界」→ 接 MCP。** 比如删除前要先查公司内部某个系统「这条待办是否已归档」，这种 Codex 自己够不着的外部能力，就靠 MCP 这个「外接口」接进来，见〔[20 用 MCP 接外部工具](20-mcp.md) 〕。

| 这个活儿 | 要不要升级 | 为什么 |
| --- | --- | --- |
| 给 `todo.py` 加一个删除功能 | ❌ 不用 | 单文件小改动，主代理够了 |
| 把 20 个文件的过期写法批量改掉 | ✅ 子代理 | 又多又能并行，拆出去快 |
| 删除前要查外部系统的归档状态 | ✅ MCP | Codex 够不着外部，得接口子 |

说句实话，新手最常犯的不是「该用没用」，而是**「不该用硬上」**——一个三行的小改动也要拆五个子代理，纯属给自己添乱。**先用最朴素的单代理把活干完，真撞上「又多又并行」或「够不着外部」的墙，再升级。**

> 💡 一句话总结：小活儿一个主代理就够，别硬上重装备；「又多又能并行」才叫子代理（21 篇），「要够外部世界」才接 MCP（20 篇）。

---

## 07 最后一步：`git` 提交——它来打草稿，你来按下确认

功能做对、测试全绿，临门一脚是把这次改动**干净地提交**。这是〔[26 Git 与 GitHub 集成](26-git-github.md) 〕的本事，也是整条链路的收口：**审查和提交信息它来拟，按下提交那一下你来定。**

**类比：这像签合同前的「双签」。** 一方拟好条款（Codex 看 diff、写提交信息），另一方逐条核对再落笔（你审一眼、确认提交）。提交是会进历史的「定稿」，**这道关必须有人按，而那个人是你**。

在会话里直接交代：

```text
把这次改动提交一下：先看 git status 和 git diff，再写一条中文提交信息，前缀用 feat:，提交前把变更和信息列给我确认。
```

Codex 处理 `git` 提交，常见做法是按这个节奏来：

```text
1. git status   —— 看有哪些变更
2. git diff      —— 审具体改了什么
3. git add       —— 把要提交的加进暂存
4. git commit    —— 写好信息、创建提交
```

> ⚠️ **实验性，可能变化**：让 Codex 自己执行 `git commit` 落库，依赖一个叫 `codex_git_commit` 的实验性开关，**默认是关的**。所以你照做时，更稳的姿势是：**让 Codex 帮你看 diff、拟提交信息，最后那条 `git commit` 你自己敲**——既拿到它的草稿，又把「按下提交」这一步牢牢攥在自己手里。

为什么 `git commit` 这一步天然就有道关卡？因为在 `workspace-write` 沙箱下，`.git` 目录是**只读保护**的（〔15 篇〕讲过这条），动到提交这种越界动作往往要先经你确认。这正好给你留出扫一眼的机会：改的文件对不对、提交信息说人话没、有没有把不该提的东西（比如临时文件）也带进去了。确认无误，再放它落锤。你可以期待一条类似这样的提交信息：

```text
feat: 新增按序号删除待办功能并补充 unittest 用例
```

提交完，亲手核一遍最稳：

```bash
git log --oneline -1
```

预期输出（哈希值你本地不一样，正常）：

```text
a1b2c3d feat: 新增按序号删除待办功能并补充 unittest 用例
```

我的真实习惯是：**`git commit` 这一下，我从不开「全自动」让它无人值守地提**。〔15 篇〕那条贯穿全书的红线——「审查它来当，放行你来按」——在提交这一步体现得最明显。倒不是怕它写错信息，是怕它把我没留意的半成品一起带进了历史，回头清理比当场看一眼贵多了。

> 💡 一句话总结：提交让 Codex 打草稿（看 diff、拟信息），但 `git commit` 那一下你亲自确认再放行——这是贯穿全书的「审查它来当、放行你来按」红线在收口处的落点。

---

## 08 回头看整条链：每一步在用哪一篇的本事

走完一遍，把这条链路拎起来看，你会发现**前面三十几篇不是孤立的知识点，是一条流水线上的工位**。这张表你以后做任何项目都能直接套：

| 步骤 | 这一步在干什么 | 对应篇 |
| --- | --- | --- |
| ① 立靶子 | 造出最小可跑的 `todo.py` 并纳入 Git | 本篇 |
| ② 写须知 | `AGENTS.md` 交代背景、怎么跑、什么规矩 | 〔[11](11-agents-md.md) 〕 |
| ③ 派活 | 目标 + 范围 + 约束 + 验收，四件套说全 | 〔[13](13-prompting.md) 〕 |
| ④ 设权限 | `workspace-write` + `on-request` 划好圈 | 〔[15](15-permissions.md) 〕 |
| ⑤ 升级（按需） | 又多又并行叫子代理、够不着外部接 MCP | 〔[21](21-subagents.md) 〕〔[20](20-mcp.md) 〕 |
| ⑥ 自验证 | 让它跑 `unittest` 跑到全绿再交付 | 〔[13](13-prompting.md) 〕 |
| ⑦ 提交 | Codex 拟 diff 与信息，你确认后 `commit` | 〔[26](26-git-github.md) 〕 |

**这条链的顺序不是我拍脑袋排的，是真实开发的自然节奏**：先让它懂项目（须知），再说清要什么（派活），再框住它能动多大（权限），干完自己验（测试），最后定稿入库（提交）。

模型选什么、推理强度配多大，这条链路里我用的就是默认那套——**旗舰 `gpt-5.5` + 默认推理强度（一般是 `medium`）**，这个 TODO 小活儿完全够用，没必要拉到 `high`。要是你碰上的是「跨模块大重构」那种硬骨头，再回〔[30 怎么选模型](30-models.md) 〕按场景调档。**这一篇我刻意全程用默认配置走，就是想让你看清：链路本身才是主角，调参是锦上添花。**

> 💡 一句话总结：须知 → 派活 → 权限 →（按需升级）→ 自验证 → 提交，这条顺序是真实开发的自然节奏；前面每一篇都是这条流水线上的一个工位，串起来才是「会用 Codex」。

---

## 小结

这一篇没教新功能，干的是「把零件装成整机」：

- **立靶子**：手工造一个最小可跑的 `todo.py` 并纳入 Git，让后续每一步都有的放矢。
- **写须知**：短而准的 `AGENTS.md`，只写它真用得上的，别注水稀释关键规矩。
- **派活**：「目标 + 范围 + 约束 + 验收」四件套说全，把验收钉进去等于「不达标不收工」。
- **设权限**：`workspace-write` + `on-request` 划好圈，别拿 `danger-full-access` 当全局默认。
- **自验证**：让它自己跑 `unittest` 到全绿，状态类逻辑尤其靠测试验，别肉眼蒙混。
- **提交**：Codex 拟草稿、你按确认——「审查它来当、放行你来按」这条红线在收口处落地。

**你现在应该能**：拿到任何一个小需求，不再东一榔头西一棒子，而是按「须知 → 派活 → 权限 → 自验证 → 提交」这条链，把前面学的零件咬合成一次干净、可复现、自己心里有底的开发。**这，就是「会用 Codex」和「听说过 Codex」的真正分界线。**

---

下一篇〔[35 命令与配置速查表](35-cheatsheet.md) 〕，咱们把整个 Codex 篇用到的命令、配置键、常用斜杠命令收进一张能随时翻的速查表——你刚跟着做完这一遍，正好趁热想一个问题：**这条链路里，哪几个命令是你下次盲敲都不会错的，哪几个还得回头查？** 那张表，就是为「还得查」的那部分准备的。
