AgentHarness 课程
Hermes 专题/课程概述

Codex 第五课:第一个编码任务

Hello World修复Bug添加功能完整流程

从"Hello World"到修复 Bug,再到添加新功能——三个任务带你完整体验 Codex 的编码工作流。


1. 启动 Codex

1.1 打开终端

首先,打开你的终端(Terminal),进入一个空的工作目录:

mkdir ~/codex-playground && cd ~/codex-playground

1.2 启动 Codex CLI

在终端中输入以下命令启动 Codex:

codex

你会看到 Codex 的欢迎界面,类似这样:

╔══════════════════════════════════════╗
║         Codex CLI v0.x.x            ║
║   OpenAI's coding agent in terminal ║
╚══════════════════════════════════════╝

Model: o4-mini
Approval mode: suggest (推荐模式)

Type your coding task, or use:
  /plan   - 规划模式,先制定方案再执行
  /help   - 查看帮助
  /quit   - 退出

>

1.3 了解三种审批模式

Codex 有三种运行模式,通过 --approval-mode-a 参数指定:

模式说明适用场景
suggest每个操作都需要你确认推荐初学者使用,安全可控
auto-edit自动执行文件编辑,但运行命令需要确认信任编辑操作时
full-auto全部自动执行,无需确认完全信任的任务

强烈建议初学者使用 suggest 模式,这样你能清楚地看到 Codex 每一步在做什么。


2. 第一个任务:创建 Hello World 脚本

2.1 输入任务描述

在 Codex 的提示符下,输入你的第一个任务:

> 创建一个 Python 脚本 hello.py,打印 "Hello, Codex!" 和当前日期时间

2.2 Codex 分析需求

Codex 会开始分析你的需求。你会看到类似这样的思考过程:

🔍 分析任务...
   - 需要创建一个 Python 文件
   - 文件名:hello.py
   - 功能要求:
     1. 打印 "Hello, Codex!"
     2. 显示当前日期和时间
   - 需要使用 Python 的 datetime 模块

2.3 Codex 生成代码方案

接着 Codex 会提出执行计划:

📋 执行计划:
   1. 创建 hello.py 文件
   2. 写入以下代码:

   代码预览:
   ┌──────────────────────────────────────────┐
   │ 1 | #!/usr/bin/env python3               │
   │ 2 | from datetime import datetime         │
   │ 3 |                                       │
   │ 4 | if __name__ == "__main__":            │
   │ 5 |     print("Hello, Codex!")            │
   │ 6 |     now = datetime.now()              │
   │ 7 |     print(f"当前时间: {now}")          │
   │ 8 |                                       │
   └──────────────────────────────────────────┘

   3. 运行 python3 hello.py 验证结果

2.4 请求审批执行

suggest 模式下,Codex 会请求你的批准:

⚠️  Codex 请求执行以下操作:

操作 1: 创建文件 hello.py
操作 2: 运行命令 python3 hello.py

你是否批准?[Y/n/all]
  • 输入 Y — 批准当前操作
  • 输入 n — 拒绝当前操作
  • 输入 all — 批准本次所有操作,后续不再询问

2.5 用户批准并查看结果

输入 Y 批准后,Codex 会执行操作:

✅ 已创建 hello.py
✅ 运行 python3 hello.py

输出:
┌──────────────────────────────────────────┐
│ Hello, Codex!                             │
│ 当前时间: 2025-06-28 10:30:15.123456      │
└──────────────────────────────────────────┘

🎉 任务完成!脚本运行成功。

2.6 完整交互流程回顾

让我们回顾一下整个交互过程:

用户 → Codex:创建一个 Python 脚本,打印 Hello World 和当前时间
Codex → Codex:分析需求,确定文件名、功能点、依赖模块
Codex → Codex:生成代码方案
Codex → 用户:展示方案,请求审批
用户 → Codex:批准(Y)
Codex → 系统:创建文件、执行脚本
Codex → 用户:展示执行结果

这就是 Codex 的基本工作模式:分析 → 计划 → 请求批准 → 执行 → 反馈


3. 第二个任务:修复 Bug

3.1 制造一个 Bug

让我们先手动创建一个有 Bug 的脚本。继续在 Codex 中输入:

> 先帮我创建一个 buggy_math.py 文件,内容如下:

def divide(a, b):
    return a / b

def average(numbers):
    total = sum(numbers)
    return divide(total, len(numbers))

print(average([10, 20, 30]))
print(average([]))

Codex 创建文件后,运行它会报错:

20.0
Traceback (most recent call last):
  File "buggy_math.py", line 9, in <module>
    print(average([]))
  File "buggy_math.py", line 4, in average
    return divide(total, len(numbers))
  File "buggy_math.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero

3.2 让 Codex 修复 Bug

现在让 Codex 来修复这个问题:

