codex cli 源码教程 | 第一篇:Codex CLI 不只是一个 CLI
Codex CLI 表面上是一个终端命令:在项目目录执行codex,输入任务,然后等待它阅读代码、执行命令并修改文件。
如果只把它理解成“给大模型套了一层命令行界面”,后续阅读源码会很快遇到困难。这个仓库实际实现的是一套本地 Agent Runtime:它既要连接模型,也要管理会话、上下文、工具、权限、沙箱、扩展、持久化和多个客户端。
本篇先不深入某个函数,而是建立一张全局地图。后续每篇课程都会沿着这张地图进入一个具体模块。
本篇目标
阅读完成后,你应该能够:
- 解释 Codex CLI 的产品边界,而不只把它看作终端 UI。
- 说清 CLI、TUI、App Server、Core 和 Tool System 的职责。
- 理解 Thread、Turn、Item 三个核心协议对象。
- 描述一条用户指令从输入到最终回复的完整路径。
- 知道接下来应该按什么顺序阅读源码。
1. Codex CLI 到底是什么
仓库根目录的 README.md 给出了最简洁的定义:Codex CLI 是一个运行在本地计算机上的编码智能体。
这里有三个关键词。
1.1 编码智能体
Codex 不只生成文本。它可以根据模型返回的工具调用执行实际操作,例如:
- 搜索和读取代码。
- 执行 Shell 命令。
- 修改工作区文件。
- 查看本地图片。
- 调用 MCP 工具。
- 发起 Web Search。
- 创建子智能体并分派任务。
- 运行测试并继续分析结果。
因此,一次用户请求通常不是一次模型调用,而是“模型推理、工具执行、结果回填、继续推理”的循环。
1.2 运行在本地
模型推理可以发生在远端服务,但 Codex 的 Agent Runtime 运行在用户环境中。工作区文件、终端进程、项目指令和本地工具都由这个 Runtime 管理。
这带来两个直接要求:
- Runtime 必须理解本地操作系统、Shell、路径和进程。
- Runtime 必须为文件修改、命令执行和网络访问建立安全边界。
所以仓库中既有模型客户端,也有沙箱、审批、网络策略和跨平台执行代码。
1.3 不只有命令行客户端
同一套能力需要服务于不同入口:
| 使用方式 | 入口 | 典型用途 |
|---|---|---|
| 交互终端 | codex | 人工参与的持续编码会话 |
| 无交互执行 | codex exec | 脚本、CI 和自动化任务 |
| 自动审查 | codex review | 针对代码变更执行 Review |
| IDE 或桌面端 | codex app-server | VS Code、图形客户端 |
| MCP 集成 | codex mcp-server | 将 Codex 暴露给其他 Agent |
| TypeScript SDK | sdk/typescript | Node.js 工作流集成 |
| Python SDK | sdk/python | Python 应用和自动化集成 |
这些入口不会分别实现一套 Agent。它们最终会收敛到相同的会话、协议和执行核心。
2. 从“命令行工具”转向“Agent Runtime”
可以先用一张简化图理解整个系统:
TUI / Exec / IDE / SDK | v App Server | v ThreadManager / Session | +----+----+ | | v v ModelClient ToolRouter | v Approval / Sandbox | v Rollout / SQLite State这张图省略了大量细节,但已经揭示了最重要的事实:
- CLI 主要负责解析参数和选择运行模式。
- App Server 提供统一的会话协议。
- Core 管理 Agent 的运行状态。
- Model Client 负责与模型服务通信。
- Tool Router 负责将模型请求映射到本地能力。
- Approval 和 Sandbox 控制工具能否以及如何执行。
- Rollout 和 SQLite 负责恢复会话与查询状态。
接下来逐层拆解。
3. 第一层:发布与进程入口
用户通过 npm 安装@openai/codex时,得到的 JavaScript 并不是 Agent 的主要实现。
codex-cli/bin/codex.js 主要完成以下工作:
- 根据操作系统和 CPU 架构确定 Target Triple。
- 找到对应平台包中的原生
codex二进制。 - 启动 Rust 二进制并继承标准输入输出。
- 将
SIGINT、SIGTERM和SIGHUP转发给子进程。 - 保持父子进程退出码和信号语义一致。
也就是说,npm 包更接近“跨平台分发启动器”。真正的命令解析和业务逻辑从 codex-rs/cli/src/main.rs 开始。
Rust 入口使用 Clap 定义MultitoolCli,再由cli_main根据子命令分发:
- 无子命令:进入交互式 TUI。
exec:进入无交互执行。review:转换为 Exec 的 Review 模式。app-server:启动 JSON-RPC 服务。mcp-server:启动 MCP Server。- 其他子命令:处理登录、插件、沙箱、云任务和诊断等能力。
这一层的核心职责是“选择产品形态”,而不是执行 Agent 推理。
4. 第二层:客户端与用户交互
4.1 TUI
codex-rs/tui基于 Ratatui 实现交互式终端。它负责:
- 输入编辑和快捷键。
- 会话列表与恢复。
- Agent 消息和 Reasoning 的流式展示。
- 命令输出和文件 Diff 渲染。
- 审批问题和用户输入。
- Token 使用量、状态和错误提示。
TUI 看起来离 Core 很近,但它没有自行管理完整的 Agent 会话。默认情况下,它会启动一个进程内 App Server,再通过 App Server Client 发起 Thread 和 Turn 请求。
相关入口位于:
- codex-rs/tui/src/lib.rs
start_embedded_app_serverAppServerSessioncodex_tui::run_main
4.2 Exec
codex-rs/exec面向无交互场景。它关注的不是界面,而是稳定输出:
- 默认模式下,标准输出只保留最终消息。
- JSON 模式下,标准输出必须是合法 JSONL。
- 日志、警告和诊断信息写入标准错误。
Exec 同样通过InProcessAppServerClient发起 Thread 和 Turn。因此,TUI 与 Exec 的差异主要在客户端交互和事件消费方式,而不是 Agent 核心。
4.3 SDK
两套 SDK 展示了两种不同的集成方式:
- TypeScript SDK 启动
codex exec --experimental-json,消费 JSONL 事件。 - Python SDK 启动
codex app-server --listen stdio://,使用类型化 JSON-RPC。
这说明 Codex 对外既提供“任务执行流”,也提供“完整会话服务”。
5. 第三层:App Server 统一协议
codex-rs/app-server/README.md 将 App Server 定义为丰富客户端使用 Codex 的接口,例如 VS Code 扩展。
App Server 支持多种传输:
- stdio。
- WebSocket。
- Unix Socket。
- 进程内 Channel。
传输形式不同,但承载的是同一组 JSON-RPC 消息。每个连接首先执行initialize握手,之后才能创建 Thread 或发起 Turn。
App Server 的价值不只是“把 Core 包成接口”,它还解决了以下问题:
- 为 TUI、IDE、SDK 提供一致的协议。
- 管理连接初始化与能力协商。
- 将内部事件转换成稳定的客户端通知。
- 维护 Thread 订阅和客户端状态。
- 使用有界队列提供背压。
- 隔离慢速网络写入与请求处理。
- 支持内嵌、本地守护进程和远程服务。
理解 App Server 是阅读当前 Codex 架构的关键。很多看似属于 TUI 或 Exec 的功能,实际上已经通过 App Server 协议实现。
6. Thread、Turn、Item 三个核心对象
App Server 使用三个对象描述一次持续的 Agent 交互。
6.1 Thread
Thread 表示一段可以持续、恢复和分叉的会话。
它包含:
- 唯一 Thread ID。
- 当前模型和工作目录。
- Sandbox 与 Approval 配置。
- 多个 Turn。
- 持久化路径和会话元数据。
- 当前运行状态。
Thread 可以被创建、恢复、分叉、归档或删除。
6.2 Turn
Turn 表示一次用户任务。
通常从一条用户输入开始,在以下情况之一结束:
- Agent 生成最终消息。
- 用户主动中断。
- 执行失败。
- 达到限制。
一个 Turn 内部可能包含多轮模型请求。只要模型仍在请求工具,Core 就会继续执行工具并发起后续采样。
6.3 Item
Item 是 Turn 中可持久化、可流式传输的最小语义单元,例如:
- 用户消息。
- Agent 消息。
- Reasoning。
- Shell 命令。
- 文件修改。
- MCP Tool Call。
- Web Search。
- Token 使用量和错误。
Item 不只是 UI 展示对象。它同时承担协议事件、会话历史和恢复依据等职责。
6.4 Session 是什么
阅读 Core 时还会遇到 Session。
可以暂时这样区分:
- Thread 是对外暴露的会话资源。
- Session 是 Core 内部承载 Thread 运行状态和服务依赖的执行对象。
- Turn 是 Session 上的一次任务。
- Item 是 Turn 产生的结构化内容。
后续课程会进一步解释CodexThread、Codex和Session的对象关系。
7. 第四层:Core Agent 引擎
codex-rs/core是 Agent 运行时的核心,但它并不是一个单文件状态机。
主要职责包括:
- 创建和恢复 Thread。
- 接收用户操作并输出事件。
- 维护会话历史和上下文。
- 构建模型请求。
- 消费流式模型响应。
- 调度工具调用。
- 管理取消、重试和错误。
- 自动压缩过长上下文。
- 加载
AGENTS.md、Skills、插件和 MCP。 - 记录 Rollout 和遥测数据。
7.1 ThreadManager
codex-rs/core/src/thread_manager.rs 中的ThreadManager负责创建 Thread 并维护内存中的活动实例。
它持有大量共享依赖:
- Auth Manager。
- Models Manager。
- Environment Manager。
- Skills Service。
- Plugins Manager。
- MCP Manager。
- Thread Store。
- Agent Graph Store。
这表明 Thread 不是一个简单的消息数组,而是多种运行时能力的组合边界。
7.2 CodexThread
codex-rs/core/src/codex_thread.rs 中的CodexThread是调用方与 Core 交互的主要入口。
它提供的方法包括:
submit:提交操作。next_event:读取事件。steer_input:向运行中的 Turn 追加输入。shutdown_and_wait:停止并等待会话退出。try_start_turn_if_idle:空闲时启动自动任务。fork、Rollout 和背景终端相关能力。
7.3 Session Loop
Session 创建后会启动一个异步 Submission Loop。它持续接收Op,并根据操作类型执行:
- 启动用户 Turn。
- 中断当前任务。
- 更新设置。
- 执行 Review。
- 压缩上下文。
- 关闭 Session。
调用方通过另一条事件通道接收执行进度。由此形成双向异步通信:
Client -- Submission/Op --> Session Client <-- Event --------- Session8. 第五层:模型与工具循环
真正体现 Agent 特征的代码位于 codex-rs/core/src/session/turn.rs。
run_turn的注释直接描述了核心算法:
- 将当前会话历史和用户输入构造成 Prompt。
- 请求模型。
- 如果模型返回工具调用,执行工具。
- 将工具输出记录到历史。
- 再次请求模型。
- 如果模型只返回最终消息,结束 Turn。
这个循环还需要处理:
- Turn 开始前的上下文压缩。
- Skills 与插件注入。
- Hooks。
- 用户在运行期间追加的输入。
- 并行工具调用。
- 流式消息增量。
- 模型请求重试。
- Token 使用量。
- 用户取消。
因此,run_turn可以看作 Codex Agent Runtime 的主循环。
8.1 ModelClient
codex-rs/core/src/client.rs 中的ModelClient负责模型通信。
它支持:
- Responses API。
- SSE 流式响应。
- WebSocket 会话。
- Provider 认证。
- 重试和连接恢复。
- 请求级遥测。
仓库内置 OpenAI、Amazon Bedrock、Ollama 和 LM Studio 等 Provider,也允许用户配置兼容服务。
8.2 ToolRouter
工具系统主要位于codex-rs/core/src/tools。
其中:
spec_plan.rs决定当前 Turn 应该有哪些工具。registry.rs保存工具名到执行器的映射。router.rs解析模型返回的 Tool Call。parallel.rs管理工具调用的并行执行和结果顺序。handlers包含具体工具实现。
工具列表不是固定常量。它会受到以下因素影响:
- 模型能力。
- Feature Flag。
- 当前执行环境。
- MCP Server。
- 插件和动态工具。
- Code Mode。
- 当前是否为 Review 或 Guardian 会话。
9. 第六层:安全与执行环境
Agent 能执行命令并修改文件,因此“能不能执行”和“在哪里执行”必须在模型之外判断。
Codex 主要通过三层机制控制风险。
9.1 Approval Policy
Approval Policy 决定何时向用户发起审批,例如:
- 从不请求审批。
- 模型按需请求。
- 不可信操作需要审批。
- 分别控制命令、规则、Skill 和权限请求。
9.2 Exec Policy
Exec Policy 根据命令和规则产生决策:
- 允许。
- 拒绝。
- 请求审批。
它不会因为模型“认为安全”就跳过本地策略。
9.3 Sandbox
codex-rs/sandboxing/src 提供跨平台沙箱抽象:
- macOS:Seatbelt。
- Linux:Bubblewrap 和 Landlock。
- Windows:受限 Token 与文件系统策略。
Sandbox 进一步限制:
- 可读目录。
- 可写目录。
- 网络访问。
- 进程能力。
审批与沙箱不是互相替代的。审批表达用户是否授权某项操作,沙箱负责限制操作实际能影响的范围。
10. 持久化:为什么同时需要 JSONL 和 SQLite
Codex 会话需要支持恢复、搜索、分叉、归档和审计。
仓库使用两类持久化数据。
10.1 Rollout JSONL
codex-rs/rollout将会话事件追加写入 Rollout 文件。默认路径类似:
~/.codex/sessions/YYYY/MM/DD/rollout-时间-线程ID.jsonlJSONL 适合:
- 按发生顺序记录事件。
- 追加写入。
- 恢复完整历史。
- 保留协议演进所需的信息。
10.2 SQLite State
codex-rs/state使用 SQLite 保存结构化状态和索引,例如:
- Thread Metadata。
- Rollout 路径。
- Git 信息。
- 记忆处理状态。
- Thread Goal。
- Agent Spawn Graph。
- Agent Job。
- 远程控制信息。
- 结构化日志。
SQLite 适合分页、筛选、排序和快速查询。JSONL 保存事实流,SQLite 提供可查询视图,两者通过回填和对账机制保持关联。
11. 仓库地图
理解职责后,再看目录会清晰很多。
| 路径 | 主要职责 |
|---|---|
codex-rs/cli | 命令定义、参数解析、运行模式分发 |
codex-rs/tui | 交互式终端客户端 |
codex-rs/exec | 无交互执行与 JSONL 输出 |
codex-rs/app-server | JSON-RPC 服务与连接管理 |
codex-rs/app-server-protocol | App Server 请求、响应和通知类型 |
codex-rs/core | Session、Turn、上下文和工具调度 |
codex-rs/protocol | Core 操作、事件、审批和配置类型 |
codex-rs/model-provider | 模型提供方运行时抽象 |
codex-rs/codex-mcp | MCP 连接与工具管理 |
codex-rs/sandboxing | 跨平台沙箱选择与策略转换 |
codex-rs/rollout | 会话事件持久化 |
codex-rs/state | SQLite 状态、日志和迁移 |
codex-rs/skills、plugin、hooks | 扩展机制 |
sdk/typescript | TypeScript SDK |
sdk/python | Python SDK |
codex-cli | npm 原生二进制启动器 |
scripts | 构建、打包、格式化和发布脚本 |
codex-rs/Cargo.toml中的 Workspace Members 是更完整的模块索引。不要尝试第一次就逐个阅读所有 crate,先沿一条请求链建立主干。
12. 一条用户请求的完整旅程
现在可以把前面的模块串起来。
假设用户在终端执行:
codex"解释这个项目的架构"请求大致经历以下步骤:
- npm 启动器找到当前平台的 Rust 二进制。
cli/src/main.rs解析参数,发现没有子命令。- CLI 调用
codex_tui::run_main。 - TUI 默认启动进程内 App Server。
- TUI 发送
thread/start,App Server 创建 Core Thread。 - 用户输入被转换为
turn/start。 - Core Session 接收操作并进入
run_turn。 ModelClient向模型发送包含上下文和工具定义的请求。- 如果模型请求工具,
ToolRouter在审批和沙箱约束下执行。 - 工具结果写入历史并再次发送给模型。
- 最终 Agent Message 以流式事件返回 App Server。
- TUI 将事件渲染到终端,同时 Rollout 和状态索引被更新。
其中第 8 到第 10 步可以重复多次。
这也是后续课程反复使用的主调用链:
CLI -> Client -> App Server -> Thread -> Turn -> Model -> Tool -> Model -> Event -> Client13. 这个架构有哪些重要取舍
13.1 协议优先
TUI、Exec、IDE 和 SDK 不直接共享 UI 代码,而是共享 Thread、Turn、Item 协议。新增客户端时,不需要重新实现 Agent 核心。
13.2 安全策略位于 Runtime
模型只能请求操作,不能决定最终权限。执行策略、审批和沙箱在本地 Runtime 中独立判断。
13.3 工具按上下文动态组装
Codex 不会把所有工具无条件暴露给模型。Tool Plan 根据模型、Feature、环境和扩展生成当前 Turn 的工具集合,减少无效能力和命名冲突。
13.4 事件流既服务 UI,也服务恢复
流式事件不仅用于实时显示,还会进入会话历史和持久化体系。这使中断、恢复、分叉和审计拥有统一事实来源。
13.5 大 Core 正在被主动约束
根目录 AGENTS.md 明确提醒开发者不要继续无条件扩张codex-core。新增概念应优先寻找现有独立 crate,必要时创建新的职责边界。
这是阅读和修改仓库时非常重要的工程约束:能够放进 Core,不代表应该放进 Core。
14. 初学者常见误区
误区一:npm 包就是 Codex 的主体
npm 包主要负责分发和启动。Agent 主体是 Rust 工作区中的原生二进制和各个 crate。
误区二:App Server 是远端模型服务
App Server 是 Codex 的客户端协议层。它可以本地运行,也可以通过远程传输访问,但它不等同于模型推理后端。
误区三:一次 Turn 等于一次模型请求
一次 Turn 可能包含多次模型采样。每次工具调用通常都会触发后续模型请求。
误区四:TUI 直接调用 Core
当前架构中,TUI 默认通过进程内 App Server 驱动 Thread 和 Turn。App Server 是统一边界,不只是给 IDE 使用的附加服务。
误区五:审批后就不需要沙箱
审批和沙箱解决不同问题。审批确认意图,沙箱限制影响范围。
误区六:所有新功能都应加入codex-core
项目规范明确要求控制 Core 体积。模型 Provider、状态、插件、协议和工具基础设施已经拆分成独立 crate。
15. 建议的源码阅读方法
大型仓库最有效的阅读方式不是从lib.rs开始逐文件展开,而是沿一个行为搜索入口。
第一轮可以只执行以下搜索:
rg-n"fn main|async fn cli_main"codex-rs/cli/src/main.rs rg-n"start_embedded_app_server|run_main"codex-rs/tui/src/lib.rs rg-n"start_thread_with_options"codex-rs/core/src/thread_manager.rs rg-n"async fn run_turn"codex-rs/core/src/session/turn.rs阅读时为每个函数记录三件事:
- 输入从哪里来。
- 状态由谁持有。
- 输出发送给谁。
不要在第一轮追踪所有错误分支和 Feature Flag。先找到主路径,再逐步补充安全、重试和兼容逻辑。
16. 动手练习
练习一:绘制产品入口图
从Subcommand枚举出发,将所有子命令按以下类别分组:
- Agent 交互。
- 服务端。
- 鉴权和配置。
- 扩展管理。
- 调试和诊断。
要求每个类别至少标记一个最终调用的 crate。
练习二:验证统一 App Server
分别在以下文件中寻找InProcessAppServerClient:
codex-rs/tui/src/lib.rscodex-rs/exec/src/lib.rs
回答:
- 两个客户端如何启动 App Server?
- 它们如何构造 Thread 参数?
- 它们消费事件的方式有什么差异?
练习三:建立个人仓库地图
只选择十个最关键的 crate,为每个 crate 写一句职责说明,并画出依赖方向。
建议至少包含:
clituiexecapp-serverapp-server-protocolcoreprotocolmodel-providersandboxingstate
练习四:跟踪一次 Turn
从 App Server 的turn/startRequest Processor 开始,一直跟踪到run_turn。
记录经过的类型和函数,不需要阅读每个函数内部实现。最终产出一条可以在 IDE 中逐步跳转的调用链。
17. 本篇小结
Codex CLI 不是简单的“终端加大模型”,而是一套面向本地编码任务的 Agent Runtime。
它的核心结构可以概括为:
- CLI 选择运行模式。
- TUI、Exec、IDE 和 SDK 作为不同客户端。
- App Server 统一 Thread、Turn、Item 协议。
- Core 管理 Session、上下文和 Agent 循环。
- Model Client 与 Tool Router 交替完成推理和执行。
- Approval、Exec Policy 和 Sandbox 建立本地安全边界。
- Rollout 与 SQLite 支持持久化、恢复和查询。
掌握这张地图后,后续阅读就不再是面对数千个文件,而是沿着明确的数据流和控制流逐层深入。
下一篇将进入实际开发环境,介绍 Cargo、Just、Bazel 和 pnpm 在仓库中的分工,并建立可重复的构建、测试与日志观察流程。
