
长程任务 Agent 的工程实现机制:中断续跑、记忆分层、长上下文不丢失与分布式优雅升级
------ 基于 Claude Code / Codex / opencode / OpenClaw 源码与公开资料的深度研究
DeepThink Research · 2026-07-19
DeepThink 是你的私有AI 操作系统 (AI Agent Platform),在安全隔离的沙箱环境中,自主执行代码、管理文件、完成超复杂长程任务。自托管的多用户本地 AI Agent Loop Engineering 系统 (支持桌面端+浏览器+移动端) ------ 让 DeepThink 成为你的全能数字助手。 ------ Powered By AI Genius Institute & 光剑AI

DeepThink 项目开源代码: Gitcode: gitcode.com/AIGeniusIns... Github: github.com/AIGeniusIns...
摘要
长程任务 Agent(Long-Horizon Task Agent)是指那些需要执行数百乃至数千个步骤、跨越数小时乃至数天、期间经历多次中断/恢复/升级仍能保持目标连贯性的自主智能体。以 Claude Code、OpenAI Codex CLI、opencode、OpenClaw 为代表的新一代 AI Coding Agent,已经从"单轮对话工具"演化为"带状态、可恢复、可演化、可集群化"的常驻工程系统。本文基于对四个项目源码的逐文件分析(~/claude-coder、~/codex、~/opencode、~/openclaw),结合 Letta/MemGPT 等学术成果与 Anthropic 工程博客,系统拆解其四个核心工程机制:① 任务状态中断续跑 、② 记忆分层管理 、③ 长上下文不丢失 、④ 分布式集群部署与优雅升级,并提炼出可复用的设计范式与对自研 Agent 平台(如 DeepThink)的启示。
关键词:Long-Horizon Agent、Context Engineering、Session Persistence、Compaction、Subagent Isolation、Stateful Agent、Graceful Upgrade
1. 引言:为什么长程任务 Agent 是一道工程难题

