第 10 章

线程管理

别让对话失控 —— fork、resume、compact 让每个任务各得其所

6 个核心命令 · 会话生命周期 · SDK 集成

一个线程走到底?

那些让你后悔"没早点分叉"的瞬间

它把上下文搞乱了

一个线程里既改了认证模块,又重构了数据库层。现在 Codex 满脑子都是数据库的上下文,认证修改的细节已经模糊了。上下文污染。

你不敢试危险方案

你想让 Codex 尝试一个激进的方案,但怕搞砸现有的进度。只能小心翼翼,或者手动备份整个目录。没有安全网。

对话越来越长,越来越笨

线程已经 50+ 轮对话了。每次请求都要重读全部历史,token 消耗爆炸,响应变慢,Codex 开始"忘记"早期的决策。上下文膨胀。

线程管理就是为这些场景设计的:用 fork 探索分支、用 resume 恢复中断的工作、用 compact 精简冗长的对话、用 archive 归档已完成的任务。让每个线程各司其职,而不是一个线程扛所有。

线程是什么?

一次对话 = 一个线程

线程(Thread)是 Codex 中一次独立对话的完整上下文。它包含了所有的消息、工具调用结果、文件变更历史。

物理存储

每个线程对应 ~/.codex/sessions/ 下的一个 JSONL 文件(按 YYYY/MM/DD/ 分目录),记录了完整的消息序列。

逻辑边界

每个线程有独立的上下文窗口。一个线程的对话不会影响另一个线程。fork 创建的新线程从当前状态分叉,之后各自独立演化。

# 会话文件存储结构
~/.codex/sessions/
├── 2026/
│   └── 07/
│       └── 04/
│           ├── session-abc123.jsonl    # 每个文件 = 一个线程
│           ├── session-def456.jsonl
│           └── session-ghi789.jsonl

关键术语

Thread · Turn · Rollout

Thread(线程)

一次完整对话。有唯一 thread_id(全局唯一、创建后不可变),包含所有历史消息。每个线程对应一个 JSONL 文件。

Turn(轮次)

一次"用户提问 → Codex 回答"的交互。一个线程包含多个 turn。fork 操作发生在 turn 之间——从某个 turn 的结束点分叉。

Rollout(执行轨迹)

Codex 内部记录的完整执行轨迹,包含工具调用、中间状态。rollout 是 fork/resume 操作的数据基础——app-server 通过 rollout 记录来恢复或复制线程状态。

官方文档: Slash Commands · Codex Manual

6 个命令掌控线程

TUI 内斜杠命令一览

命令做什么何时用
/new开始新线程切换到完全不同的任务
/fork从当前轮次分叉想尝试新方案但保留现状
/resume恢复已保存的线程继续之前中断的工作
/compact压缩当前对话上下文太长、响应变慢
/archive归档并退出任务完成,归档保存
/delete永久删除并退出废弃的实验线程
补充:/rename 可重命名当前线程,便于在 /resume 选择器中识别。/clear 清空终端并新建对话(比 /new 更彻底,会重置终端视图)。
注意:/archive 和 /delete 执行后会退出 Codex。/delete 不可撤销。

命令 vs SDK

两种使用方式

交互式(TUI 斜杠命令)

在 Codex TUI 中输入 /fork、/resume 等。适合人在回路的开发流程。

> /fork
> /resume
> /compact

CLI 子命令(终端)

在终端直接运行,不进入 TUI。适合脚本和自动化。

$ codex fork          # 打开会话选择器后 fork
$ codex resume --last  # 恢复最近的会话
$ codex archive <id>   # 按 ID 归档
$ codex unarchive <id> # 恢复归档

SDK(Python)

通过 codex-sdk-py 的 thread_start() / thread.run() / thread_resume() 管理会话。fork / archive 等操作需通过 CLI 子进程调用。

from codex_sdk import Codex
codex = Codex()
thread = codex.thread_start()

/fork:分叉实验

保留现状,探索可能

/fork 把当前线程完整复制到一个新线程。原线程不受影响,你在新线程里可以大胆尝试。

# 在 TUI 中
> /fork
✅ 已创建分叉 thread-xyz789
   原线程 thread-abc123 不受影响

# 或在终端
$ codex fork   # 打开会话选择器

