第十四篇:《Codex 源码拆解:137 个 crate 构成的智能体架构》

在前面的十三篇文章中,我们从使用者的视角理解了 Codex 的方方面面------CLI、桌面应用、IDE 插件、SDK、插件、MCP、CI/CD、批量任务。但如果你想知道 Codex 的 Agent Loop 到底是怎么实现的、为什么它能高效处理复杂任务、它的状态持久化机制是怎样的,就需要深入源码。Codex 的官方仓库是一个用 Rust 构建的大型工程------137 个 crate 构成的智能体架构。 本文从整体架构讲起,逐层拆解七层架构的设计,深入剖析 Session/Turn/Step 三级模型的实现,帮你理解在 Rust 中构建 AI 智能体的工程方法论。

一、为什么用 Rust 构建 Codex CLI?

Codex CLI 用 Rust 实现,而非 Python 或 TypeScript。这个选择背后有明确的工程考量:

性能:CLI 需要快速启动(毫秒级),Rust 的零成本抽象和静态编译让启动时间远优于 Python。

内存安全:AI 智能体需要处理来自不可信来源的数据(模型输出、工具返回结果),Rust 的所有权系统在编译期消灭了内存安全问题。

并发安全:Codex 需要同时管理多个工具调用、多个会话状态,Rust 的 Send/Sync 在编译期保证了并发安全。

单二进制分发:Rust 编译为单个静态二进制文件,用户无需安装运行时依赖,npm install 即可使用。

二、七层架构全景

Codex 的源码架构可以拆解为七层,从上到下依次为:

2.1 入口层:codex-cli

codex-cli 是用户交互的入口,负责:

解析命令行参数(codex、codex exec、codex mcp 等子命令)。

加载配置文件(config.toml、AGENTS.md)。

初始化认证(ChatGPT OAuth 或 API Key)。

启动会话核心。

rust 复制代码
// 简化的入口逻辑
fn main() {
    let args = Cli::parse();
    match args.command {
        Command::Interactive => run_interactive(args),
        Command::Exec { prompt, sandbox } => run_exec(prompt, sandbox),
        Command::Mcp { action } => run_mcp(action),
    }
}

2.2 会话核心层:codex-core

codex-core 是 Codex 的"大脑",实现了 Agent Loop、Turn 管理和状态机。

核心结构:

rust 复制代码
pub struct Session {
    id: SessionId,
    turns: Vec<Turn>,
    state: SessionState,
    config: SessionConfig,
}

pub struct Turn {
    id: TurnId,
    steps: Vec<Step>,
    status: TurnStatus,
}

pub struct Step {
    id: StepId,
    tool_call: Option<ToolCall>,
    model_response: Option<ModelResponse>,
}

Agent Loop 的实现:

rust 复制代码
impl Session {
    pub async fn run_turn(&mut self, user_input: String) -> Result<TurnResult> {
        let mut turn = Turn::new();
        let mut prompt = self.build_prompt(&user_input);

        loop {
            // 1. 查询模型
            let response = self.client.query(&prompt).await?;

            // 2. 判断是否有工具调用
            match response.tool_call {
                Some(tool_call) => {
                    // 3. 执行工具
                    let tool_result = self.execute_tool(tool_call).await?;
                    // 4. 将工具输出追加到提示词
                    prompt = self.append_tool_result(prompt, tool_result);
                    turn.add_step(Step::tool_call(tool_call, tool_result));
                }
                None => {
                    // 5. 没有工具调用,返回最终响应
                    turn.complete(response.text);
                    break;
                }
            }
        }

        self.turns.push(turn);
        Ok(turn.into_result())
    }
}

2.3 协议层:codex-protocol

codex-protocol 定义了 Codex 与模型、工具之间的通信协议。

核心 trait:

rust 复制代码
#[async_trait]
pub trait Tool {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn schema(&self) -> serde_json::Value;
    async fn execute(&self, input: serde_json::Value) -> Result<ToolOutput>;
}

工具路由:

rust 复制代码
pub struct ToolRouter {
    registry: ToolRegistry,
    parallel_tools: HashSet<String>,
}

impl ToolRouter {
    pub async fn route(&self, call: ToolCall) -> Result<ToolOutput> {
        let tool = self.registry.get(&call.name)?;
        tool.execute(call.input).await
    }
}

