双型智能体架构对照:Claude Code 与 Codex 运行机制与核心设计对比¶
Claude Code 与 Codex 都不是单一形态的工具:Claude Code 有终端交互、SDK 调用、服务端桥接、一次性执行等多种入口,终端交互只是最常见的一种;Codex 同样有 TUI 交互终端、codex exec 非交互执行、app-server 协议服务等多种入口,且 TUI 与 codex exec 场景下 app-server 是同进程内的协议层,而不是一个独立部署的远端服务。两者真正的架构差异不在"本地交互 vs 服务端异步"这个错误框架上,而在于会话状态建模方式(Transcript 线性转录 vs Thread-Turn-Item 结构化状态机)和审批/权限机制的具体设计上。本文从系统架构、会话状态机、上下文压缩和权限屏障四个维度做对照,具体机制的完整拆解见本站的 claude-code-source 与 codex-agent-source 两个源码系列,本文只做横向对比,不重复展开单侧细节。
1. 系统架构与执行流向对比¶
两者的核心差异不在"本地交互 vs 服务端异步",而在于会话状态如何建模、工具执行如何与状态机衔接:
========================================================================================
【Claude Code: QueryEngine + Transcript 循环(以终端交互入口为例)】
用户输入 (CLI) ──> QueryEngine (同步组装上下文) ──> Transcript Loop (采样-执行-回填闭环)
│
└──> 本地 Tool (直接 exec / stdio)
========================================================================================
【Codex: Thread / Turn / Item 会话状态机(以 codex exec 一次性执行入口为例)】
用户输入 ──> app-server (进程内协议层) ──> Session (操作队列串行处理) ──> Thread / Turn / Item 结构体
│
└──> 工具执行 (权限 + 审批策略 + 沙箱)
========================================================================================
注:这里各只画了一条最常见的入口路径便于对照核心状态机。Claude Code 实际还有 SDK 调用、服务端桥接、内部子智能体/分叉技能等多种入口形态,详见 claude-code-source/03-entrypoints.md;Codex 实际还有 TUI 交互终端、外部协议客户端(如 IDE 插件)等入口,且 TUI 与
codex exec场景下 app-server 是同进程内的协议转换层,不是独立部署的远端服务,详见 codex-agent-source/03-entrypoints.md。
- Claude Code:围绕一个轻量级的 QueryEngine 与 Transcript(转录记录) 循环展开(详见 claude-code-source/04-query-loop.md)。它的状态是高度线性的,模型输出的工具意图直接在本地进程内执行并同步等待回填。终端交互入口下这意味着低延迟的同步确认体验;非交互入口(SDK、一次性执行)没有界面时,需要询问的动作在策略不允许自动批准的情况下会直接拒绝或失败,而不是挂起等待。
- Codex:采用
Thread -> Turn -> Item三层状态模型(详见 codex-agent-source/02-domain-model.md)。所有会话级操作被归入同一个操作队列串行调度以避免并发脑裂,模型通道支持 HTTP 与 WebSocket 两种传输(详见 codex-agent-source/06-model-streaming.md),工具执行经过权限、审批策略与沙箱多层判断(详见 codex-agent-source/08-permissions-safety.md)。命令执行链路本身是同步的进程级子进程派生(详见 project-breakdowns/openai-codex.md),"操作队列串行处理"解决的是多路输入(用户输入、中断、审批响应)的顺序一致性问题,不代表整体是服务端异步部署的架构。
2. 核心技术规格对比矩阵¶
| 技术规格维度 | Claude Code | Codex |
|---|---|---|
| 会话模型定义 (Session Modeling) | Transcript-centric:会话是一份持续追加、可恢复的转录历史记录(JSONL),按父子关系重建主线。 | Thread-Turn-Item:Thread 承载长期会话,Turn 承载单次用户任务,Item 承载消息、命令、补丁、审批等结构化事件。 |
| 执行引擎模型 (Event Loop) | 查询循环(query loop):本地循环反复执行“准备上下文 → 采样 → 执行工具 → 回填结果”,工具调用与执行在同一进程内同步等待。 | 操作队列会话循环:用户输入、中断、审批响应等操作统一提交到会话入口,按顺序串行处理,避免并发状态错位;工具执行本身走同步的进程级子进程派生。 |
| 上下文压缩策略 (Compaction) | 阈值触发自动压缩:摘要替换旧历史 + 保留工作记忆(详见下文 3.1) | 同样是摘要替换旧历史;长期记忆是独立于压缩的后台异步流程(详见下文 3.1) |
| 安全沙箱隔离 (Sandboxing) | 工具处理器可在本地沙箱中运行,限制文件系统写入范围、网络域名和敏感路径访问,具体隔离粒度由权限规则和沙箱配置共同决定(详见 claude-code-source/08-permissions-safety.md)。 | 进程级操作系统原生沙箱(非容器/虚拟化):macOS 用 Seatbelt(sandbox-exec),Linux 默认用 Bubblewrap + seccomp(Landlock 仅作历史遗留兜底,需显式开启),Windows 用受限令牌 + ACL,与执行策略(风险判断)、审批策略共同构成隔离执行环境(详见 project-breakdowns/openai-codex.md、codex-agent-source/08-permissions-safety.md)。 |
| 权限阻断逻辑 (ACL / Human-in-the-Loop) | 终端交互入口下同步确认;非交互入口无法自动批准时直接拒绝/失败(详见下文 3.2) | 审批请求进入操作队列,客户端异步响应写回(详见下文 3.2) |
| 持久化与分叉 (Restore & Fork) | 转录记录序列化为本地 JSONL 文件,支持 resume 时按父子关系重建主线,也支持从历史点分叉子转录。 | 持久化记录(rollout)保存会话元信息、模型响应条目、压缩替换历史和轮次上下文,支持恢复同一线程,也支持从历史点分叉出新线程。 |
3. 核心机制深度对比剖析¶
3.1 内存与上下文压缩机制 (Compaction Algorithm)¶
大模型的上下文窗口是有限的,智能体运行越久,历史 Token 堆积越严重。两者在“压缩”这件事上其实采用了同一种基本思路——用摘要替换旧历史,而不是做语义检索或知识图谱构建——差异主要在触发时机和长期记忆是否独立于压缩流程:
- Claude Code:当 token 接近上下文窗口阈值时,运行时触发自动压缩,构造一次摘要请求,让模型把旧对话压缩成新的摘要消息。压缩不是简单删除历史:它会运行压缩前后钩子,生成压缩边界标记,清理已读文件状态,并尽量重新附加计划、已调用技能、动态工具差异等仍有价值的工作记忆;压缩后还会把最近读过且仍有价值的文件片段重新作为附件注入,避免模型只剩一段抽象摘要。长期记忆(CLAUDE.md、AutoMem/TeamMem/MEMORY.md 体系)是与压缩并列的另一层,靠显式文件维护,不是压缩过程的产物(详见 claude-code-source/05-context-memory.md)。
- Codex:压缩同样是用摘要替换旧历史,摘要需要保留用户目标、已执行命令、工具结果、修改过的文件和仍需遵守的约束。这里还要区分两种触发场景:手动压缩或采样前压缩会清除旧的上下文基线,让下一次普通轮次重新注入完整规则;轮次中触发的压缩不能中断当前任务,因此会把必要的初始上下文一并放入替换历史,并直接建立新的基线,而不是先清空再等下一轮补齐。长期记忆是完全独立的后台流程:从持久化记录中提炼稳定事实,经两阶段处理(抽取候选记忆 → 加写锁合并去重)后写入本地记忆文件(
~/.codex/memories),供后续会话读取时按需引用;它不在压缩阶段同步执行,也不涉及向量检索或信息熵计算(详见 codex-agent-source/05-context-memory.md)。
3.2 权限控制与风控屏障(Human-in-the-Loop)¶
智能体自动修改本地代码或执行 Shell 命令时,安全是决定能否上线的生命线:
- Claude Code 的权限屏障: 权限决策先看显式规则(allow/deny/ask),再看权限模式(default/acceptEdits/plan/bypassPermissions 等)、工具自身的权限检查和沙箱条件。终端交互入口下,当模型请求的动作落在“需要询问”的范围时,界面会挂起并弹出确认提示,等待用户在同一个终端里同步做出允许/拒绝的选择;非交互场景(SDK、一次性执行等无界面入口)没有界面可用时,策略不允许自动批准就直接拒绝或失败,而不是挂起等待。拒绝结果不会被吞掉,而是作为工具结果写回历史,供模型下一轮决策参考(详见 claude-code-source/08-permissions-safety.md)。
- Codex 的队列化审批屏障:
Codex 的会话是一个操作队列,所有外部输入(用户输入、中断、审批响应等)都提交到同一入口按顺序处理,避免多个入口同时修改共享状态。审批策略
AskForApproval实际只有四种取值:UnlessTrusted(只有判定为只读安全的命令才自动放行)、OnRequest(默认值,由模型自己决定何时请求审批)、Granular(按沙箱/规则/技能等维度细分放行开关)、Never(从不询问用户,失败直接返回给模型)(详见 project-breakdowns/openai-codex.md 第56-67行核实结果)。需要确认时,审批请求进入等待,客户端异步响应后写回会话的操作队列,运行时据此继续执行或返回拒绝结果。部分场景下还会经过“守护复核”做自动判断,复核超时或异常一律按拒绝处理,不会成为绕过人工确认的隐式放行通道(详见 codex-agent-source/08-permissions-safety.md)。
4. 面试表达要点¶
面试提问:Claude Code 和 Codex 都支持多种接入方式(终端交互、SDK/协议客户端、一次性执行),并不是简单的“本地 vs 服务端”之分,那它们在会话状态建模和审批机制设计上,究竟有什么本质不同?
回答要点:
先纠正一个常见的误判:不能用“终端交互 vs 服务端异步”来区分两者——Claude Code 本身就有 SDK 调用、服务端桥接等非终端入口,Codex 的 app-server 在 TUI 和 codex exec 场景下也只是同进程内的协议层,不是独立部署的远端服务。两者真正的差异在于会话状态怎么建模、审批怎么设计:
Claude Code:状态机是线性的,围绕 Transcript(持续追加的转录记录)展开,工具意图直接在本地进程内执行并同步等待回填。权限决策先看显式规则,再看权限模式,终端交互入口下用同步确认弹窗,非交互入口(SDK、一次性执行)没有界面时策略不允许自动批准就直接拒绝或失败,而不是挂起等待。
Codex:引入 Thread-Turn-Item 三层结构化状态模型,所有会话级操作(用户输入、中断、审批响应)都进入同一个操作队列串行处理,避免并发状态错位;工具执行链路本身是同步的进程级子进程派生,要经过权限配置、审批策略(AskForApproval 的 UnlessTrusted/OnRequest/Granular/Never 四态)、执行策略(命令风险判断)和沙箱(macOS Seatbelt、Linux Bubblewrap+seccomp 等操作系统原生隔离)多层判断;审批请求被建模为可等待、可异步响应写回队列的事件;持久化记录支持从任意历史点恢复或分叉。
两者的差异根源不是“部署位置”,而是状态机粒度(线性转录 vs 结构化 Thread-Turn-Item)和多路输入的调度方式(本地入口直接处理 vs 统一操作队列排序处理)——这是评估任何 Agent 运行时设计时更值得关注的维度。
需要强调的是,两者的设计取舍不能简单归因为“本地单用户”与“服务端多方协作”的部署差异——因为两者都支持终端交互之外的多种入口。真正的取舍在于:Transcript 线性建模换来的是实现简单、状态可读性直观;Thread-Turn-Item 结构化建模换来的是跨客户端协议一致性和更细粒度的事件级恢复/分叉能力,代价是状态机复杂度更高。审批机制上,同步确认弹窗换来交互即时性,队列化异步审批换来多入口并发下的顺序一致性。两者是针对不同工程目标的合理选择,不是谁更先进,也不是“本地 vs 服务端”能概括的差异。