何时 fork?

  • 方案选型:A 方案 vs B 方案,fork 后分别尝试
  • 危险操作前:fork 一个安全副本再动手
  • 并行任务:同一上下文分出多条线索

关键特性

  • fork 后的新线程有独立的 ID
  • 之后两线程各自独立,互不影响
  • fork 发生在当前 turn 的结束点
  • 原线程的 rollout 记录是 fork 的基础

/resume:恢复线程

从中断处继续

/resume 打开会话选择器,从历史线程中选一个恢复。原历史完整保留。

# 在 TUI 中
> /resume
# 弹出会话选择器:
  ┌─────────────────────────────────────┐
  │ fix-auth-bug     2026-07-04 14:30   │
  │ refactor-db      2026-07-04 11:15   │
  │ add-tests        2026-07-03 18:45   │
  └─────────────────────────────────────┘

# 或在终端直接恢复最近的会话
$ codex resume --last

resume vs fork 的区别

  • resume:继续已有线程,原历史不变
  • fork:基于当前线程创建新线程
  • resume 后继续追加 turn 到同一个线程

SessionStart hook

当通过 resume 恢复线程时,SessionStart hook 会触发,source 值为 "resume"。你可以用它加载上下文笔记。

/compact:压缩对话

释放上下文空间

/compact 将当前对话历史摘要化,用一份精炼总结替代冗长的早期消息,释放 token 预算。

# 对话变长后
> /compact
# Codex 会确认后执行摘要

compact 做了什么

  • 摘要替代早期消息(非删除,是压缩)
  • 保留关键决策和文件变更信息
  • 释放上下文窗口空间
  • 触发 PreCompact / PostCompact hooks

何时 compact?

  • 对话超过 30+ 轮
  • 响应明显变慢
  • token 用量告警
  • 早期细节已不重要
注意:compact 是有损压缩——细节可能丢失。compact 前如果有关键信息,先用 /copy 或手动记录。

/archive & /delete:归档与删除

完成后清理

/archive — 归档

归档当前线程并退出 Codex。归档后的线程变为只读、受保护状态,不会被 /resume 误选。

> /archive
# 归档后退出 Codex

# 恢复归档的会话
$ codex unarchive <id>

/delete — 永久删除

永久删除当前线程并退出 Codex。JSONL 文件被移除,不可恢复。

> /delete
⚠️ 此操作不可撤销!
# 确认后删除并退出
建议:完成的项目用 /archive(可恢复),废弃的实验用 /delete(彻底清理)。两者都会退出 CLI。

实战:fork 的工作流

同一上下文,探索两种方案

# 1. 当前线程:讨论认证模块重构
> 我想把 JWT 验证从中间件抽到独立服务

# 2. Codex 给出方案 A(拦截器模式)
> 好的,先记下这个方案

# 3. fork 一个分支探索方案 B
> /fork
✅ 已创建分叉 thread-def456

# 4. 在分叉中探索方案 B(代理模式)
> 如果用代理模式呢?

# 5. 对比两个线程的结果
#    thread-abc123: 方案 A 的完整讨论
#    thread-def456: 方案 B 的完整讨论
#    互不干扰,可随时 /resume 切换
fork 的价值:不是"重头来过",而是"从共识点分叉探索"。原线程保留已建立的上下文,分叉线程承担风险探索。

实战:resume 的工作流

中断后无缝恢复

# 昨天的工作中断了
$ codex resume --last

# 或通过 TUI 选择器
> /resume
  选择 "fix-auth-bug" 线程

# Codex 加载完整历史
# 你可以接着说:
> 继续昨天的,我们改到 password-validator.ts 的第 42 行

resume 恢复了什么

  • 完整的消息历史
  • 所有文件变更的上下文
  • Codex 对项目的理解
  • 之前的决策和理由

最佳实践

  • 有意义的线程名:用 /rename 起一个描述性名字
  • 一个线程一个任务:便于快速定位
  • 中断前 compact:如果对话很长

实战:compact 的工作流

长对话瘦身

# 线程已经很长了
> /status
# 查看 token 使用情况

# 执行压缩
> /compact
# Codex 确认后摘要历史

# 压缩后
> /status
# token 使用量下降,响应恢复流畅
compact 的代价:它是"有损压缩"。细节级信息(具体行号、中间试错过程)可能被摘要掉。如果需要精确历史,不要 compact——用 /fork 保留完整副本。