> 修复 buggy_math.py 中的 bug。当传入空列表时,average 函数应该返回 0 而不是报错。同时给 divide 函数添加除数为零的检查。

3.3 Codex 的修复方案

Codex 会展示修复计划:

📋 修复方案:

问题分析:
  1. average() 在空列表时调用 divide(total, 0) 导致除零错误
  2. divide() 没有对 b=0 的情况做防御性检查

修改方案:
  1. 在 divide() 中添加 b==0 的检查,返回 None 或 0
  2. 在 average() 中添加空列表检查,直接返回 0

代码预览:
┌──────────────────────────────────────────────────┐
│ 1 | def divide(a, b):                             │
│ 2 |     if b == 0:                                │
│ 3 |         return 0                              │
│ 4 |     return a / b                              │
│ 5 |                                                │
│ 6 | def average(numbers):                          │
│ 7 |     if not numbers:                            │
│ 8 |         return 0                               │
│ 9 |     total = sum(numbers)                       │
│10 |     return divide(total, len(numbers))         │
└──────────────────────────────────────────────────┘

3.4 批准并验证

批准后 Codex 执行修改并运行测试:

✅ 已修改 buggy_math.py
✅ 运行 python3 buggy_math.py

输出:
┌──────────────────────────────────────────┐
│ 20.0                                      │
│ 0                                         │
└──────────────────────────────────────────┘

🎉 Bug 已修复!空列表情况现在正确返回 0。

关键观察:Codex 不是简单地"改代码",它会:

  1. 先分析错误原因
  2. 理解代码逻辑
  3. 提出防御性修复方案
  4. 验证修复后的行为

4. 第三个任务:添加新功能

4.1 提出新功能需求

现在让我们在已有的 buggy_math.py 基础上添加新功能:

> 给 buggy_math.py 添加以下功能:
> 1. 添加一个 median() 函数,计算中位数
> 2. 添加一个 mode() 函数,计算众数(出现次数最多的数)
> 3. 添加一个 describe() 函数,返回一组数据的完整统计信息(最小值、最大值、平均值、中位数、众数)
> 4. 在文件底部添加测试代码,用 [3, 1, 4, 1, 5, 9, 2, 6, 5] 这组数据测试所有函数

4.2 Codex 的工作方式:先阅读代码

这是 Codex 最智能的地方——它不会直接开始写代码,而是先阅读现有代码:

🔍 正在分析现有代码...

当前文件结构:
  - divide(a, b) → 除法函数,含除零保护
  - average(numbers) → 平均值函数,含空列表保护

新功能规划:
  - median() → 排序后取中间值,偶数个取中间两个的平均
  - mode() → 统计频率,返回出现次数最多的值
  - describe() → 聚合所有统计函数

4.3 Codex 制定计划

📋 实施计划:

步骤 1: 从 collections 导入 Counter(用于 mode 计算)
步骤 2: 添加 median() 函数
  - 排序列表
  - 奇数个取中间值
  - 偶数个取中间两个值的平均
步骤 3: 添加 mode() 函数
  - 使用 Counter 统计频率
  - 返回出现次数最多的值
步骤 4: 添加 describe() 函数
  - 调用所有统计函数
  - 返回字典格式的统计信息
步骤 5: 添加测试代码
步骤 6: 运行验证

4.4 Codex 逐步执行

批准后,Codex 会逐步生成代码:

from collections import Counter

def divide(a, b):
    if b == 0:
        return 0
    return a / b

def average(numbers):
    if not numbers:
        return 0
    return divide(sum(numbers), len(numbers))

def median(numbers):
    if not numbers:
        return 0
    sorted_nums = sorted(numbers)
    n = len(sorted_nums)
    mid = n // 2
    if n % 2 == 1:
        return sorted_nums[mid]
    else:
        return divide(sorted_nums[mid - 1] + sorted_nums[mid], 2)

def mode(numbers):
    if not numbers:
        return None
    counter = Counter(numbers)
    max_freq = max(counter.values())
    modes = [k for k, v in counter.items() if v == max_freq]
    return modes[0]  # 返回第一个众数

def describe(numbers):
    return {
        "count": len(numbers),
        "min": min(numbers) if numbers else None,
        "max": max(numbers) if numbers else None,
        "average": average(numbers),
        "median": median(numbers),
        "mode": mode(numbers),
    }

# 测试代码
if __name__ == "__main__":
    data = [3, 1, 4, 1, 5, 9, 2, 6, 5]
    print(f"数据: {data}")
    print(f"个数: {len(data)}")
    print(f"最小值: {min(data)}")
    print(f"最大值: {max(data)}")
    print(f"平均值: {average(data)}")
    print(f"中位数: {median(data)}")
    print(f"众数: {mode(data)}")
    print(f"\n完整统计: {describe(data)}")

4.5 验证结果

