深入理解 Codex CLI 调用:从原理到实战

封面图

本文通过源码分析和实际测试,带你理解如何通过 Python 调用 OpenAI Codex CLI,构建自己的 AI 编程助手。

引言

在使用 OpenAI Codex 的过程中,你是否好奇过:

  • 市面上的 AI 编程工具是如何调用 Codex 的?
  • 为什么有的工具能安全地修改代码,有的却会”越界”?
  • 如何构建一个可控的、带质量门控的 AI 交付系统?

本文将以一个真实的课程项目为例,深入剖析 Codex CLI 的调用机制,并提供可直接运行的测试代码。

核心发现:不是 API 调用,而是 CLI 调用

重要结论:这个项目不直接调用 OpenAI REST API,而是通过 subprocess 调用本地安装的 Codex CLI 工具。

为什么这样设计?

特性 CLI 调用 直接 API 调用
沙箱隔离 CLI 内置 需自行实现
文件系统访问 CLI 直接操作 需通过工具定义
配置管理 ~/.codex/config.toml API 参数
开发复杂度 低 高

简单来说,Codex CLI 已经帮你做好了沙箱隔离、文件系统管理等复杂工作,直接调用 CLI 可以省去大量重复开发。

架构全景图

整个调用链路可以分为四层:

架构图

1
2
3
4
5
6
7
用户请求
↓
Agent Loop(多轮编排)
↓
LLM Adapter / Execution Runner
↓
Codex CLI(subprocess 调用)

核心组件:

  • codex_command.py:解析 codex 可执行文件路径
  • execution.py:核心执行逻辑,构建命令并调用 subprocess
  • codex_options.py:读取配置,构建 CLI 参数
  • codex_events.py:解析 JSON-lines 输出

核心调用代码解析

第一步:找到 Codex CLI

1
2
3
4
5
6
7
8
9
def resolve_codex_command(executable=None):
# 优先级:显式参数 > 环境变量 > PATH 查找 > Windows 特殊路径
explicit = executable or os.getenv("FLOWERP_CODEX_COMMAND")
command = explicit or "codex"
resolved = shutil.which(command)
if resolved:
return resolved
# Windows 特殊处理...
return command

第二步:构建调用命令

这是最核心的部分。一个完整的生产级调用长这样:

1
2
3
4
5
6
7
8
9
10
11
command = [
codex_path, # codex 可执行文件
"exec", # exec 子命令
"--json", # JSON 输出
"--sandbox", "workspace-write", # 沙箱限制
"--ephemeral", # 临时会话
"--output-schema", schema_file, # 输出 schema
"--output-last-message", final_file,
"--cd", workspace_root, # 工作目录
"-" # 从 stdin 读取 prompt
]

关键参数说明:

  • --json:输出 JSON-lines 格式,便于程序解析
  • --sandbox workspace-write:限制只能在工作区内写文件
  • --ephemeral:不保留会话状态
  • --output-schema:定义期望的结构化输出格式

第三步:执行并解析输出

1
2
3
4
5
6
7
8
9
10
process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=workspace_root,
text=True,
)

stdout, stderr = process.communicate(input=prompt)

Codex 的输出是 JSON-lines 格式(每行一个 JSON):

1
2
3
4
{"type":"thread.started","thread_id":"01a10f61-..."}
{"type":"turn.started"}
{"type":"item.completed","item":{"type":"agent_message","text":"你好"}}
{"type":"turn.completed","usage":{"input_tokens":9907,"output_tokens":83}}

解析这些事件,就能获取 Codex 的响应、推理过程和 token 使用情况。

实战:5 分钟上手 Codex CLI

环境准备

1
2
3
4
5
6
7
8
# 1. 安装 Codex CLI
npm install -g @openai/codex

# 2. 登录
codex login

# 3. 验证
codex --version

重要提示:Codex CLI 必须在 git 仓库 中运行!

1
2
3
4
5
# 创建测试环境
mkdir ~/codex-test && cd ~/codex-test
git init
echo "# Test" > README.md
git add . && git commit -m "init"