2.4 客户端层:codex-client

codex-client 负责与 OpenAI API(或自定义 Provider)通信。

rust 复制代码
pub struct Client {
    base_url: String,
    auth: Auth,
    http: reqwest::Client,
}

impl Client {
    pub async fn query(&self, prompt: &Prompt) -> Result<ModelResponse> {
        let request = self.build_request(prompt);
        let response = self.http.post(&self.base_url)
            .bearer_auth(self.auth.token())
            .json(&request)
            .send()
            .await?;
        self.parse_response(response).await
    }
}

2.5 沙盒执行层:codex-sandbox

codex-sandbox 负责安全执行命令和文件操作。它使用平台特定的隔离技术:

rust 复制代码
pub trait Sandbox {
    fn execute(&self, command: &Command) -> Result<CommandOutput>;
    fn read_file(&self, path: &Path) -> Result<String>;
    fn write_file(&self, path: &Path, content: &str) -> Result<()>;
}

// Linux 实现:Landlock + seccomp
pub struct LinuxSandbox { /* ... */ }

// macOS 实现:Seatbelt
pub struct MacOSSandbox { /* ... */ }

// Windows 实现:受限令牌
pub struct WindowsSandbox { /* ... */ }

2.6 状态持久化层:codex-state

codex-state 负责会话状态的保存和恢复。Codex 使用 Rollout 机制持久化会话:

rust 复制代码
pub struct Rollout {
    session_id: SessionId,
    turns: Vec<TurnRecord>,
    metadata: RolloutMetadata,
}

impl Rollout {
    pub fn save(&self, path: &Path) -> Result<()> { /* ... */ }
    pub fn load(path: &Path) -> Result<Self> { /* ... */ }
}

Rollout 的价值:当你中断一个会话后重新打开,Codex 可以从 Rollout 恢复完整的对话历史,继续之前的任务。

2.7 基础设施层:codex-common

codex-common 提供日志、遥测和配置管理:

rust 复制代码
pub struct Telemetry {
    session_id: SessionId,
    turn_id: TurnId,
    events: Vec<TelemetryEvent>,
}

impl Telemetry {
    pub fn record_turn_start(&mut self, turn: &Turn) { /* ... */ }
    pub fn record_tool_call(&mut self, call: &ToolCall) { /* ... */ }
    pub fn record_turn_end(&mut self, turn: &Turn) { /* ... */ }
}

三、Session / Turn / Step 三级模型的源码实现

三级交互模型是 Codex 状态管理的核心。

3.1 Session:完整会话

rust 复制代码
pub struct Session {
    id: SessionId,
    turns: Vec<Turn>,
    status: SessionStatus,
    config: SessionConfig,
    rollout: Option<Rollout>,
}

impl Session {
    pub fn new(config: SessionConfig) -> Self { /* ... */ }
    pub async fn run_turn(&mut self, input: String) -> Result<TurnResult> { /* ... */ }
    pub fn save_rollout(&self) -> Result<()> { /* ... */ }
}

3.2 Turn:用户视角的一轮交互

rust 复制代码
pub struct Turn {
    id: TurnId,
    user_input: String,
    steps: Vec<Step>,
    status: TurnStatus,
    started_at: Instant,
    completed_at: Option<Instant>,
}

pub enum TurnStatus {
    Running,
    Completed,
    Failed(String),
    Aborted,
}

3.3 Step:系统内部的一次推理-工具循环

rust 复制代码
pub struct Step {
    id: StepId,
    model_input: Prompt,
    model_output: ModelResponse,
    tool_call: Option<ToolCall>,
    tool_output: Option<ToolOutput>,
}

pub enum StepResult {
    ToolCall(ToolCall),
    FinalResponse(String),
}

四、从源码中学到的设计模式

4.1 命令模式:工具调用的统一抽象

Codex 将所有工具调用统一为 ToolCall 结构:

rust 复制代码
pub struct ToolCall {
    pub id: String,
    pub name: String,
    pub input: serde_json::Value,
}

无论是 Shell 命令、文件读写还是 MCP 工具,都通过相同的 ToolCall 结构表达。这让 Agent Loop 无需关心具体工具的实现细节。

