夜雨聆风学习资料网

ARTICLE · 959747

Codex源码拆解

Codex源码拆解

前日 OpenAI 正式开源 Codex 全量源码,我针对项目完整代码库做了分层拆分、模块梳理与底层逻辑逐段解析。

先说总体判断:

今天的 Codex 已经不能只理解为“LLM + Shell + while 循环”。更准确地说,它是一个智能体调度平台。

Codex 核心运行主链路

外围还接入 MCP、Skills、Plugins、Memory、Multi-Agent、Approval、Telemetry、Persistence。

这是理解整个 Codex 源码最重要的主线。

模型推理核心把下一步动作交给承载执行、状态、权限与扩展能力的智能体调度层。

1. Agent Loop

核心结论

Codex 底层仍保留经典 Agent Loop:

Agent Loop 标准闭环

核心入口:codex-rs/core/src/session/turn.rs

源码注释直接说明了这个循环的设计:

模型每次判断可能返回 function call 或 assistant message;如果返回 function call,就执行并把结果拼接到下一次推理;如果只有 assistant message,则本轮会话完成。

关键源码

以下中文注释均为我追加:

pub(crate) async fn run_turn( // 一个完整 Agent Turn 的主入口 sess: Arc , // 持有整个会话级状态、历史、工具、环境等 turn_context: Arc , // 当前 Turn 固定配置和运行上下文 input: Vec , // 当前用户提交给 Agent 的输入 prewarmed_client_session: Option , // 可复用已经预热的模型连接 cancellation_token: CancellationToken, // 支持用户中断整个 Turn ) -> CodexResult

值得注意的是,在第一次模型推理之前,Codex 会先执行:

drain_async_hook_results(&sess, &turn_context, true).await; // 先吸收上一轮异步 Hook 产生的新上下文 let mut client_session = prewarmed_client_session // 优先使用已经预热的模型连接 .unwrap_or_else(|| sess.services.model_client.new_session()); // 否则创建 Turn 级模型会话 run_pre_sampling_compact(&sess, &turn_context, &mut client_session, &cancellation_token).await; // 推理前先检查是否需要压缩上下文

完成这些准备后,Codex 才进入持续推理。

真正的产品设计

因此,当前 Codex 的 Agent Loop 更接近:

Agent Loop 决策与回流

关键在于:

Codex 不会简单地重复调用模型,而是会在每个推理步骤重新构建模型所见的“世界视图”。

这是一项重要的架构演进。

一个 Turn 容器内,第一次模型判断触发 Tool Call 与执行,Tool Result 返回后重新注入上下文,再次模型判断并产生 Final Answer。

2. Intent Understanding & Task Routing

核心结论

Codex 当前没有设置统一的:

意图理解与任务路由

至少,运行时主路径并不采用这种结构。

它把路由分成两层:

1.第一层:程序确定性路由

客户端明确操作直接映射为 Typed Op。

2.第二层:模型语义路由

进入普通 Turn 后,“要不要查代码、执行 Shell、调用 MCP、启动 Agent”等,交给模型结合 Tool Specs 和 Instructions 判断。

核心文件:codex-rs/core/src/session/handlers.rs

程序级 Routing

match sub.op { // 根据客户端提交的强类型操作进行确定性路由 Op::Interrupt => { // 明确的中断操作,不需要让模型判断 interrupt(&sess).await; // 直接终止当前任务 false // Session 不退出 } // 完成 Interrupt 分支 Op::Compact => { // 用户明确要求进行 Context Compact compact(&sess, sub.id.clone()).await; // 直接进入 CompactTask false // Session 继续运行 } // 完成 Compact 分支 Op::Review { review_request } => { // 明确进入 Code Review 工作流 review(&sess, &config, sub.id.clone(), review_request).await; // 创建 Review 流程 false // Session 继续运行 } // 完成 Review 分支

同一个任务调度器同时还会处理:

•TurnInput

•RecoverTurn

•SuspendTurnAndShutdown

•ThreadSettings

•InterAgentCommunication

•ExecApproval

•PatchApproval

•RequestPermissionsResponse

•DynamicToolResponse

•RefreshMcpServers

•RunUserShellCommand

•Rollback

•Memory Mode

•Shutdown

产品含义

这种划分很明确:

确定性行为

例如:

•/compact

•review

•interrupt

•rollback

•approval

不要浪费 LLM 推理。

程序直接完成 Routing。

模糊语义行为

例如用户说:

帮我看看这个项目登录为什么有问题。

此时:

•是否搜索代码?

•搜哪些文件?

•是否运行测试?

•是否调用 sub-agent?

•是否联网?

这些问题交给 Model 推理。

因此,Codex 的原则是:

能由协议确定的意图,不交给模型;真正需要语义理解的部分,才交给模型。

相比“所有请求先经过 Intent Agent”,这种方式更稳定。

3. Context Engineering

Context Engineering 是 Codex 当前最值得研究的模块之一。

核心结论

Codex 的 Context 已经不再只是:

system prompt + chat history + user message

它至少包括:

Base Instructions + User / Developer Instructions + AGENTS.md + Environment State + Workspace State + Conversation History + World State + Skills + Plugins + Memory + MCP / Connector Context + Current User Input

同时还有:

Compaction + Reinjection + 当前执行上下文快照

3.1 当前执行上下文

run_turn 第一次请求前:

let first_step_context = sess // 从 Session 中开始捕获上下文快照 .capture_step_context_with_required_mcp_servers( // 同时考虑本轮必须启用的 MCP Server Arc::clone(&turn_context), // 保留当前 Turn 固定配置 &cancellation_token, // 捕获过程也支持中断 &required_servers, // 用户输入显式依赖的 MCP Servers ) // 完成参数构造 .await; // 异步等待 Environment/MCP 等状态准备完成

之后每轮推理还会重新 capture。

这表明:

TurnContext ≠ StepContext。

TurnContext 是这一轮的相对稳定配置。

当前执行上下文指:

模型这一次推理真正看到的环境快照。

3.2 Compaction

核心:codex-rs/core/src/compact.rs

Codex 已经明确区分:

•Pre-turn compaction

•Manual compaction

•Mid-turn compaction

更关键的问题是 Compaction 后如何重新注入上下文。

pub(crate) enum InitialContextInjection { // 定义压缩后如何重新恢复基础 Context BeforeLastUserMessage { // Mid-turn compact 使用这种策略 world_state: Arc , // 保留压缩时真实的环境感知 step_context: Arc , // 保留对应推理步骤的运行环境 }, // Mid-turn 模式结束 DoNotInject, // Manual/Pre-turn compact 后先不注入,下一个普通 Turn 再完整注入 } // Context reinjection 策略结束

这一设计已经相当成熟。

Context Compact 最大的风险并不在:

“摘要写得好不好。”

而是:

摘要后模型看到的真实环境是否还和现实一致。

Codex 已经着手处理这个问题。

4. Knowledge & Retrieval

核心结论

Codex 当前没有把 Knowledge 设计为单独的“万能知识库模块”。

它采用:

Knowledge as Tools

不同来源的知识通过以下入口进入:

知识与检索入口

因此:

Retrieval 是多源的,不是一个统一 Vector DB。

Web Search 已被 Extension 化

文件:codex-rs/ext/web-search/src/extension.rs

impl ToolContributor for WebSearchExtension { // Web Search 本质上是一个 Tool Extension fn tools( // Extension 向 Codex 运行时注册可执行工具 &self, // 当前 WebSearch Extension session_store: &ExtensionData, // Session 级扩展数据 thread_store: &ExtensionData, // Thread 级扩展配置 ) -> Vec> { // 返回可以暴露给 Agent 的工具执行器

Web Search 是否可用,也由系统动态计算:

available: (config.model_provider.is_openai() // 当前 Provider 支持 OpenAI Web Search || config.model_provider.uses_openai_actor_authorization() // 或使用兼容授权 || config.model_provider.supports_standalone_web_search()) // 或 Provider 自己提供 Web Search && web_search_mode != WebSearchMode::Disabled, // 同时配置没有禁用 Web Search

产品含义

Codex 没有将:

“知识检索”

绑定为:

“RAG”。

而是将其抽象为:

Agent 可以访问哪些 Knowledge Interfaces。

这对 Coding Agent 的设计很重要。

5. Workspace & Environment

核心结论

Codex 里的 Workspace 已经不只是:

cwd = /repo

现在有了正式的 Environment 抽象。

核心:codex-rs/core/src/environment_selection.rs

Thread 可以绑定多个 Environment。

每个 Environment 至少可以拥有:

•environment_id

•cwd

•workspace roots

•EnvironmentConfig

•ExecutorFileSystem

•Shell

•Capability Roots

•connection state

默认 Environment 生成

.map(|environment_id| TurnEnvironmentSelection { // 为每个可用 Environment 创建选择对象 environment_id, // 标识具体执行环境,例如 local 或 remote environment cwd: PathUri::from_abs_path(cwd), // 每个环境拥有自己的当前工作目录 workspace_roots: workspace_roots.iter() // 获取允许作为 Workspace 的目录集合 .map(PathUri::from_abs_path).collect(), // 统一转换成跨环境 Path URI config: EnvironmentConfigState::FromThread, // Environment 默认继承 Thread 配置 }) // 一个完整 Environment Selection 构造完成

这里的产品思路很重要

传统 Coding Agent:

业务流程图 · 传统 Coding Agent 执行链

Codex 正在向:

Agent Thread 的多环境绑定

演进。

因此,未来的 Coding Agent Workspace 本质上更接近:

Execution Environment Graph

而非“一个项目目录”。

6. Tool System & Tool Routing

Tool System 是 Codex 运行时的另一个核心模块。

核心文件:codex-rs/core/src/tools/router.rs

6.1 Tool Spec 与工具执行器分离

pub struct ToolRouter { // 当前推理步骤使用的统一工具路由器 registry: ToolRegistry, // 真正能够执行 Tool 的注册表 model_visible_specs: Arc<[ToolSpec]>, // 当前 Model 实际能够看到的 Tool Schema } // ToolRouter 定义结束

这种分离很重要。

因为:

已注册的 Tool ≠ 本轮模型需要看到的 Tool

6.2 Model Response → ToolCall

Codex 会统一转换不同类型的 Tool Call:

ResponseItem::FunctionCall { // 模型返回普通 Function Tool Call name, namespace, arguments, call_id, .. // 提取 Tool 身份、参数以及调用 ID } => { // 开始规范化 Tool Call let tool_name = ToolName::new(namespace, name) // 将 namespace + name 合成为统一 ToolName .with_default_namespace(); // 没有 namespace 时补充默认 namespace Ok(Some(ToolCall { // 转换成 Codex 内部统一 ToolCall

此外还支持:

•FunctionCall

•ToolSearchCall

•CustomToolCall

6.3 最终全部进入 Registry

let invocation = ToolInvocation { // 将一次 Tool Call 包装成完整的执行调用 session, // Session 状态 turn, // Turn 状态 step_context, // 当前推理步骤的环境快照 cancellation_token, // 工具执行支持取消 tracker, // 追踪工具对 Workspace 造成的变化 call_id, // 对应 Model Tool Call ID tool_name, // 要执行的 Tool source, // Tool 调用来源 payload, // Tool 参数 }; // Invocation 构造结束

然后:

self.registry // 进入统一 Tool Registry .dispatch_any_with_terminal_outcome(invocation, terminal_outcome_reached) // 找到具体执行器并执行 .await // 等待 Tool 执行结果

产品设计总结

Codex 的 Tool 架构可以抽象成:

工具调用与执行路由

这个分层值得 Agent 平台借鉴。

左侧 Tool Spec 表示模型可见 Schema,中间 ToolRouter 将其连接到右侧工具执行器与注册表。

7. MCP & External Connectors

Codex 的 MCP 已经不只是:

MCP Server 启动与注册

现在有专门的管理组件:

McpManager

核心文件:codex-rs/core/src/mcp.rs

MCP 运行配置投影

pub(crate) struct McpRuntimeProjection { // 描述当前推理步骤真正有效的 MCP 运行配置 pub(crate) config: McpConfig, // 当前合并完成后的 MCP 配置 pub(crate) plugins_available: bool, // 当前是否存在可用 Plugin pub(crate) selected_plugins: SelectedPluginSnapshot, // 当前实际启用的 Plugin 集合 } // 运行配置投影定义结束

更关键的一点是:

MCP 配置是按推理步骤计算的。

pub(crate) async fn runtime_config_for_step( // 为当前推理步骤构造有效 MCP 配置 &self, // McpManager config: &Config, // Thread/Codex 配置 thread_init: &ExtensionDataInit, // Thread 初始化扩展数据 thread_store: &ExtensionData, // 当前 Thread Extension State identity: McpThreadIdentity<'_>, // 当前 Thread/Originator/Environment 身份

输入来源会融合:

Static MCP Config + Extension Contributions + Hosted Apps + Selected Plugins + Plugin Connector Packages + Environment Policy

它还支持 Contributor:

•Set

•Remove

•HostedApps

•SelectedPlugin

•SelectedPluginPackage

因此,MCP 在 Codex 中已经演变为:

外部能力执行层(External Capability Runtime)

不再只是 Protocol Client。

8. Memory

Memory 值得单独说明,因为 Codex 当前源码里已经有清晰的 Memory 架构。

Memory 被拆成独立 Extension:

•codex-rs/ext/memories

以及:

•codex-rs/memories/read

•codex-rs/memories/write

Memory Root

pub fn memory_root(codex_home: &AbsolutePathBuf) -> AbsolutePathBuf { // 根据 Codex Home 计算 Memory 根目录 codex_home.join("memories") // 默认持久化到 $CODEX_HOME/memories } // Memory Root 计算结束

Memory 是一个 Extension Contributor

enabled: config.features.enabled(Feature::MemoryTool) // Feature Flag 必须开启 && config.memories.use_memories, // 用户配置也必须允许 Memory dedicated_tools: config.memories.dedicated_tools, // 决定是否给模型暴露专门 Memory Tool codex_home: config.codex_home.clone(), // Memory 数据保存位置来源于 Codex Home

Memory 可以作为 Prompt:

build_memory_tool_developer_instructions(&config.codex_home) // 根据 Memory 状态生成使用说明 .await // 异步读取本地 Memory 信息 .map(PromptFragment::developer_policy) // 作为 Developer Policy 注入 Prompt

同时还可以作为 Tool:

tools::memory_tools( // 创建真正可供 Agent 使用的 Memory Tools LocalMemoriesBackend::from_codex_home(&config.codex_home), // 使用本地 Memory Backend self.metrics_client.clone(), // Memory Tool 自己也接入 Metrics ) // 返回 Memory Tool 集合

产品含义

Memory 并非:

每次把所有历史聊天塞给模型。

而是:

Conversation History ≠ Memory

Codex 已经正式拆分二者。

Memory 有自己的:

•lifecycle

•storage

•read path

•write path

•tools

•prompt policy

•citations

•telemetry

这是一套较成熟的 Agent Memory 架构。

9. 智能体调度层

核心结论

Codex 智能体调度中心可以分成三层:

调度引擎分层

核心抽象:SessionTask

文件:codex-rs/core/src/tasks/mod.rs

pub(crate) trait SessionTask: Send + Sync + 'static { // 所有可在 Session 内运行的任务统一实现这个接口 fn kind(&self) -> TaskKind; // 告诉执行器当前是哪一类 Task fn span_name(&self) -> &'static str; // 给 tracing/observability 提供任务名称 fn run( // Task 真正的异步运行入口 self: Arc , // Task 实例 session: Arc , // 共享 Session 运行时

run 这个执行方法同时拿到:

•TurnContext

•TurnInput

•CancellationToken

目前核心 Task Module 中可以看到:

regular review compact user_shell lifecycle

这个抽象为什么重要?

Codex 没有让:

run_turn() 管理所有事情。

而是:

SessionTask 任务分派

Agent Loop 只是运行时中的一类任务。

这样一来:

•Review

•Compact

•Shell

•Regular Agent

能够共享同一套:

•lifecycle

•cancellation

•telemetry

•state

•persistence

10. Sandbox & Isolation

Codex 的安全边界不只依赖 Prompt 中的一句:

“不要访问敏感文件。”

它还通过 OS 机制执行约束。

核心:codex-rs/sandboxing/src/manager.rs

平台 Sandbox

pub enum SandboxType { // Codex 支持的平台级隔离类型 None, // 不启用平台 Sandbox MacosSeatbelt, // macOS 使用 Seatbelt LinuxSeccomp, // Linux 使用 Seccomp/Landlock 等机制 WindowsRestrictedToken, // Windows 使用 Restricted Token Sandbox } // Sandbox 类型结束

平台自动选择:

if cfg!(target_os = "macos") { // 当前运行在 macOS Some(SandboxType::MacosSeatbelt) // 使用 macOS Seatbelt } else if cfg!(target_os = "linux") { // 当前运行在 Linux Some(SandboxType::LinuxSeccomp) // 使用 Linux Sandbox } else if cfg!(target_os = "windows") { // 当前运行在 Windows

Sandbox Request 同时包含:

•cwd

•env

•network

•permission profile

•sandbox policy cwd

•environment id

•managed network

•additional permissions

因此,这不是简单的:

sandbox = true / false

它是一套:

Policy-driven execution sandbox。

11. Task Lifecycle & State

这一部分解释了 Codex 为什么可以被中断、恢复、审批和 Steer。

核心文件:codex-rs/core/src/state/turn.rs

ActiveTurn

pub(crate) struct ActiveTurn { // Session 当前正在运行的 Turn pub(crate) task: Option , // 当前真正执行中的 Task pub(crate) turn_state: Arc, // 当前 Turn 所有可变运行状态 } // ActiveTurn 定义结束

RunningTask

pub(crate) struct RunningTask { // 运行时对正在运行 Task 的完整描述 pub(crate) done: Arc , // Task 完成通知机制 pub(crate) kind: TaskKind, // Regular / Review / Compact pub(crate) task: Arc , // 真正运行的 Task pub(crate) cancellation_token: CancellationToken, // 中断控制 pub(crate) handle: AbortOnDropHandle<()>, // Tokio Task Handle

TurnState 还维护更多运行状态

它里面直接维护:

pending_approvals: HashMap >, // 等待用户审批的 Tool Call pending_request_permissions: HashMap , // 等待权限授予 pending_user_input: HashMap >, // Agent 主动向用户提问后的等待状态 pending_elicitations: HashMap<(String, RequestId), oneshot::Sender >, // MCP elicitation 等待状态 pending_dynamic_tools: HashMap >, // 动态 Tool 的异步返回

这表明 Turn 并非只有:

running / completed

它是一个真正的异步状态机。

当前版本还加入 Suspend + Recovery

最新 main 中已有:

Op::SuspendTurnAndShutdown { reply } => { // 请求暂停当前未完成 Turn 并关闭运行时 let result = super::turn_suspension::suspend_turn_and_shutdown( // 进入 Turn Suspension 流程 &sess, sub.id.clone() // 对当前 Session/Turn 执行挂起 ).await; // 等待 History 持久化和运行时安全关闭

目标是:

未完成 Turn 可以交给另一个运行时恢复,而不把它标记为 Completed 或 Aborted。

这是智能体调度层服务化的明确信号。

12. Multi-Agent Orchestration, Scheduling & Workload

现在,Codex 的 Multi-Agent 已经超出“调用一次 sub-agent”的范围。

核心:

•codex-rs/core/src/session/multi_agents.rs

•codex-rs/core/src/agent/control/

Agent 是层级树结构

内置 instruction 明确告诉 Root:

"You can spawn sub-agents to handle subtasks, // Root Agent 可以创建子 Agent and those sub-agents can spawn their own sub-agents. // 子 Agent 也可以继续创建 Agent All agents ... have access to the same set of tools." // Agent 默认具备同级工具能力

可用协作操作包括:

spawn_agent send_message followup_task wait_agent interrupt_agent list_agents

Context Fork 也是明确设计

fork_turns

决定父 Agent 历史向子 Agent 传播多少。

spawn 源码还会过滤 Fork History:

"system" | "developer" | "user" => true, // 系统、开发者和用户消息允许继承 "assistant" => *phase == Some(MessagePhase::FinalAnswer), // Assistant 只保留已经成为 Final Answer 的内容 _ => false, // 其他角色默认不进入子 Agent History

Tool calls、reasoning 等并不会无脑完整复制。

这相当于:

Sub-Agent Context Isolation。

Workload 也已进入执行层

let max_concurrency = config.max_concurrent_threads_per_session; // 获取当前 Session 最大并发 Agent 数 let mut text = format!( // 将真实执行器容量告知模型 "... {max_concurrency} available concurrency slots ..." // 模型知道自己还剩多少并发槽位 ); // Multi-Agent 使用提示完成

因此,Scheduling 并非完全由模型自由决定。

运行时设有明确的:

concurrency slots。

一项值得注意的设计

源码中:

Some(ReasoningEffort::Ultra) => MultiAgentMode::Proactive, // Ultra 推理级别可自动切换到主动多 Agent _ => ... MultiAgentMode::ExplicitRequestOnly, // 普通模式默认更保守,只在显式需要时启动

这一设计开始将:

推理预算

和:

Agent 并行预算

关联起来。

这一关联值得关注。

13. Model & Inference Layer

Codex 并非只是:

model="xxx" client.responses.create(...)

这样一层简单调用。

它至少有:

模型推理调用链

Codex 将 Session ModelClient 与 Turn ModelClientSession 分离

核心:codex-rs/core/src/client.rs

源码注释明确说明:

ModelClient 是 Session-scoped;ModelClientSession 是 Turn-scoped。

ModelClient 保存:

•Provider

•Auth

•Thread ID

•transport fallback state

•originator

•telemetry config

而 Turn-scoped Session 保存:

•WebSocket

•previous response

•incremental request state

•sticky routing token x-codex-turn-state

这表明:

Inference transport 本身也有生命周期。

Model Metadata 还包含更多调度层信息

codex-rs/models-manager/src/model_info.rs

模型 Metadata 保存的不只是名称。

其中包括:

shell_type: ConfigShellToolType::UnifiedExec, // 模型默认使用哪种 Shell Tool web_search_tool_type: WebSearchToolType::Text, // 模型支持哪种 Web Search truncation_policy: TruncationPolicyConfig::bytes(10_000), // Tool Output 如何截断 context_window: Some(272_000), // 模型 Context Window multi_agent_version: None, // 模型对应哪一版多智能体调度层

因此:

Model Metadata 会反向驱动智能体调度层行为。

这已经超出“模型下拉选择器”的范畴。

14. Prompt & Instruction System

Codex 的 Prompt 体系已经采用层级式 Instruction System。

不能把它简单理解为一个:

SYSTEM_PROMPT.md

第一层:Model Base Instructions

模型自身 metadata 中:

pub const BASE_INSTRUCTIONS: &str = include_str!("../prompt.md"); // 编译时加载模型默认基础指令

同时允许:

if let Some(base_instructions) = &config.base_instructions { // 用户或 Host 显式覆盖基础 Prompt model_messages.instructions_template = Some(base_instructions.clone()); // 替换模型默认 Instructions model_messages.instructions_variables = None; // 自定义模板后清空默认模板变量 } // Base Instructions override 完成

第二层:AGENTS.md

核心文件:codex-rs/core/src/agents_md.rs

它并非只读取 cwd 下的一个文件。

规则是:

AGENTS.md 分层发现与拼接

按层级拼接。

源码说明:

•向上搜索 project root

•默认以 .git 为 marker

•从 root 一直收集到 cwd

•不超过 project root

•有最大 byte budget

•读取还受 Sandbox 限制

一项安全设计

if config.active_project.is_untrusted() { // 当前项目被标记为不可信 return Ok((!loaded.is_empty()).then_some(loaded)); // 不继续读取项目级 AGENTS.md } // 阻止不可信 Repo 注入额外项目 Prompt

这在防范:

Repository Prompt Injection。

Prompt 采用 Layered Prompt

最终 Prompt 可以理解为:

 Layered Prompt:指令装配顺序

Codex 的 Prompt Engineering 已经演变为:

Instruction Engineering。

15. Skills, Plugins & Extensions

当前 Codex 源码中,这部分的变化也很明显。

Skill 是可被发现的文件系统能力包

核心:codex-rs/ext/skills/src/loader/host.rs

Skill Root 有:

pub(crate) struct HostSkillRoot { // 一个可以被 Skill Loader 扫描的根目录 pub(crate) path: AbsolutePathBuf, // Skill Root 实际文件路径 pub(crate) scope: SkillScope, // User / Repo / Admin / System Scope pub(crate) file_system: Arc , // Skill 可以来自抽象 Environment FS plugin: Option , // Skill 也可以隶属于某个 Plugin } // Skill Root 定义结束

Skill Discovery 并非简单遍历目录

不同 Scope 甚至有不同 Symlink Policy:

SkillScope::User | SkillScope::Repo | SkillScope::Admin // 用户、Repo、管理员 Skill => DirectorySymlinkPolicy::Follow, // 允许跟随目录 Symlink SkillScope::System // 系统 Skill => DirectorySymlinkPolicy::Ignore, // 系统级 Skill 更严格

Plugin Skill 还会检查:

Canonicalized path 必须仍位于 Plugin Root 内。

防止通过 symlink/path trick 越界。

Extension 是更高一层抽象

Memory Extension 已经展示了 Extension 可以同时贡献:

Thread Lifecycle Config Prompt Context Tools

Web Search Extension 又贡献:

Config Thread Lifecycle Tool

MCP Manager 则允许 Extension:

MCP Server Contribution

由此,Codex 正在形成:

Extension Contributor 能力模型

相较单一 Skill,这套机制更接近:

Agent Plugin Framework。

16. Permission, Approval & Safety Governance

这里需要区分两个概念:

Sandbox

负责:

技术上能不能做。

Approval

负责:

治理上允不允许做。

Codex 已经明确分开两者。

Central Approval Stage

核心:codex-rs/core/src/tools/approvals.rs

pub(crate) enum ApprovalAction { // 所有需要风险治理的动作统一抽象为 ApprovalAction ExecCommand { /* ... */ }, // Shell / Process 执行审批 ApplyPatch { /* ... */ }, // 文件修改审批 McpToolCall { /* ... */ }, // 外部 MCP / App 行为审批 NetworkAccess { /* ... */ }, // 网络访问审批 RequestPermissions { /* ... */ }, // 运行时动态申请更多 Permission } // ApprovalAction 定义结束 ExecCommand { / ... / }, // Shell / Process 执行审批 ApplyPatch { / ... / }, // 文件修改审批 McpToolCall { / ... / }, // 外部 MCP / App 行为审批 NetworkAccess { / ... / }, // 网络访问审批 RequestPermissions { / ... / }, // 运行时动态申请更多 Permission

MCP Approval 甚至携带:

•connector_id

•connector_name

•connected_account_email

•tool_title

•approval policy

•reviewer

•approval mode

因此,审批 客户交互端 可以知道:

哪个外部应用、哪个账号、哪个动作正在被调用。

Guardian

源码还包含:

GuardianReviewContext GuardianReviewOptions review_approval_request routes_approval_policy_to_guardian

这意味着审批方不一定只有:

Human。

系统也可以:

权限审批与安全治理流程

这已经构成:

Agent Governance Layer

而不只是权限弹窗。

17. Protocol & Client Integration

Codex 运行时和 客户交互层 已明显解耦。

直接证据是:

app-server-protocol

TurnStartParams

文件:codex-rs/app-server-protocol/src/protocol/v2/turn.rs

pub struct TurnStartParams { // Client 启动一次 Agent Turn 的正式协议对象 pub thread_id: String, // 指定 Turn 属于哪个 Thread pub input: Vec , // 用户输入,可以包含多模态内容 pub environments: Option, // 可为本 Turn 指定执行 Environment pub cwd: Option , // 可动态切换工作目录 pub approval_policy: Option , // 可动态覆盖审批策略

继续还有:

pub sandbox_policy: Option , // 本 Turn 可覆盖 Sandbox Policy pub permissions: Option , // 可选择 Permission Profile pub model: Option , // 可在 Turn 边界切换模型 pub effort: Option , // 可调整 Reasoning Budget pub output_schema: Option , // 可约束最终回答 JSON Schema

由此可见:

Sandbox、Model、Environment、Permissions、Collaboration 并不是 CLI 特性。

它们已经是正式的运行时协议。

TypeScript SDK

sdk/typescript/src/thread.ts

SDK 向用户暴露的接口很简洁:

async run(input: Input, turnOptions: TurnOptions = {}): Promise { // 非流式执行一次 Agent Turn const generator = this.runStreamedInternal(input, turnOptions); // 底层仍然走流式事件执行层 const items: ThreadItem[] = []; // 收集整个 Turn 产生的 Item let finalResponse: string = ""; // 保存最终 Agent Answer let usage: Usage | null = null; // 保存 Token Usage

因此,SDK 是:

运行时协议的高级封装。

当前 Client Surface

从源码可以看到至少包括:

•CLI

•TUI

•VS Code

•Exec

•App Server

•TypeScript SDK

•Python SDK

•MCP

•Custom Client

这说明 Codex 的核心产品正在从:

Codex CLI

转向:

Codex 智能体调度层 + 多个客户端。

18. Observability, Diagnostics & Reliability

Observability 很重要,因为 Agent 面临的一个主要问题是:

“失败时到底哪里出了问题?”

Codex 已经建立了较完整的运行监控体系。

18.1 Telemetry

codex-rs/otel/src/events/session_telemetry.rs

可以看到:

use crate::metrics::API_CALL_DURATION_METRIC; // API 请求耗时 use crate::metrics::TOOL_CALL_DURATION_METRIC; // Tool 执行耗时 use crate::metrics::TURN_TTFT_DURATION_METRIC; // Turn 首 Token 延迟 use crate::metrics::WEBSOCKET_REQUEST_DURATION_METRIC; // WebSocket 请求耗时 use crate::metrics::SSE_EVENT_DURATION_METRIC; // SSE Stream Event 耗时

SessionTelemetry 还携带:

•thread id

•agent name

•session source

•model

•reasoning effort

•service tier

•terminal type

•app version

•auth metadata

18.2 智能体调度层自己也有指标

Tasks 中直接有:

TURN_E2E_DURATION TURN_MEMORY TURN_NETWORK_PROXY TURN_TOKEN_USAGE TURN_TOOL_CALL TURN_UNIFIED_EXEC_RUNNING_PROCESSES

借助这些指标可以分析:

某个 Turn 慢,到底慢在 Model、Tool、Network、Memory 还是 Exec。

18.3 Retry + Transport Fallback

codex-rs/core/src/responses_retry.rs

if retry_state.retries >= max_retries // 当前 Transport 已经达到最大重试次数 && client_session.try_switch_fallback_transport( // 尝试切换备用 Transport &turn_context.session_telemetry, // 同时记录 Transport fallback telemetry &turn_context.model_info, // 根据当前模型能力判断能否 fallback ) // Fallback 判断完成

随后会向用户发送:

message: format!("Falling back from WebSockets to HTTPS transport. {err:#}"), // 明确告知 UI 已从 WebSocket 降级到 HTTPS

连接异常还采用:

5s 10s 20s 40s 60s 60s...

指数退避。

整体架构重新拼起来

Codex 当前的真实架构大致如下:

业务流程图 · Codex 智能体运行平台:整体业务流程

我认为 Codex 源码里最值得产品设计学习的 8 个思想

如果把几万行源码压缩为产品方法论,我认为值得带走的不是某个 Rust 类,而是以下原则。

① Agent Loop 要极简,但 Loop 外围要工程化

Agent 最底层仍然只是:

 极简 Agent Loop

复杂能力不应硬塞进这个 Loop,而应放在外围的:

•Context

•Runtime

•Tool

•State

•Governance

由外围层解决。

② Turn 和当前执行的上下文必须分开

这是当前 Codex 一项成熟的设计:

Thread、Turn 与执行步骤

每次模型判断都对应新的执行上下文。

这样,Agent 可以在一个 Turn 中动态感知:

•Workspace 已改变

•MCP 已改变

•Permission 已改变

•Sub-agent 返回结果

•用户新增内容

•Tool 改了文件

③ Tool Schema 和工具执行器必须分开

这对 Agent 平台很有参考价值:

模型可见工具生成流程

不必一次把 100 个 Tool Schema 全部塞给模型。

④ Context 不应该只是 Prompt 拼接

Codex 已经把 Context 构建为:

运行状态投影(Runtime State Projection)

它回答的是:

“在这一时刻,这个 Agent 应该看到的真实环境是什么?”

而不再只是:

“我该往 System Prompt 再拼什么字符串?”

⑤ Approval 和 Sandbox 必须分层

这是安全架构中的关键原则:

Approval = 这件事允不允许做          Sandbox = 就算允许,系统物理上能让它做到什么程度

只有 Approval 没有 Sandbox,不安全。

只有 Sandbox 没有 Approval,不可治理。

⑥ Multi-Agent 的难点在 Context + Scheduling,不只在 Spawn

Codex 已经开始解决:

•Context fork

•Agent Tree

•Message delivery

•Concurrency slots

•Agent persistence

•Agent recovery

•shared workspace

•resident runtime

这些能力共同构成真正的 Multi-Agent Engineering。

⑦ Skills / MCP / Plugins 应该是不同层级

从当前 Codex 架构可以看出,三者正逐步分开:

Skill = 教 Agent 怎么完成一类任务          Tool / MCP = 给 Agent 一个外部执行能力          Plugin / Extension = 给整个调度层安装一整套能力

Plugin 可以同时带:

Skill Tool MCP Prompt Config Lifecycle Connector

相比把所有东西都称为“插件”,这种区分清晰得多。

⑧ Coding Agent 最终会从 工具 演进成智能体调度底座

这是我阅读当前 Codex 源码后看到的最明显趋势。

早期更像:

早期 Coding Agent 模式

现在则越来越像:

从单体应用演进为智能体调度平台

当前版本新加入的:

•Turn Suspension

•Recovery

•App Server Protocol

•Environment abstraction

•Agent persistence/residency

•Multi-Agent scheduling

•Extension system

都表明它正在向:

通用 Coding Agent 调度中心 / Agent Operating Layer

这一方向演进。

最终结论

用一句话定义当前 Codex:

Codex 是一个围绕 LLM 推理构建的、可持久化、可扩展、可治理、支持多执行环境和多 Agent 协作的事件驱动智能体调度平台。

它的技术壁垒已经从:

“会不会调用 Shell。”

转向下面这套完整体系:

Model Intelligence + Agent Loop + Context Engineering + tool call + Execution Environment + State & Lifecycle + Multi-Agent + Extension system + Permission & Sandbox + Protocol + Observability

其中,我认为最值得继续深入源码的 6 个模块是:

Agent Loop → Context Engineering → tool call → Multi-Agent → Permission/Sandbox → Extension System。

这六块构成了 Codex 最核心的“Agent OS”基本骨架。

能看到此处的都是潜心学习、深耕 AI 领域的技术伙伴,给你们个超赞。如果需要 Codex 源码资源的朋友点赞留痕,我私信发送链接。

相关学习资料