compact vs new

  • /compact:保留摘要,延续当前任务
  • /new:清空上下文,切换新任务
  • 同一任务对话太长 → compact
  • 换一个完全不同的任务 → new

用 SDK 管理线程

Python 自动化集成

通过 codex-sdk-py(或 codex-app-server SDK),你可以在 Python 脚本中启动和管理 Codex 线程。

from codex_sdk import Codex

codex = Codex()

# 启动新线程
thread = codex.thread_start()

# 发送 prompt 并获得响应
turn = thread.run("帮我检查 auth.ts 的安全问题")

# 打印结果
print(turn.messages)

# 之后可以用 thread_id 恢复
thread_id = thread.thread_id
# ... 存储 thread_id 供后续使用

恢复已有线程

# 用之前保存的 thread_id 恢复
thread = codex.thread_resume(thread_id)
turn = thread.run("继续修复刚才的问题")

fork / archive(CLI 子进程)

SDK 不直接支持 fork/archive/compact。需要通过 subprocess 调用 CLI:

import subprocess

# fork 最近会话
subprocess.run(["codex", "fork"])
# 归档指定会话
subprocess.run(["codex", "archive", thread_id])
SDK 参考: codex-sdk-py (PyPI)

SDK 生命周期示例

完整的线程管理工作流

from codex_sdk import Codex
import subprocess, json

codex = Codex()

# === 1. 启动线程做代码审查 ===
thread = codex.thread_start()
turn = thread.run("审查 src/ 目录的安全性,只读不改")
print(turn.messages)
thread_id = thread.thread_id

# === 2. fork 一个分支尝试修复(通过 CLI) ===
result = subprocess.run(
    ["codex", "fork"],
    capture_output=True, text=True
)
# fork 创建新线程,原审查线程不变

# === 3. 恢复原线程继续讨论 ===
thread = codex.thread_resume(thread_id)
turn = thread.run("详细解释第 3 个安全问题")

# === 4. 任务完成后归档(通过 CLI) ===
subprocess.run(["codex", "archive", thread_id])
注意:SDK 的 thread_start() / thread.run() / thread_resume() 是核心 API。fork / archive / compact / delete 等操作需要通过 CLI 子进程调用——SDK 层暂未封装这些命令。

会话文件结构

JSONL 中的线程数据

每个线程以 JSONL 文件存储在 ~/.codex/sessions/YYYY/MM/DD/ 下。每行是一条 JSON 消息。

{"type":"message","role":"user","content":"帮我重构 auth.ts"}
{"type":"message","role":"assistant","content":"好的,我来分析..."}
{"type":"tool_call","name":"read_file","input":{"path":"src/auth.ts"}}
{"type":"tool_result","output":"export function validateToken..."}
{"type":"message","role":"assistant","content":"我发现三个问题..."}

直接读取会话文件

JSONL 是纯文本,可以用任何工具读取:

# 读取会话历史
import json
with open(session_path) as f:
    for line in f:
        msg = json.loads(line)
        print(msg["type"], msg.get("role",""))

会话浏览方式

  • codex resume:TUI 选择器
  • 直接读取 JSONL 文件
  • 第三方工具解析 JSONL

与 Hooks 集成

在线程事件上触发自动化

SessionStart

当线程启动或通过 /resume 恢复时触发。source 字段区分 "startup"(新启动)和 "resume"(恢复)。

[[hooks.SessionStart]]
matcher = "startup|resume"

PreCompact / PostCompact

当执行 /compact 时触发。PreCompact 在压缩前(可注入提醒),PostCompact 在压缩后(可记录日志)。

注意:目前 10 种 Hook 事件中没有 SessionEnd。/archive 和 /delete 操作不触发 Hook。如果需要在归档时执行操作,用 SDK + CLI subprocess 包装。

实战:自动会话归档

用 Python 脚本管理线程

#!/usr/bin/env python3
"""自动归档 7 天前的会话"""
import subprocess, json, os
from pathlib import Path
from datetime import datetime, timedelta

sessions_dir = Path.home() / ".codex" / "sessions"
cutoff = datetime.now() - timedelta(days=7)