基础调用

1
2
3
4
5
6
7
8
# 最简单的调用
echo "你好" | codex exec -

# JSON 输出(推荐)
echo "回复:测试成功" | codex exec --json -

# 指定工作目录
echo "分析项目" | codex exec --cd /path/to/project -

沙箱模式

1
2
3
4
5
# 只读模式(安全)
echo "列出文件" | codex exec --sandbox read-only -

# 工作区写入(允许修改文件)
echo "创建 test.txt" | codex exec --sandbox workspace-write -

结构化输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 定义 schema
cat > schema.json << 'EOF'
{
"type": "object",
"properties": {
"summary": {"type": "string"},
"files": {"type": "array", "items": {"type": "string"}}
},
"required": ["summary"]
}
EOF

# 使用 schema
echo "分析项目" | codex exec \
--output-schema schema.json \
--output-last-message result.json \
-

# 查看结果
cat result.json

Python 调用 Demo

如果你想用 Python 调用 Codex,这里有一个完整的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import subprocess
import json

def call_codex(prompt, workspace="."):
"""调用 Codex CLI 并返回解析后的结果"""
command = [
"codex", "exec",
"--json",
"--sandbox", "read-only",
"--ephemeral",
"--cd", workspace,
"-"
]

result = subprocess.run(
command,
input=prompt,
text=True,
capture_output=True,
timeout=120,
cwd=workspace,
)

# 解析 JSON-lines 输出
events = []
for line in result.stdout.splitlines():
if line.strip():
try:
events.append(json.loads(line))
except json.JSONDecodeError:
pass

return {
"returncode": result.returncode,
"events": events,
"stderr": result.stderr,
}

# 使用示例
result = call_codex("列出当前目录的文件")
for event in result["events"]:
if event.get("type") == "item.completed":
item = event.get("item", {})
if item.get("type") == "agent_message":
print(item.get("text"))

设计亮点

这个项目的设计有几个值得学习的地方:

1. 沙箱隔离

通过 --sandbox workspace-write 限制 Codex 只能在工作区内操作,防止意外修改系统文件。

2. 文件系统快照

执行前后各做一次文件系统快照,对比检测越界写入:

1
2
3
4
before = snapshot_filesystem(workspace)
# ... 执行 Codex ...
after = snapshot_filesystem(workspace)
out_of_scope = detect_out_of_scope_changes(before, after, allowed_scope)

3. 质量门控

通过 hooks 在 Codex 结束前自动运行测试:

1
2
3
4
5
6
7
{
"hooks": {
"Stop": [{
"command": ["python", "quality_gate.py"]
}]
}
}

4. 降级策略

Codex 不可用时自动回退到模板适配器,保证系统可用性。

常见问题

Q1: 为什么不用 API 直接调用?

CLI 已经封装了沙箱、文件管理等复杂逻辑,直接调用可以省去大量工作。而且 CLI 的配置更灵活,支持本地模型。

Q2: 如何控制 Codex 的行为?

通过 prompt 和 AGENTS.md 文件。AGENTS.md 定义了项目规则、边界约束和验证命令。

Q3: 如何保证安全性?

  • 使用 --sandbox read-only 或 workspace-write
  • 文件系统快照检测越界
  • Hooks 质量门控
  • 不提交敏感信息

Q4: 性能如何优化?

1
2
3
4
5
6
7
8
# 使用更小的模型
codex exec -c model="codex-mini-latest" -

# 降低推理强度
codex exec -c model_reasoning_effort="low" -

# 使用临时会话
codex exec --ephemeral -

总结

通过本文的学习,你应该掌握了:

  1. Codex CLI 调用原理:通过 subprocess 调用本地 CLI,而非直接 API
  2. 核心参数:--json、--sandbox、--output-schema 等
  3. 安全实践:沙箱隔离、文件快照、质量门控
  4. 实战技能:命令行调用和 Python 集成

这些知识可以帮助你构建自己的 AI 编程助手,或者理解市面上的 AI 工具是如何工作的。