✅ 已修改 buggy_math.py
✅ 运行 python3 buggy_math.py

输出:
┌──────────────────────────────────────────┐
│ 数据: [3, 1, 4, 1, 5, 9, 2, 6, 5]        │
│ 个数: 9                                   │
│ 最小值: 1                                 │
│ 最大值: 9                                 │
│ 平均值: 4.0                               │
│ 中位数: 4                                 │
│ 众数: 1                                   │
│                                           │
│ 完整统计: {'count': 9, 'min': 1,          │
│   'max': 9, 'average': 4.0, 'median': 4,  │
│   'mode': 1}                              │
└──────────────────────────────────────────┘

🎉 新功能添加完成!所有测试通过。

5. 理解 Codex 的工作方式

通过这三个任务,我们观察到 Codex 有一套清晰的工作模式:

5.1 五步工作流

┌─────────────────────────────────────────────────────┐
│                   Codex 工作流                        │
│                                                       │
│  ①  阅读代码 ──→ ② 分析需求 ──→ ③ 制定计划          │
│                                        │               │
│                                        ▼               │
│                        ⑤ 验证结果 ←── ④ 逐步执行      │
│                            │                           │
│                            ▼                           │
│                      展示结果给用户                     │
└─────────────────────────────────────────────────────┘

① 阅读代码:Codex 会先了解项目结构和现有代码。它不只是看你当次输入的 prompt,还会查看工作目录下的文件、理解代码上下文。

② 分析需求:将你的自然语言描述转化为具体的编程任务。它会识别:

  • 需要创建/修改哪些文件
  • 需要使用什么语言特性或库
  • 有哪些边界情况需要考虑

③ 制定计划:在执行之前,Codex 会列出详细的步骤。这是审查它工作思路的好时机——如果计划不合理,你可以直接拒绝并重新描述需求。

④ 逐步执行:按照计划一步步执行,每一步都是原子操作(创建文件、编辑代码、运行命令等)。

⑤ 验证结果:运行代码确认功能正确,展示执行结果。

5.2 Codex 的"记忆"

Codex 在单次会话中有上下文记忆。这意味着:

  • 后续任务可以引用之前的任务:比如"给刚才创建的文件添加功能"
  • 它会记住之前的错误:如果某次修复失败,它会记住并调整策略
  • 但新会话会从头开始:退出 codex 重新进入后,之前的上下文就丢失了

5.3 Codex vs 普通 AI 对话

特性普通 AI 对话Codex
代码生成✅ 生成代码文本✅ 生成代码并写入文件
运行代码❌ 需要手动复制运行✅ 直接在终端运行
读取文件❌ 需要你粘贴内容✅ 自动读取项目文件
安装依赖❌ 无法操作✅ 可以执行 pip/npm 等
Git 操作❌ 无法操作✅ 可以 commit/push
交互确认❌ 无✅ 展示方案等待审批

6. 交互技巧

6.1 清晰描述需求

❌ 模糊的描述:

> 写个程序

✅ 清晰的描述:

> 创建一个 Python 脚本 weather.py,使用 requests 库
> 调用 OpenWeatherMap API 获取北京当前天气,
> 显示温度、湿度、天气状况,并用彩色输出

描述越具体,Codex 的输出越符合你的期望。好的描述包含:

  • 语言/框架:Python、Node.js、React 等
  • 具体功能:做什么、输入什么、输出什么
  • 技术偏好:用什么库、什么风格
  • 边界条件:如何处理异常情况

6.2 提供上下文

❌ 缺少上下文:

> 修复这个 bug

✅ 提供上下文:

> 修复 api_handler.py 中的 bug。
> 当 POST 请求 body 中没有 "name" 字段时,
> 服务器应该返回 400 而不是 500。
> 期望的返回格式: {"error": "name field is required"}

6.3 使用 /plan 先规划

对于复杂的任务,建议先用 /plan 模式让 Codex 规划:

> /plan
> 帮我创建一个 REST API 项目,包含:
> 1. 用户注册和登录(JWT 认证)
> 2. CRUD 操作的 Todo 列表
> 3. SQLite 数据库
> 4. 完整的错误处理
> 5. 单元测试

/plan 模式下 Codex 只会展示方案,不会执行任何操作。你可以:

  • 审查方案是否合理
  • 让它调整计划:"把 SQLite 改成 PostgreSQL"
  • 确认后再用正常模式执行

6.4 迭代式开发

不要一次给太大的任务。推荐迭代式开发:

第 1 步: > 创建一个基础的 Flask API,有 /health 端点
第 2 步: > 添加 /users 端点,支持 GET 和 POST
第 3 步: > 给 POST /users 添加输入验证
第 4 步: > 添加 JWT 认证中间件
第 5 步: > 添加单元测试

每一步都小而清晰,Codex 的表现会更好,你也更容易控制质量。