一个能跑 5000 步的 Agent,与一个只能跑 50 步的 Agent,中间隔的不是"更大的上下文窗口",而是一整套状态管理基础设施。核心矛盾有四个:
- 中断不可避免:进程崩溃、网络中断、用户主动暂停、配额耗尽、CI 超时------任何一种都会让"内存里的对话"瞬间蒸发。如果不能续跑,前面 4900 步全部白费。
- 上下文窗口物理有限:即便 200K/1M token 窗口,几千步的工具调用结果(读文件、grep、测试输出)也会迅速撑爆。无限堆窗口是死路。
- 记忆不能扁平化:所有信息一锅塞进 system prompt,既不可扩展也难以维护。记忆必须有层级、有生命周期、有写入策略。
- 生产化要能升级:一个跑在用户机器/集群上的常驻进程,必须能在不丢会话状态的前提下完成版本升级、配置变更、横向扩容。
本文围绕这四个矛盾,逐一拆解四个主流实现的真实源码机制。所有引用均给出 文件:行号 或公开链接,确保可复现。
2. 维度一:任务状态中断续跑
2.1 核心思想:把"对话"当作"事务日志"
四个项目不约而同地采用了同一范式------会话状态以 append-only 日志形式落盘,恢复即回放。这与数据库的 WAL(Write-Ahead Log)思想同源:每一条消息/事件在产生时即持久化,进程崩溃后从日志重放重建内存状态。
2.2 Claude Code:JSONL 事务日志式会话
Claude Code 将会话以 JSONL 文件 逐条消息落盘,存储于 ~/.claude/projects/<project-hash>/。每条消息(user / assistant / tool_call / tool_result)独立成行,构成天然的事务日志式 checkpoint。
claude --continue/claude -c:自动恢复当前目录最近一次会话,重放 JSONL 回填上下文。claude --resume/claude -r:交互式列表选择历史会话恢复。- 恢复时若上下文已膨胀,会先走 compaction 再续跑,避免溢出。
在 ~/claude-coder 的 Rust 实现中,session.rs 给出了双格式持久化与增量写入的具体机制:
- 双格式 :JSON 全量快照(
to_json/from_json,原子写并 rotate)与 JSONL 流式 append(save_to_path/load_from_path自动嗅探格式)------session.rs:231-277、session.rs:360-411、session.rs:485-591。 - 增量写 :
push_message同时调用append_persisted_message追加到 JSONL,失败则回滚 ------session.rs:279-293。这保证了"每条消息要么完整落盘,要么不存在",无中间态。 - 回放即 resume :
load_from_path把session_meta/message/compaction/prompt_history记录重建为内存Session,下一轮run_turn直接把messagesclone 进请求 ------session.rs:515-591。 - 压缩历史记录 :
SessionCompaction { count, removed_message_count, summary }让多次压缩不丢元信息 ------session.rs:62-70、session.rs:328-336。 - Fork :
fork_session/Session::fork克隆 messages 与 compaction、生成新 session_id 与SessionFork { parent_session_id, branch_name }------conversation.rs:562-564、session.rs:339-358。这是"从某点分叉出新会话、不污染原会话"的基础。
2.3 Codex CLI:rollout + SQLite 双写 + 反向扫描重建
Codex(~/codex/codex-rs/)把这一范式做到了工业级:
持久化层:
rollout/src/lib.rs:24-25--- 会话文件存放于~/.codex/sessions/rollout-<timestamp>-<UUID>.jsonl,归档于archived_sessions/。rollout/src/recorder.rs:84---RolloutRecorder是 JSONL 追加写入器,内部 spawnRolloutWriterTask(:132)异步处理写入队列,失败可重试不丢条目。RolloutRecorderParams::resume(path)(:258)从已有 rollout 文件续写。rollout/src/metadata.rs:153-220---backfill_sessions扫描sessions与archived_sessions,回填进state_db(SQLite),支持分页/过滤/排序。这是"日志 + 索引库"双写:JSONL 是事实源,SQLite 是查询加速层。
恢复层 :Codex 的 resume 不是简单重放整条日志,而是从最新 checkpoint 反向扫描、按需重建:
core/src/session/rollout_reconstruction.rs:113---Session::reconstruct_history_from_rollout是核心重放函数。算法从最新向最旧扫描(rollout_items.iter().enumerate().rev(),:154),逐轮累积ActiveReplaySegment,直到同时拿到"存活的 replacement-history checkpoint"与"resume 元数据"即提前停止(:286-294)------不必读完整条日志,O(尾部) 而非 O(全长)。- 它提取的重建要素包括:最新存活的
Compacted.replacement_history(作为基线,:181-186)、窗口 ID、最近TurnContext(模型/comp_hash)、reference_context_item、world_state_replay(按时间序应用全量快照与 merge-patch,:389-422)、回滚标记(ThreadRolledBack,:188-191)。 - 之后正向物化:把
rollout_suffix(checkpoint 之后的条目)灌入新的ContextManager,遇到Compacted调history.replace,遇到回滚标记调history.drop_last_n_user_turns------:325-373。 - 调用方:
session/mod.rs:1263record_initial_history处理InitialHistory::Resumed,:1303处理InitialHistory::Forked,handlers.rs:536处理 live fork/resume。
命令层:
cli/src/main.rs:180,192---codex resume/codex fork子命令;--last选最近一条。tui/src/resume_picker.rs:99,387---SessionPickerAction::Resume/Fork,通过 app-server 查询 thread-store 获取可恢复会话列表。core/src/agent/control/spawn.rs:720---resume_single_agent_from_rollout:读history.items,封装为InitialHistory::Resumed(ResumedHistory{...})(:746,含rollout_path),再走resume_thread_with_history_with_source把历史重放进新会话。core/src/codex_thread.rs:80---forked_from_thread_id记录 fork 来源;:223emit_thread_resume_lifecycle触发on_thread_resume扩展事件。Fork 本质:复制历史快照到新 thread_id 新 rollout,不污染原会话。
resume 与 fork 的严格分离 (Rust port 的关键设计点):resume 复用同一 conversation_id、通过 RolloutRecorderParams::Resume { path } 重开原 rollout 文件以 append 模式续写(recorder.rs:1802-1819 open_rollout_for_append,ensure_rollout_is_newline_terminated 修复缺失尾换行,ordinal_state_for_rollout 扫描算下一个 ordinal);fork 则总是走 RolloutRecorderParams::Create { forked_from_id, ... } 生成全新 ThreadId + 全新 rollout 文件,源历史以 InitialHistory::Forked 拷贝(thread_manager.rs:1017-1092 fork_thread_with_initial_history,fork_history_from_snapshot 注入 InterruptedTurnHistoryMarker 标分支点)。甚至 JSON-RPC thread/resume 带 inline history 字段时也走 fork 式路径(resume_thread_from_history 返回 InitialHistory::Forked,thread_processor.rs:3439-3445),只有按 id/path resume 才返回 InitialHistory::Resumed 保留同 conversation_id。rollout 文件布局为 ~/.codex/sessions/YYYY/MM/DD/rollout-<date>-<conversation_id>.jsonl(recorder.rs:1505-1535),冷文件由后台 worker 压缩为 .jsonl.zst(compression.rs:29 spawn_rollout_compression_worker),append 前透明解压回 .jsonl。
持久化策略(什么写什么不写) :rollout/src/policy.rs:9 is_persisted_rollout_item 决定耐久性。所有 ResponseItem 持久化(除 AdditionalTools/CompactionTrigger/Other);Compacted/TurnContext/WorldState/SessionMeta 永远持久化;事件按 should_persist_event_msg 过滤------TurnStarted/TurnComplete/ThreadRolledBack/TokenCount 永久,UserMessage/AgentMessage 仅在 Legacy 模式,瞬态 delta/error 不写。
2.4 opencode:V2Session + Effect 框架 + 三段式 revert
opencode(~/opencode,TypeScript/Bun)用 Effect 函数式框架重新实现了同样的语义,并把"会话操作"显式建模为一个操作集合。其持久化是 SQLite + 事件溯源双轨:
packages/core/src/database/database.ts:43-57--- DB 解析为~/.local/share/opencode/opencode-{channel}.db(WAL 模式,busy_timeout=5000,foreign_keys=ON,synchronous=NORMAL),通道(latest/beta/prod)隔离 DB 文件可并存。- 表结构(
packages/core/src/session/sql.ts:32-130):SessionTable(会话元数据)、MessageTable/PartTable(V1 消息/part,JSON blob)、SessionMessageTable(V2 事件流,seq单调递增 +(session_id,seq)唯一索引)、SessionInputTable(待消费用户输入队列)、SessionContextEpochTable(系统上下文快照基线)。 - V2 是事件溯源 架构:每次变更都是
SessionMessageTable中一行(type区分 user/assistant/tool/compaction/system/synthetic/shell)。SessionHistory.load(packages/core/src/session/history.ts:54-101)从最新compactionevent seq 起回放,跳过已被压缩的历史------这就是 resume 原语,与 Codex 反向扫描同构。
packages/core/src/session.ts 定义 V2Session 服务,暴露 create/get/list/messages/message/context/history/prompt/shell/skill/switchAgent/switchModel/compact/wait/resume/interrupt/revert 等操作(:208-449)。其中:
resume(sessionID)(:426)---yield* execution.resume(sessionID),唤醒挂起的执行。prompt(:360)---if (input.resume !== false) yield* execution.wake(admitted.sessionID)(:382),即提交 prompt 时可带 resume 语义续跑。interrupt(sessionID)(:430)--- 中断运行中的会话(保留状态)。revert.stage/clear/commit(:434-449)--- 三段式回滚 :stage暂存一个可回退点,clear丢弃暂存,commit提交。这是比单纯 fork 更细粒度的 checkpoint 原语,等价于"事务保存点"。compact(:417)--- 显式压缩操作。
并发 drain :SessionRunCoordinator(packages/core/src/session/run-coordinator.ts:24-104)是 per-key 串行执行器------run(key) 加入在飞 drain 或启动新的;wake(key) 合并后续唤醒;interrupt(key) 取消 owner fiber;settle(:51)处理 pendingWake 接力避免丢失续跑请求。接线在 session/execution/local.ts:16-37,注释明确写着 "Replace local ownership with durable multi-node ownership when clustered"------集群化是 roadmap,目前单进程 。实际 drain 循环在 SessionRunner.run(runner/llm.ts:358-410)先调 failInterruptedTools(:118-138)把上次崩溃遗留的 pending/running tool 调用标为 failed,再循环 runTurn。续跑即再次调用 resume(sessionID),内存 runner 重建,状态全在 SQLite。
snapshot/checkpoint :packages/opencode/src/snapshot/index.ts:71 用一个独立的 bare git 仓库 ~/.local/share/opencode/snapshot/{projectID}/{hash(worktree)},与用户工作树解耦。track() 写 commit、patch(hash) 出 diff、restore checkout、revert(patches) 反向。SessionRevert.revert(session/revert.ts:38-88)遍历 message/part 收集 patch,调 snap.revert,把 revert state 存到 session row 的 revert 字段------用户可见的"回滚到某步"。
会话状态在 server 端持久,client 崩溃不丢任务;详见第 5 节。
2.5 OpenClaw:复用 codex app-server 协议 + active-memory session 层
OpenClaw(~/openclaw)的策略是站在 codex 的肩膀上 :scripts/sync-codex-app-server-protocol.ts 会从 codex 生成 app-server 的 JSON-RPC 协议 schema,落到 extensions/codex/src/app-server/protocol-generated/。这意味着 OpenClaw 的会话/恢复语义直接复用 codex 的 rollout + thread-store 体系,而非另起炉灶。
在此之上,OpenClaw 加了一层 active-memory 扩展(extensions/active-memory/):
session.ts---resolveCanonicalSessionKeyFromSessionId(:12)把外部 sessionId 规约到 canonical sessionKey,跨 IM 渠道(iMessage/WhatsApp/Telegram)绑定同一逻辑会话;persistPluginStatusLines(:271)通过patchSessionEntry把 active-memory 的状态行持久化进 session entry(:293-328),崩溃后可恢复。session-policy.ts---api.runtime.state.openKeyedStore(:20)提供按 session 粒度的键值存储,承载会话级策略。
换言之,OpenClaw = codex 会话内核 + 多渠道会话绑定层 + 持久化插件状态。这是"协议复用 + 扩展点"的典型工程取舍。
2.6 小结与范式提炼
中断续跑的可复用范式:
| 要素 | 实现 |
|---|---|
| 事实源 | append-only 日志(JSONL rollout) |
| 加速层 | 索引库(SQLite state_db) |
| 恢复算法 | 从最新 checkpoint 反向扫描 + 正向物化,O(尾部) |
| 细粒度原语 | fork(分支不污染)/ revert stage-clear-commit(保存点) |
| 持久化策略 | 区分耐久/瞬态事件,瞬态 delta 不落盘 |
| 崩溃一致性 | 增量 append 失败回滚,无中间态 |
3. 维度二:记忆分层管理
3.1 为什么不能"一锅端"
如果把所有记忆(项目规范、用户偏好、历史决策、工具结果)都塞进 system prompt,会导致三个问题:① token 预算爆炸;② 记忆无生命周期(该忘的忘不掉);③ 修改记忆要重写整段。四个项目的共识是------记忆分层 + 按需注入 + 差分更新。
3.2 Claude Code:CLAUDE.md 三级金字塔 + auto-memory
Claude Code 的记忆金字塔(由高到低):
CLAUDE.md(项目级 + 用户级 + 全局级),随上下文每次注入顶部。~/.claude/CLAUDE.md用户私有偏好。- 子目录
CLAUDE.md按需加载(递归向上读取)。 - Auto-memory:近期版本自动将"用户偏好/项目事实"追加写入 CLAUDE.md,形成自进化记忆。
来源:Claude Code memory、settings。
3.3 Codex:AGENTS.md 层级发现 + world-state 差分注入
Codex(~/codex/codex-rs/)把记忆分层做成了严格的"发现 + 注入 + 差分"三段式:
发现(core/src/agents_md.rs):
- 算法(模块文档 :1-16):从 cwd 向上找
project_root(按project_root_markers,默认.git),绝不越过项目根 ;再从 root 向下到 cwd 收集每层AGENTS.md,按序拼接。 DEFAULT_AGENTS_MD_FILENAME = "AGENTS.md"(:38)、LOCAL_AGENTS_MD_FILENAME = "AGENTS.override.md"(:40,本地覆盖优先)。AGENTS_MD_SEPARATOR = "\n\n--- project-doc ---\n\n"(:44)------ 用户/内部指令与项目指令的分隔符。read_agents_md(:89)读取每个发现的文件,强制project_doc_max_bytes预算(:95),超限截断(:120-130),附InstructionProvenance::Project { source_path, environment_id, cwd }(:134-141)溯源。agents_md_paths(:155)合并非 Project 层读project_root_markers,调find_nearest_ancestor_with_markers(:180),构建 root→cwd 搜索目录列表(:188-205)。
全局用户层(codex-home crate):
CodexHomeUserInstructionsProvider(codex-home/src/instructions/mod.rs:14)实现UserInstructionsProvidertrait(:70)。load_from_codex_home(:24)按序扫描AGENTS.override.md→AGENTS.md(:9-10),返回UserInstructions { text, source }。在cli/src/main.rs:1973接入。
注入(关键设计:AGENTS.md 不进 base_instructions) : Codex 有两条独立注入通道------base_instructions(作为 Responses API instructions 参数一次性发送)与一组 contextual user/developer 消息(world-state 片段,AGENTS.md 走这条)。
core/src/session/mod.rs:609-613--- base_instructions 优先级:CLI/文件config.base_instructions→ resumed-threadBaseInstructions→ModelInfo.get_model_instructions(personality)(模型默认,protocol/src/openai_models.rs:476,模板BASE_INSTRUCTIONS_DEFAULT=protocol/src/models.rs:1255的include_str!("prompts/base_instructions/default.md"))。core/src/session/mod.rs:3399---for fragment in world_state.render_full()按role()分派:"developer"打包成 developer 消息,"user"打成 contextual user 消息。AGENTS.md 内容在此作为 user-role 消息 (包裹在# AGENTS.md instructions ... </INSTRUCTIONS>)注入,而非塞进 system prompt。
差分更新(避免每轮重发全部记忆):
core/src/context/world_state/mod.rs---render_full(:282,线程启动/压缩后全量渲染)、render_diff(:287,与持久化快照 diff)、render_history_diff(:298,稳态路径,只重发"变更或从历史中消失"的 section)。core/src/session/mod.rs:2801record_step_world_state_if_changed--- 每步渲染render_diff,记录结果项,持久化WorldStatemerge-patch rollout 项,存新基线。AGENTS.md 中途变更以 in-band user 消息送达 (REPLACEMENT_NOTICE/REMOVAL_NOTICE,world_state/agents_md.rs:9-11),而非重写 base_instructions。agents_md_manager.rs:11---AgentsMdManager按TurnEnvironmentSelection缓存LoadedAgentsMd,refresh选择未变时短路(:32)。
Config 分层(config/src/config_layer_source.rs) :precedence()(:31)定义 Mdm(0) → System(10) → EnterpriseManaged(15) → User(20/21) → Project(25) → SessionFlags(30) → Legacy(40/50)。loader/mod.rs:116 按磁盘层序加载(admin→system→cloud→user→profile→cwd→tree→repo→runtime),不可信的 project/repo/cwd 层加载但禁用 ;PROJECT_LOCAL_CONFIG_DENYLIST(:62)阻止项目级配置设凭据/model_provider/notify 等敏感键。merge.rs:7 merge_toml_values 递归合并含键别名归一化。
3.4 Letta:学术级的显式分层
Letta(前身 MemGPT,arxiv 2310.08560 / 2502.10428)把分层做到了理论清晰:
- Core memory(persona block + human block):每次 LLM 调用强制注入顶部,容量小而精。
- Episodic memory:近期事件日志,运行时加载、周期性截断压缩。
- Archival memory :长期存储(向量库),仅 agent 主动调
archival_memory_search时按需检索。 - Memory stream:后台异步进程,按 importance score 给新事件打分路由到不同层。
- Self-editing memory :agent 用
core_memory_replace/core_memory_append工具自编辑记忆。 - Sleep-time computation:空闲期归档旧记忆、压缩 episodic、更新 core------"睡眠整理记忆"。
来源:arxiv 2502.10428、Letta memory hierarchy、sleep-time。
3.5 范式提炼:记忆的三轴分层

四个项目的共识:记忆不进 system prompt 主体,而以 contextual 消息注入 + 差分更新 ;记忆有生命周期 (永久/近期/归档);记忆可被 agent 自编辑。
4. 维度三:长上下文管理------几千步不丢失
这是最硬核的一节。"几千步骤上下文仍然能够不丢"靠的不是窗口大小,而是多层级的上下文精炼 + subagent 隔离 + 可回放压缩。
4.1 压缩的三条路由(Codex compact.rs 家族)
Codex 把"压缩"实现了三种可路由的策略,选择发生在 core/src/session/turn.rs:980 run_auto_compact:
- Token-budget 变体 (
compact_token_budget.rs:49)------ 跳过模型摘要 ,直接start_new_context_window安装全新窗口。最快、最省 token,代价是丢弃对话连续性,适合"窗口已满、开新篇"。 - 本地 Responses 摘要 (
compact.rs:92/:124/:221)------ 用SUMMARIZATION_PROMPT流式让模型生成结构化 summary(scope/tools/recent user requests/pending work/key files/key timeline),再build_compacted_history组装替换历史。 - 远端摘要 v1/v2 (
compact_remote.rs/compact_remote_v2.rs)------ 服务端 summarization;v2 保留user/developer/system角色消息,受RETAINED_MESSAGE_TOKEN_BUDGET = 64_000(compact_remote_v2.rs:54)预算约束。
触发 :core/src/session/context_window.rs:23 context_window_token_status 是中央预算检查。token_limit_reached(:75-79)当 auto_compact_scope_tokens >= buffered_auto_compact_limit(scope limit + fallback buffer)或 active_context_tokens >= full_context_window_limit(模型硬上限)时为真。触发时机分三阶段:PreTurn(:836,采样前)、MidTurn(:368,采样中遇 ContextWindowExceeded)、StandaloneTurn(手动)。
保留什么/丢弃什么(本地路径):
- 保留 :token 预算内最近的真实 user 消息(最多
COMPACT_USER_MESSAGE_MAX_TOKENS = 20_000,newest-biased)、新生成的 summary、重注入的 canonical initial context。system prompt 经base_instructions单独提供,不在 replacement_history 内。 - 丢弃:所有 assistant 消息、reasoning、tool calls、tool outputs、旧 summary------全部压进新 summary。
- 边界保护 :若首条保留消息是 ToolResult,回退边界以保留配对的 assistant ToolUse,避免 OpenAI-compat 400 ------
compact.rs:124-166。 - 二次压缩 :
merge_compact_summaries把旧 summary highlights 平铺(非嵌套),防多次压缩膨胀 ------compact.rs:290-325;summary_compression.rs的compress_summary(预算 1200 字符/24 行/160 字符每行,按核心细节→段落头→bullet 优先级选行去重)------:128-156、:174-199。
关键:压缩是可回放的 。core/src/session/mod.rs:2983 replace_compacted_history 持久化 RolloutItem::Compacted(含 replacement_history: Some(items))+ 可选 WorldState 全量快照 + TurnContext reference item(:3012-3022)。恢复时 reconstruct_history_from_rollout 直接以最新 Compacted.replacement_history 为基线(§2.3),不必重放整条日志------压缩点即 checkpoint 点。
4.2 Claude Code:trident 三阶段精炼 + auto-compact 阈值

~/claude-coder 的 Rust 实现给出了更精细的"压缩前预处理"管线------trident (trident.rs:105-160,trident_compact_session),在标准 compact_session 之前跑三阶段:
- Stage 1 Supersede (:179-228):对同一文件的旧 Read/Write/Edit,在最后一次 Write/Edit 之后视为过时,零成本删除------工具调用结果的"垃圾回收"。
- Stage 2 Collapse (:299-379):连续 ≥4 条无 ToolUse/ToolResult 的小消息(<200 字符)合并为
[Collapsed Conversation]system 摘要------压缩对话气泡。 - Stage 3 Cluster (:426-510,:566-602):用 Jaccard 相似度(0.4 tool + 0.4 file + 0.2 length,阈值默认 0.6)聚类相似消息,每组替换为
[Clustered N messages]摘要------去重相似工作。
之后再走标准 compact_session(:153)。这套"先回收过时、再合并碎片、再聚类相似、最后摘要"的流水线,是"几千步不丢主线"的关键------它把 token 预算花在刀刃上。
阈值机制:
- token 估算
estimate_session_tokens/estimate_message_tokens按len()/4 + 1粗估(非真实 tokenizer)------compact.rs:35-37、:458-474。 - 手动压缩
CompactionConfig { preserve_recent_messages: 4, max_estimated_tokens: 10_000 }------compact.rs:15-22。 - 自动压缩阈值
DEFAULT_AUTO_COMPACTION_INPUT_TOKENS_THRESHOLD = 100_000input tokens(基于 provider 真实返回的usage_tracker累计,可由CLAUDE_CODE_AUTO_COMPACT_INPUT_TOKENS环境变量覆盖),运行时遇 400 错误可动态下调 ------conversation.rs:18-19、:571-576、:706-720、:210-212。 - 自动压缩用
max_estimated_tokens: 0最大化压缩 ------conversation.rs:578-584。
保留策略 :合成一条 system continuation 消息(summary + "Recent messages are preserved verbatim" + 续写指令)+ 最近 N 条原文 ------ compact.rs:71-92、:174-183。
4.3 Subagent 上下文隔离:把 token 消耗关进笼子

这是"几千步不丢主线"的最核心机制 。主 agent 调用 subagent,subagent 拥有独立、隔离的 context window,中间探索(读几十个文件、失败尝试)的 token 全部消耗在 subagent 内,主 agent 仅收到压缩后的 result。
Claude Code(~/claude-coder 的 Task 工具):
tools/src/lib.rs:4091-4093---execute_agent→execute_agent_with_spawn→spawn_agent_job,用std::thread::Builder::spawn起独立线程。lib.rs:4211-4231---build_agent_runtime传入全新Session::new(),不复用父 session------这是隔离的物理边界。- 三件套隔离边界:
- (a) 独立 system prompt(追加 "You are a background sub-agent... do not ask the user questions")------
lib.rs:4233-4247; - (b)
allowed_tools_for_subagent按subagent_type白名单裁剪工具集 ------lib.rs:4257-4336; - (c)
SubagentToolExecutor+PermissionEnforcer+PermissionMode::DangerFullAccess(仍受 tool requirement 约束)------lib.rs:4219-4223、:4338-4343。
- (a) 独立 system prompt(追加 "You are a background sub-agent... do not ask the user questions")------
DEFAULT_AGENT_MAX_ITERATIONS = 32------lib.rs:4089、:4203。- 结果回传不走 context,而走磁盘 manifest :写
output_file(.md) 与manifest_file(.json),终态由persist_agent_terminal_state落盘 ------lib.rs:4107-4138、:4157、:4355-4379。即父 agent 通过读文件而非膨胀 context 获取结果。
TaskPacket(结构化任务包) :task_packet.rs:29-67 定义 TaskPacket(objective/scope/scope_path/repo/worktree/branch_policy/acceptance_criteria/resources/model/provider/permission_profile/commit_policy/reporting/escalation/recovery/verification_plan),validate_packet(:115-191)校验后存入 task_registry,create_from_packet(task_registry.rs:133-144)生成 Task。
TaskRegistry 状态机 (task_registry.rs):
Arc<Mutex<RegistryInner { tasks: HashMap<String, Task>, counter }>>内存态 ------:105-114。- 状态机 Created→Running→Blocked/Completed/Failed/Stopped;
stop拒绝终态 ------:12-34、:249-269。 - 调度视图
lane_board_at按 active/blocked/finished 分桶,用LaneHeartbeat.freshness_at判定 Healthy/Stalled/TransportDead/Unknown(基于stalled_after_secs)------:67-78、:199-241。这是多 agent 协作的"看板"。 - 注意:registry 只登记状态,不调度执行;真正执行由 Task 工具的
spawn_agent_job线程 +worker_boot状态机驱动。
Codex 的 subagent fork :core/src/thread_manager.rs:735 spawn_subagent --- flush 父 rollout,读 stored thread,包成 InitialHistory::Forked(fork_history_from_snapshot(ForkSnapshot::Interrupted, ...),:758),再 start_thread_with_options_and_fork_source。每个 subagent 是独立 Session/thread + 独立 ContextManager + 独立 rollout 文件。agent/control/spawn.rs:554 支持 SpawnAgentForkMode::LastNTurns(n)(调 truncate_rollout_to_last_n_fork_turns,thread_rollout_truncation.rs:241)或 FullHistory。keep_forked_rollout_item(:590)剥离 multi-agent-v2 usage-hint 消息。
agent-graph-store crate (src/store.rs:17):trait AgentGraphStore 提供 upsert_thread_spawn_edge/set_thread_spawn_edge_status/list_thread_spawn_children/list_thread_spawn_descendants------纯拓扑持久化(父→子有向图),父线程可引用子线程结果而不必把全部 token 拉入父上下文。
opencode 的 subagent :packages/opencode/src/agent/agent.ts:38 --- mode: Schema.Literals(["subagent", "primary", "all"]),subagent 是一等公民的 agent 模式;subagent-permissions.ts 控制子代理权限;:193/:216 在调用处标记 mode: "subagent";:333 防止 default agent 被设为 subagent。
4.4 历史持久化与回放(claude-coder conversation.rs/session.rs)
- 会话循环:
run_turn每轮构造ApiRequest { system_prompt, messages: self.session.messages.clone() }整体发送,assistant 与 tool_result 依次push_message------conversation.rs:364-367、:403-405、:512-514。
4.5 范式提炼:长上下文不丢失的四层防线
核心洞察:"不丢失"不等于"全保留"。真正的机制是------把"重要信息"(决策、改动、pending work、key files)压进 summary 并 checkpoint 化,把"过程垃圾"(过时工具结果、相似探索、失败尝试)回收或隔离到 subagent,主线上下文永远精简。
5. 维度四:分布式集群部署与优雅升级
5.1 从单进程 CLI 到常驻 server 架构
四个项目呈现出清晰的"进程模型演进":Claude Code 单进程 CLI + 内嵌 Task 递归 → Codex 三层(TUI/app-server/core)→ opencode client/server 分离 + daemon → OpenClaw 复用 codex 协议 + 多渠道 + Sparkle 升级。
关于 Claude Code(~/claude-coder)分布式能力的真实状态 :源码审计显示,其"分布式/升级"更多停留在 schema 与状态机设计层 而非落地实现。rust/crates/runtime/src/remote.rs:41-211 定义了 UpstreamProxyBootstrap/UpstreamProxyState,读取 CLAUDE_CODE_REMOTE 等环境变量、计算 http://127.0.0.1:{port} 代理与 wss://.../v1/code/upstreamproxy/ws 远端 WebSocket URL,并能把代理环境变量注入子进程(subprocess_env,:161-182),但全仓无 TcpListener::bind 起这个本地代理 server ------只在 main.rs:14307 测试中出现。worker_boot.rs:8-12 自述"In-memory worker-boot state machine",WorkerRegistry::create(:307-347)只往 HashMap 插记录,靠字符串匹配屏幕文本推断状态,无 thread::spawn/Command::spawn。team_cron_registry.rs:135-230 的 CronRegistry 是 Arc<Mutex<HashMap>>,无调度线程、无 cron 表达式解析器。/upgrade slash 命令在 commands/src/lib.rs:303 注册但在 main.rs:8209-8234 落入空 stub。lane_events.rs 定义了 LaneEventName(lane.started/ready/blocked/red/green/...)事件 schema + reconcile_terminal_events 乱序调解,但无并发调度器。ROADMAP.md:474 把"mixed-version claw fleets"列为愿景。换言之,Claude Code 的分布式集群是"设计先行、实现待补",而 Codex/opencode/OpenClaw 已在 daemon 与热升级上落地------这正是本节聚焦后三者的原因。
5.2 Codex 的三层架构与 daemon 热升级
三层 :codex-rs/tui(终端 UI 客户端)↔ codex-rs/app-server(长驻服务进程)↔ codex-rs/core(引擎)。codex-rs/cli 仅路由分发。
IPC 多 transport (app-server-transport/src/transport/mod.rs:73-148):AppServerTransport 枚举 Stdio / UnixSocket{socket_path} / WebSocket / Off;DEFAULT_LISTEN_URL="stdio://"(:112)。unix_socket.rs:30 用 UnixListener::bind,:47 accept 循环。
daemon(app-server-daemon/src/lib.rs:31-35) :用 app-server.pid/app-server-updater.pid/daemon.lock 文件管进程;:78 探测现存 socket 获取版本;:232 start_pairing 走 daemon socket。
优雅升级(update_loop.rs:55-101)------这是工业级热升级的范例:
update_loop监听 SIGTERM,循环update_once:比对ExecutableIdentity,决定restart_mode/updater_refresh_mode,热重启 app-server。- :144
reexec_managed_updater用受管 codex 二进制重 exec 替换 updater。 - :160-191
fetch_standalone_updater走远端下载 standalone updater 二进制并 spawn。 managed_install.rs:38-98managed_codex_version调codex --version校验受管二进制版本;parse_codex_version解析输出。- 升级原子性:daemon 通过 pid 文件 +
daemon.lock保证原子替换,TUI 重连 Unix socket 即可------session 状态在 rollout/SQLite 落盘,进程替换不丢。
MCP 集成(rmcp-client/src/rmcp_client.rs:49-100) :三种 transport------DuplexStream(in-process)、StdioServerTransport(子进程 stdio)、StreamableHttpClientTransport(HTTP/SSE + OAuth)。core/src/mcp.rs:30-85 McpRegistry 汇总 config + 插件贡献的工具。codex-mcp crate 把 codex 自身作为 MCP server 对外暴露工具。
多会话并发 :thread-store/src/lib.rs:15-24 ThreadStore trait + LocalThreadStore(本地 SQLite+rollout)/InMemoryThreadStore(测试);live_thread::LiveThread 跟踪并发活跃线程。agent/control/spawn.rs reserve_spawn_slot 做并发槽位限额(agent_max_threads,:761)。
5.3 opencode 的 daemon + 版本匹配 health check
opencode 的进程模型与升级机制有一处需要澄清:它不是 JSON-RPC,而是 REST/HTTP + SSE ,且无进程内热重载。
packages/opencode/src/server/server.ts起一个 Node HTTP server,暴露 EffectHttpApi(OpenCodeHttpApi),事件流在/event+/global/event(public.ts:155)。端口 fallback:显式 0 时先试 4096 再 0(server.ts:110-114)。mDNS publish(mdns.ts)在非环回地址广播opencode-{port}.local,供 LAN 内 IDE/客户端发现。一个 workspace 一个 server 进程,多客户端(TUI/web/IDE)共用。- 进程模型:单 Bun/Node 进程/workspace instance;
InstanceStore管理实例生命周期;GlobalLifecycle.disposeAllInstancesAndEmitGlobalDisposed(server/global-lifecycle.ts)优雅下线;WebSocketTracker推送实时更新。优雅关闭:forceStop时server.closeAllConnections()。 - 升级机制 :
packages/opencode/src/installation/index.ts:265-307Installation.upgrade(method, target)分派到curl|sh(默认安装脚本)、npm/pnpm/yarn/bun install -g opencode-ai@target、brew upgrade、choco upgrade、scoop install------spawn 子进程跑安装器,再process.execPath --version验证,无进程内热重载 。getReleaseType(semver major/minor/patch)驱动 UX;通道 DB 命名让beta/latest共存。 - 并发 :
SessionRunCoordinator按 sessionID 串行化 drain(每会话同时仅一个在飞 turn),不同 session 并行;SQLite WAL +busy_timeout=5000处理并发写。 - 多设备同步 :
packages/opencode/src/sync/+handlers/sync.ts的replay端点接收序列化EventV2事件,经events.replayAll(payload, {ownerID, strictOwner: true})重放------这是多设备同步原语(事件日志复制)。但 README 明确 "Syncing with only one writer"------目前是单写者模型,多写者协同未完成。
回到 daemon 层(packages/cli/src/services/daemon.ts)::40 server.json 注册文件;:44-48 password 跨重启保持一个私有凭据让被发现的 client 复用(避免重启后 client 失联);:71 health check(Registered server is not healthy);:77 版本匹配校验 (Registered server version does not match the client,client 与 server 版本不一致时拒绝,强制升级对齐);:110 start spawn(..., "serve", "--register") 拉起 server;:155 stop 先认证再 signal PID(防误杀);:164 register。
opencode serve --stream-all-parts 把 LLM 每一个 part 流式广播给所有订阅 client,支持多端协同。来源:opencode serve。
5.4 OpenClaw:协议复用 + Sparkle + 容器化多平台
OpenClaw 在部署侧的取舍:
- 协议复用 :
scripts/sync-codex-app-server-protocol.ts从 codex 生成 app-server JSON-RPC schema 落到extensions/codex/src/app-server/protocol-generated/------不发明新协议,直接复用 codex 的稳定协议,降低维护成本与互操作摩擦。 - 多渠道会话 :
extensions/imessage、whatsapp、telegram等把外部 IM 渠道绑定到 canonical session(§2.5),实现"一个逻辑会话跨多个 IM"。 - 自动更新(macOS Sparkle) :
appcast.xml是 Sparkle RSS feed,含<sparkle:version>/<sparkle:shortVersionString>/<sparkle:minimumSystemVersion>。scripts/sparkle-build.ts:49sparkleBuildFloorsFromShortVersion把语义版本转成YYMMPPPPLL楼层号(:87 注释保持旧条目字节稳定后切换新格式)。Sparkle 框架自动检查 appcast、下载、原子替换------桌面端优雅升级的标准方案。 - 容器化与多平台 :
Dockerfile/docker-compose.yml/deploy/+linux/macos/android/ios/macos-mlx-tts目录------支持容器化集群部署与移动端。
5.5 范式提炼:优雅升级的四个不变量
来源共识:
- state 全落外部存储(JSONL/DB),进程无状态。
- client 与 server 解耦,server 滚动重启时 client 重连 resume(codex Unix socket、opencode daemon、openclaw app-server)。
- session 级幂等(基于 session_id + message_id 续跑,防重复执行)。
- worker 无状态化 + 调度器单一写入点(防同一文件并发写,OpenClaw/Cowork 类 scheduler→worker pool,worker 优雅 drain)。

6. 综合对比
| 维度 | Claude Code (~/claude-coder) |
Codex CLI (~/codex) |
opencode (~/opencode) |
OpenClaw (~/openclaw) |
|---|---|---|---|---|
| 中断续跑 | JSONL 事务日志 + --continue/--resume;Session::fork |
rollout JSONL + SQLite state_db;reconstruct_history_from_rollout 反向扫描;codex resume/fork |
V2Session resume/interrupt;revert.stage/clear/commit 三段式 |
复用 codex 协议 + active-memory persistPluginStatusLines |
| 记忆分层 | CLAUDE.md 三级 + auto-memory | AGENTS.md root→cwd 发现 + global ~/.codex/AGENTS.md;world-state diff 注入;Config 8 层 precedence |
AGENTS.md/CONTEXT.md + Effect 服务化 | active-memory 扩展 + keyedStore 会话策略 |
| 长上下文 | trident 三阶段精炼 + auto-compact 100k 阈值;Task subagent 独立 Session + 磁盘 manifest 回传 | compact 三路由(token-budget/local/remote);subagent fork LastNTurns/FullHistory;agent-graph-store 拓扑 | subagent 一等公民 mode;compact 操作 | 复用 codex 内核 |
| 分布式/升级 | 单进程 CLI + 内嵌 Task 递归;claude update |
TUI/app-server/core 三层;daemon update_loop + reexec_managed_updater + pid 原子替换;MCP 三 transport |
daemon + 版本匹配 health check + stream-all-parts 多端 |
协议复用 + Sparkle appcast + Docker/多平台 |
| 语言 | Rust | Rust (~90 crate) | TypeScript/Bun (Effect) | TypeScript (复用 codex) |
7. 设计原则提炼(对 DeepThink 的启示)
综合四个实现,长程任务 Agent 的可复用设计原则:
- 日志即状态(Log as State):会话状态以 append-only JSONL 为事实源,DB 仅索引加速。恢复从最新 checkpoint 反向扫描,O(尾部)。
- 记忆不进主 prompt:以 contextual 消息注入 + 差分更新(render_diff),有生命周期分层(core/episodic/archival),可自编辑。
- 压缩点即 checkpoint 点 :compaction 结果持久化为
Compacted.replacement_history,恢复时直接以此为基线------压缩与持久化合二为一。 - 先精炼后摘要:trident 式预处理(回收过时→合并碎片→聚类相似)再走 LLM 摘要,把 token 预算花在刀刃上。
- subagent 隔离 + 磁盘回传:独立 context window + 工具白名单 + 结果走 manifest 文件而非膨胀父 context------这是上千步不丢主线的物理保障。
- 进程无状态 + 协议有状态:worker 无状态化,session 状态全落盘;client/server 解耦,滚动升级时 client 重连。
- 升级靠原子替换 + 版本协商:pid 文件 + daemon.lock 原子替换(codex)/ Sparkle appcast(openclaw)/ 版本匹配 health check(opencode),核心是"状态在外、二进制可换"。
- 并发靠单一写入点 + 槽位限额 :
reserve_spawn_slot限并发,scheduler 持全局队列与并发锁防冲突。
对应到 DeepThink 的 Loop Engineering 范式(需求→PRD→技术方案→编码→测试→提交闭环),这套机制直接支撑"一个 Agent 独立完成软件研发全生命周期、持续学习、自我改进"的目标------会话状态可中断续跑保证长任务不丢失,记忆分层保证自进化,subagent 隔离保证多会话并发,优雅升级保证生产常驻。

8. 结论
长程任务 Agent 的工程实现,本质上是在有限的上下文窗口 与无限的任务长度之间建立一套可持久化、可压缩、可隔离、可升级的状态管理基础设施。Claude Code、Codex、opencode、OpenClaw 四个实现虽语言与侧重各异,却收敛到同一组范式:
- 中断续跑 = append-only 日志 + 反向扫描重建 + fork/保存点原语;
- 记忆分层 = contextual 注入 + 差分更新 + 生命周期分层;
- 长上下文不丢失 = 多层精炼 + 可回放压缩 + subagent 隔离;
- 分布式优雅升级 = 进程无状态化 + client/server 解耦 + 原子替换 + 版本协商。
这些不是某一家的"魔法",而是工程方法论的共识------也是 DeepThink 这类企业级自托管 Agent 平台走向"超级智能体孵化器"的必经之路。
参考文献
源码(本地)
~/claude-coder(Rust 实现):session.rs、conversation.rs、compact.rs、trident.rs、summary_compression.rs、tools/src/lib.rs、task_packet.rs、task_registry.rs、worker_boot.rs~/codex/codex-rs/(~90 crate):rollout/、core/src/agents_md.rs、core/src/compact*.rs、core/src/session/、core/src/context_manager/、core/src/context/world_state/、core/src/agent/control/spawn.rs、core/src/thread_manager.rs、core/src/thread_rollout_truncation.rs、app-server-daemon/、app-server-transport/、agent-graph-store/、thread-store/、config/、codex-home/~/opencode/packages/:core/src/session.ts、cli/src/services/daemon.ts、opencode/src/agent/~/openclaw/:scripts/sync-codex-app-server-protocol.ts、extensions/active-memory/session.ts、extensions/active-memory/session-policy.ts、appcast.xml、scripts/sparkle-build.ts
公开资料
- Claude Code 概览:docs.claude.com/en/docs/cla...
- Claude Code CLI(--continue/--resume):docs.claude.com/en/docs/cla...
- Claude Code memory / CLAUDE.md:docs.claude.com/en/docs/cla...
- Claude Code sub-agents:docs.claude.com/en/docs/cla...
- Claude Code 成本与 compaction:docs.claude.com/en/docs/cla...
- Anthropic Building Effective Agents:www.anthropic.com/research/bu...
- Anthropic Context Engineering:www.anthropic.com/engineering...
- Anthropic 多 agent 研究系统:www.anthropic.com/engineering...
- OpenAI Codex CLI:github.com/openai/code...
- opencode sessions:opencode.ai/docs/sessio...
- opencode serve / server:opencode.ai/docs/serve
- opencode stream-all-parts:opencode.ai/docs/serve#...
- opencode LSP:opencode.ai/docs/lsp
- Letta Stateful Agents 论文(arxiv 2502.10428):arxiv.org/abs/2502.10...
- Letta memory 概念:docs.letta.com/concepts/me...
- Letta memory hierarchy:docs.letta.com/guides/agen...
- Letta memory stream:docs.letta.com/guides/agen...
- Letta self-editing memory:docs.letta.com/guides/agen...
- Letta sleep-time:docs.letta.com/guides/agen...
- Letta archival memory:docs.letta.com/guides/agen...
- Letta blog:www.letta.com/blog
- MemGPT 原始论文(arxiv 2310.08560):arxiv.org/abs/2310.08...
本文由 DeepThink 基于本地源码逐文件分析与公开资料检索综合产出,所有源码引用均给出 文件:行号,可在本地仓库复现。