# 遍历会话目录
for session_file in sessions_dir.rglob("*.jsonl"):
    mtime = datetime.fromtimestamp(session_file.stat().st_mtime)
    if mtime < cutoff:
        session_id = session_file.stem.replace("session-", "")
        print(f"归档: {session_file.name} (最后修改: {mtime})")
        # 调用 CLI 归档
        subprocess.run(["codex", "archive", session_id])
注意:归档前请确认会话确实不再需要。codex archive 后可用 codex unarchive 恢复,但 /delete 不可撤销。

常见错误 1:fork 时机

不是所有时候都该 fork

该 fork 的时候

  • Codex 给出了方案,你想试另一种
  • 即将执行有风险的操作
  • 想并行探索多条路径

不该 fork 的时候

  • 任务才刚开始(直接 /new)
  • 只是想撤回最后一步(用 rollback)
  • 对话太长(先 /compact)
判断标准:"我想保留当前进度,同时试试别的" → fork。"我不需要当前进度了" → new 或 resume。

常见错误 2:过度 compact

压缩不是免费的

# ❌ 错误:每次对话都 compact
> 问一个问题
> /compact           # 没必要!
> 再问一个问题
> /compact           # 还是不必要!

# ✅ 正确:只在需要时 compact
> /status            # 先看 token 用量
# 如果还有充足空间,不需要 compact
# 接近上限时才执行

过度 compact 的后果

  • 关键细节被摘要掉
  • Codex"忘记"早期决策的上下文
  • 每次 compact 自身也消耗 token

常见错误 3:线程爆炸

创建太多不清理

# 查看 sessions 目录
$ ls ~/.codex/sessions/2026/07/*/
session-aaa111.jsonl   # 不知道是啥
session-bbb222.jsonl   # 好像是昨天的实验
session-ccc333.jsonl   # 已经没用了
session-ddd444.jsonl   # 又一个忘了的
...

解决方案

  • 及时归档:完成的任务立刻 /archive
  • 起好名字:用 /rename 标记任务名
  • 定期清理:废弃实验用 /delete
  • 脚本归档:用自动化脚本定期归档旧会话

命令速查表

完整参考

命令TUI 斜杠CLI 等价SDK
新建线程/newcodex(直接启动)thread_start()
分叉线程/forkcodex fork—(CLI subprocess)
恢复线程/resumecodex resumethread_resume()
压缩对话/compact——(CLI subprocess)
归档线程/archivecodex archive <id>—(CLI subprocess)
删除线程/delete——
重命名线程/rename——
查看状态/statuscodex status—
说明:SDK(codex-sdk-py)仅封装了核心的 thread_start / thread.run / thread_resume。fork / archive / compact / delete 等需通过 subprocess 调用 CLI。

线程管理最佳实践

核心原则

1. 一个线程一个任务

不要在一个线程里混合多个无关任务。任务切换时用 /new 或 /resume,保持上下文干净。

2. 关键决策前 fork

如果即将做出可能影响方向的决定,先 /fork 保留安全副本。

3. 对话太长就 compact

监控 /status 的 token 用量,接近上限时执行 /compact。但记住它是有损压缩。

4. 完成后归档

任务完成后及时 /archive,保持会话列表干净。废弃实验用 /delete。

5. 起有意义的名字

用 /rename 给线程起描述性名字(如 fix-auth-bug),方便 /resume 时快速识别。

6. compact 前保存关键信息

如果有特别重要的上下文(具体行号、关键约束),compact 前先用 /copy 或手动记录。

总结

掌控你的对话

今天学到了什么

  • 6 个核心命令:/new · /fork · /resume · /compact · /archive · /delete
  • 辅助命令:/rename · /status
  • 线程以 JSONL 文件存储在 ~/.codex/sessions/
  • SDK 支持 thread_start / thread.run / thread_resume
  • fork/archive 等通过 CLI subprocess 实现
  • Hooks:SessionStart(resume 时触发)+ PreCompact/PostCompact

核心心智模型

  • fork = 复制:保留现状,探索分支
  • resume = 恢复:继续中断的工作
  • compact = 压缩:有损释放空间
  • archive = 归档:可恢复地清理
  • delete = 删除:不可撤销地清理
下一步:第 11 章 MCP & Plugins —— 扩展 Codex 的工具生态。