4.2 状态机模式:Turn 的生命周期

Turn 的状态转换是一个明确的状态机:

text

Pending → Running → Completed

↓

Failed / Aborted

每个状态转换都有明确的条件和副作用,避免了状态混乱。

4.3 观察者模式:事件流

Codex 通过事件流将执行进度实时推送给客户端:

rust 复制代码
pub enum Event {
    TurnStarted { turn_id: TurnId },
    ToolCallStarted { call: ToolCall },
    ToolCallCompleted { call: ToolCall, output: ToolOutput },
    TurnCompleted { turn_id: TurnId, result: TurnResult },
}

SDK 的 runStreamed() 就是通过订阅这个事件流实现的。

五、七层架构的协作流程

将以上各层串联起来,一次完整的 Codex 任务执行流程如下:

text

  1. 入口层:用户执行 codex "修复 bug"

  2. 入口层:解析参数,加载配置,初始化认证

  3. 会话核心层:创建 Session,开始 Turn

  4. 会话核心层:构建提示词(系统指令 + 上下文 + 用户输入)

  5. 客户端层:发送请求到 OpenAI API

  6. 会话核心层:接收模型响应,发现工具调用请求

  7. 协议层:将工具调用路由到对应工具

  8. 沙盒执行层:在沙盒中执行工具(读取文件、运行命令)

  9. 会话核心层:将工具输出追加到提示词

  10. 客户端层:重新查询模型

  11. 重复 6-10 直到模型返回最终响应

  12. 状态持久化层:保存 Rollout

  13. 基础设施层:记录遥测事件

  14. 入口层:返回结果给用户

    六、对构建 AI 智能体的启示

    从 Codex 的源码中,我们可以提炼出构建 AI 智能体的通用设计原则:

  15. 分层解耦:将 Agent Loop、工具执行、状态管理、通信协议分离,每层只关心自己的职责。

  16. 统一抽象:用 ToolCall 统一所有工具调用,用 Event 统一所有进度通知。

  17. 状态持久化:Rollout 机制让会话可以中断和恢复,这对于长时间运行的任务至关重要。

  18. 安全隔离:沙盒层是独立的一层,不是散落在各处的安全检查。

  19. 可观测性:遥测事件贯穿整个执行流程,便于调试和优化。

  20. 类型安全:用 Rust 的类型系统表达状态转换,编译期消灭状态错误。

七、小结

为什么用 Rust:性能、内存安全、并发安全、单二进制分发。

七层架构:入口层 → 会话核心层 → 协议层 → 客户端层 → 沙盒执行层 → 状态持久化层 → 基础设施层。

Session / Turn / Step:三级交互模型的源码实现。

设计模式:命令模式(ToolCall)、状态机模式(Turn 生命周期)、观察者模式(事件流)。

协作流程:从用户输入到最终响应的完整链路。

通用原则:分层解耦、统一抽象、状态持久化、安全隔离、可观测性、类型安全。

相关推荐
MobotStone1 小时前
做了几个 Agent 项目后,我发现:真正拉开 AI 产品经理差距的,不是技术
人工智能·架构
IT大白鼠3 小时前
DeepSeek Harness 详解开源插件化 AI Agent 运行时:架构、模式、安装与生态
人工智能·架构·开源
这个DBA有点耶4 小时前
数据库双轨并行实战:全量并行策略、增量延迟控制、双向回切与一致性校验
数据库·架构·dba
Gl�ria4 小时前
MySQL 单机版 vs 高可用版:宕机排查 + 故障处理
mysql·adb·架构
Devlive 开源社区5 小时前
KnowForge 2026.0.8 发布:协作写作、团队空间、AI 朗读,这次更新有点大
架构
53488736abcdefg5 小时前
Hive 入门&架构原理
hive·hadoop·架构
一条大祥脚6 小时前
【CS336】lecture5 GPU|算力缩放|架构|内存模型|执行模型|TPU
架构
吴建旭 智宅焕6 小时前
智能家居B端交付能力解耦架构:从全链路自持到全国交付基础设施接入
架构·智能家居
fundoit6 小时前
为什么需要 Access Token 和 ID Token 两个令牌
java·spring·架构·github·oauth2