6.5 拒绝并重新引导

如果 Codex 的方案不符合预期,不要勉强批准,直接拒绝并给出更清晰的指引:

⚠️  Codex 请求执行以下操作...
你是否批准?[Y/n/all]
> n
> 不要使用全局变量,请用类来封装这些状态。
> 另外,用 logging 模块替代 print 输出。

6.6 利用 Codex 的自纠错能力

如果 Codex 的代码运行出错了,它会自动分析错误并尝试修复:

> 运行一下 hello.py

✅ 运行 python3 hello.py

❌ 错误: ModuleNotFoundError: No module named 'requests'

🔍 检测到缺少依赖,正在修复...
运行: pip install requests

✅ 已安装 requests
✅ 重新运行 python3 hello.py

输出:Hello, Codex! 当前时间: ...

7. 常见问题

Q1: Codex 和 ChatGPT 有什么区别?

ChatGPT 是一个对话助手,它能写代码,但只能把代码以文本形式返回给你。你需要手动复制代码到文件、手动运行。

Codex CLI 是一个编码代理(Agent),它能在你的终端中直接操作文件系统、运行命令、安装依赖。它是"动手干活"的,不只是"出主意"的。

Q2: Codex 会修改我的重要文件吗?

suggest 模式下,每个操作都需要你批准。只要你在审批时仔细查看,就不会有意外修改。建议在版本控制(Git)下使用 Codex,这样即使出错也能轻松回退:

git init                    # 初始化 Git
codex                       # 使用 Codex
git diff                    # 查看 Codex 做了哪些改动
git checkout .              # 如果不满意,回退所有改动
git add -A && git commit    # 满意的话,提交改动

Q3: Codex 支持哪些编程语言?

Codex 底层使用大语言模型,理论上支持所有主流编程语言。实际测试中,以下语言表现最好:

  • Python(最擅长)
  • JavaScript / TypeScript
  • Go
  • Rust
  • Java / Kotlin
  • C / C++
  • Shell / Bash

Q4: Codex 能处理多大的项目?

Codex 的上下文窗口有限,对于非常大的项目,它不会一次性读取所有文件。它会:

  1. 先看目录结构
  2. 根据任务需求读取相关文件
  3. 只修改需要改动的部分

如果你的项目非常大,建议在描述中明确指出要修改哪个文件的哪部分功能。

Q5: 网络断了还能用吗?

Codex 需要调用云端的 AI 模型,所以需要网络连接。但你已经创建的文件和已安装的依赖不受影响。

Q6: 为什么 Codex 有时候会"理解错"我的需求?

大语言模型对自然语言的理解虽然很强,但仍有局限性。常见原因:

  • 描述太模糊:没有指定语言、框架、具体行为
  • 隐含假设:你认为"理所当然"的细节没有说出来
  • 技术术语歧义:"cache" 可能是缓存数据,也可能是缓存策略

解决方法:提供更多上下文,或者用 /plan 先看方案再决定。

Q7: Codex 生成的代码质量如何?

Codex 生成的代码通常质量不错,但它不是完美的。你仍然需要:

  • 审查代码逻辑是否正确
  • 检查边界情况是否处理
  • 考虑安全性问题
  • 进行代码测试

把 Codex 当作一个高效的编程搭档,而不是可以完全信赖的"自动程序员"。

Q8: 如何让 Codex 效率更高?

几个实用建议:

  1. 使用项目级别的 codex.md:在项目根目录创建 codex.md,写上项目规范、技术栈、代码风格等信息,Codex 每次启动都会读取
  2. 保持良好的项目结构:规范的目录和文件命名帮助 Codex 更好地理解项目
  3. 分步发布任务:大任务拆小,每步验证
  4. 善用 /plan:复杂任务先规划再执行
  5. 提供示例:如果想要特定风格的代码,给一个示例

8. 本课小结

在这一课中,我们通过三个实际任务完整体验了 Codex 的工作流程:

任务技能点Codex 展示的能力
Hello World创建新文件需求理解 → 代码生成 → 运行验证
修复 Bug修改现有代码错误分析 → 防御性编程 → 回归测试
添加功能扩展代码库阅读上下文 → 增量开发 → 集成验证

你已经掌握了 Codex 的基本使用方法。在下一课中,我们将深入学习 /plan 规划模式,让 Codex 帮你规划复杂的项目架构,先"画蓝图"再"施工"。


9. 动手练习

  1. 练习一:让 Codex 创建一个 calculator.py,实现加减乘除四则运算,支持命令行参数输入
  2. 练习二:故意在 calculator.py 中引入一个除零 Bug,然后让 Codex 修复它
  3. 练习三:让 Codex 给 calculator.py 添加科学计算功能(平方根、幂运算、对数)

完成这些练习后,你就真正掌握了 Codex CLI 的使用!