Back to Blog

OpenCode Session 管理:持久化、分支与 Undo

2026-04-27T10:30:00+08:00
OpenCode Session SQLite Undo 上下文压缩

OpenCode Session 管理:持久化、分支与 Undo

用 AI 编程助手最怕什么?不是它写错代码,而是聊了半小时的上下文,手一滑——没了。很多 CLI 工具关掉终端就什么都没了,那种痛,经历过的人都懂。

OpenCode 从设计之初就把 Session 持久化当成核心能力来做。每次对话都自动存入本地 SQLite 数据库,关机、断电、换终端,回来接着聊。不仅如此,它还支持 Session 分支、Undo/Redo、上下文自动压缩、对话分享——一套完整的 Session 生命周期管理。

这篇文章是我深入 OpenCode 文档和源码后的总结,把 Session 管理的方方面面都梳理清楚。

本文提纲

  1. SQLite 持久化:Session 存在哪里
  2. Session 创建与恢复
  3. Session 分支:安全探索的底气
  4. Undo 与 Redo:后悔药的正确吃法
  5. 上下文压缩:对话太长怎么办
  6. Session 共享:团队协作的桥梁
  7. Session 导出与导入
  8. Session 时间线与事件回放
  9. 实战技巧与配置建议

SQLite 持久化:Session 存在哪里

OpenCode 把所有 Session 数据存在本地 SQLite 数据库中。数据库文件位于项目目录下的 .opencode/state.db(路径可通过 OPENCODE_CONFIG_DIR 环境变量自定义)。

选择 SQLite 是个很务实的设计决策:

  • 零配置:不需要额外安装数据库服务,OpenCode 启动就能用
  • 单文件:整个数据库就是一个文件,备份、迁移、删除都很方便
  • 事务支持:Session 的写入、更新、Undo 操作都在事务里,不怕数据损坏
  • 性能够用:Session 数据量不大,SQLite 绰绰有余,读写都快

OpenCode 使用 Drizzle ORM 来操作 SQLite。Drizzle 是一个轻量级的 TypeScript ORM,类型安全、无运行时开销,和 OpenCode 的技术栈非常契合。Drizzle 的 schema 定义清晰,迁移也很方便,适合这种本地嵌入式场景。

数据表主要包含这几类:

  • Session 表:存储 Session 元数据(ID、标题、创建时间、父 Session 等)
  • Message 表:存储每条消息(用户消息、AI 回复)
  • Part 表:存储消息的各个部分(文本、工具调用、工具结果等)
  • Event 表:记录 Session 生命周期内的各种事件

这种分层设计让 OpenCode 能精确地追踪每一轮对话的完整上下文,也为 Undo、分支、压缩等高级功能打下了基础。

Session 创建与恢复

每次在 TUI 里按 ctrl+x n 或输入 /new,OpenCode 就创建一个新的 Session。每个 Session 有唯一 ID、标题(可以自动生成,也可以手动指定)、创建时间和完整的消息历史。

启动 OpenCode 时,有几种方式控制 Session 行为:

# 默认:继续上一次的 Session
opencode --continue

# 恢复指定 Session
opencode --session abc123

# 启动新 Session(默认行为)
opencode

在 TUI 里,用 ctrl+x l/sessions 命令可以打开 Session 列表,浏览所有历史对话,选中一个就能直接跳过去继续聊。/sessions 也支持别名 /resume/continue,记哪个都行。

用 CLI 脚本化操作也很方便:

# 列出所有 Session
opencode session list

# 以 JSON 格式输出,方便程序处理
opencode session list --format json

# 只看最近 5 个
opencode session list -n 5

OpenCode 还会通过 small_model(默认用便宜的 Haiku 模型)自动为每个 Session 生成标题,这样在 Session 列表里一眼就能找到你想要的对话。

Session 分支:安全探索的底气

这个功能我觉得是 OpenCode Session 管理里最精巧的设计。

想象一个场景:你正在和 AI 讨论一个复杂功能的实现,聊到一半想试一个不同的方案,但不想丢失当前的对话进度。传统做法是手动复制对话内容,或者开个新窗口重新描述上下文。

OpenCode 的 Session 分支(Fork)完美解决了这个问题。

# 继续上一次对话,但以分支方式——原 Session 不受影响
opencode --continue --fork

# 从指定 Session 分支
opencode --session abc123 --fork

Fork 出来的新 Session 会保留原 Session 的完整消息历史作为起点,但从那一刻起,两条路就分道扬镳了。你可以在分支里大胆尝试各种方案,搞砸了也不影响主线的对话。

在 SDK 层面,Session 有 parentID 字段来追踪分支关系,通过 session.children() API 可以列出某个 Session 的所有子分支。这构成了一棵 Session 树,非常适合管理复杂的探索式开发流程。

实际使用中,我经常这样做:

  1. 主 Session 保持稳定的方案讨论
  2. 遇到不确定的地方,Fork 一个分支去试
  3. 试通了,把结果贴回主 Session
  4. 试不通,直接删掉分支,回到主线

这种工作流让 AI 辅助编程变得更可控,不用担心"聊歪了"。

Undo 与 Redo:后悔药的正确吃法

AI 写的代码不满意怎么办?OpenCode 的 /undo 命令不仅能撤回消息,还能回滚文件变更。

# 撤回最后一轮对话(包括 AI 对文件做的修改)
/undo

# 撤回后反悔了,重做
/redo

Undo 的实现依赖两套机制协同工作:

消息层面:SQLite 数据库里记录了完整的消息链。/undo 会移除最近一条用户消息及其后所有 AI 回复。/redo 则把它们恢复回来。

文件层面:OpenCode 使用内部的 Git 快照系统追踪 Agent 对文件做的每一次修改。/undo 时,文件变更也会一并还原;/redo 时重新应用。这个快照系统默认开启,可以在配置里关掉:

{
  "snapshot": false
}

但说实话,我不建议关掉。没有 Snapshot,Undo 就只能撤回消息不能回滚代码,功能大打折扣。

几个实用技巧:

  • /undo 可以连续执行多次,一步步往回退
  • Undo 之后你可以修改 Prompt 重新提问,不用从头来
  • 快捷键 ctrl+x u(Undo)和 ctrl+x r(Redo)比输入命令快得多

有个前提条件值得注意:项目必须是 Git 仓库。OpenCode 的文件回滚依赖 Git,如果你的项目还没 git init,Undo 只能撤回消息,不能恢复文件。

上下文压缩:对话太长怎么办

LLM 的 Context Window 是有限的。一个 Session 聊久了,历史消息越来越长,最终会撑爆窗口。很多工具到这一步就直接报错或者截断,用户体验很差。

OpenCode 用 Compaction(上下文压缩)优雅地解决了这个问题。

当 Context Window 快满的时候,OpenCode 会自动触发压缩:

  1. 用 LLM 对当前 Session 的历史消息生成一份摘要
  2. 用摘要替代原始的冗长对话
  3. 保留最近几轮完整对话作为即时上下文
  4. 继续正常对话,用户几乎感知不到

这个行为可以通过配置精细控制:

{
  "compaction": {
    "auto": true,
    "prune": true,
    "reserved": 10000
  }
}
  • auto:是否自动压缩,默认 true。设为 false 后只在手动执行 /compact(快捷键 ctrl+x c)时才压缩
  • prune:压缩时是否移除旧的工具输出(Tool Result),默认 true。工具调用结果往往很长但参考价值递减,清掉能省大量 token
  • reserved:预留的 token 缓冲区大小,默认 10000。确保压缩过程本身不会导致上下文溢出

手动压缩也很有用。当你觉得对话变得冗长、AI 开始"忘记"前面的内容时,主动 /compact 一下往往能改善回复质量。

压缩对用户基本透明——你不会丢失任何重要信息,只是旧的对话被智能摘要替代了。如果你需要回顾原始对话,Session 时间线功能依然保留着完整的事件记录。

Session 共享:团队协作的桥梁

OpenCode 支持把 Session 生成公开链接分享给其他人。这在团队协作中非常有用——让同事看看你遇到的问题,或者向社区展示 AI 辅助编程的效果。

三种分享模式:

Manual(默认):需要主动执行 /share 命令才会分享。分享后生成类似 opncd.ai/s/<share-id> 的链接,自动复制到剪贴板。

{
  "share": "manual"
}

Auto:每次新对话自动分享,适合经常需要同步进度的场景。

{
  "share": "auto"
}

Disabled:完全禁用分享功能。对安全要求高的项目,可以在项目配置里设置并提交到 Git,确保团队成员都不能意外分享。

{
  "share": "disabled"
}

不再需要分享时,用 /unshare 移除链接和远程数据。也可以通过环境变量 OPENCODE_AUTO_SHARE 控制自动分享行为。

分享出去的对话包含完整的消息历史、所有 AI 回复和 Session 元数据,所以分享前最好检查一下是否包含敏感信息。企业版还支持 SSO 认证限制和自托管分享服务。

Session 导出与导入

OpenCode 提供了完整的 Session 导出导入功能,让你能备份、迁移和分享对话数据。

导出

# 交互式选择要导出的 Session
opencode export

# 导出指定 Session
opencode export abc123

导出的是 JSON 格式,包含完整的消息、工具调用、元数据等信息。在 TUI 里也可以用 /export(快捷键 ctrl+x x)导出当前对话为 Markdown 并在编辑器中打开。

导入

# 从本地文件导入
opencode import session.json

# 从分享链接导入
opencode import https://opncd.ai/s/abc123

从分享链接导入这个功能特别实用。同事给你发了一个 OpenCode 的分享链接,你可以直接导入到本地继续讨论,而不是从头开始描述上下文。

SDK 也提供了对应的 API:

// 列出所有 Session
const sessions = await client.session.list()

// 获取某个 Session 的详情
const session = await client.session.get({ path: { id: "abc123" } })

// 删除 Session
await client.session.delete({ path: { id: "abc123" } })

Session 时间线与事件回放

OpenCode 的 Session 不是一条简单的消息列表,而是一条完整的时间线。每个 Session 记录了从创建到当前状态的所有事件——消息发送、工具调用、文件变更、压缩操作、分支创建等。

通过 SDK 可以深度访问这些数据:

// 获取 Session 的所有消息
const messages = await client.session.messages({ path: { id: "abc123" } })

// 每条消息包含 info(元数据)和 parts(内容部分)
messages.forEach(msg => {
  console.log(msg.info.role, msg.parts)
})

// 获取某个 Session 的所有子分支
const children = await client.session.children({ path: { id: "abc123" } })

事件订阅则提供了实时监听能力:

const events = await client.event.subscribe()
for await (const event of events.stream) {
  console.log("Event:", event.type, event.properties)
}

这意味着你可以构建自己的 OpenCode 监控面板、日志分析工具或自动化工作流。事件流包含了所有 Session 的状态变更,非常适合用来做:

  • 实时监控:追踪多个并行 Session 的执行状态
  • 审计日志:记录 AI 做了什么操作,方便事后回溯
  • 自动化触发:某个 Session 完成后自动触发下一个流程

Session 的 revertunrevert API 也很有意思——对应 /undo/redo 的底层能力,你可以通过编程方式回滚和恢复 Session 状态:

// 回滚到某条消息之前
await client.session.revert({ path: { id: "abc123" }, body: { messageID: "msg456" } })

// 恢复回滚的内容
await client.session.unrevert({ path: { id: "abc123" } })

实战技巧与配置建议

用了一段时间后,我总结了几个让 Session 管理更好用的配置技巧。

合理配置 Compaction。如果你的项目比较大、工具调用多,建议把 reserved 调大一些:

{
  "compaction": {
    "auto": true,
    "prune": true,
    "reserved": 20000
  }
}

为不同项目设置不同的分享策略。内部项目可以 auto 分享方便协作,外部项目用 manualdisabled 保护隐私。在项目根目录的 opencode.json 里设置,提交到 Git 后团队成员自动继承。

利用 Session 标题快速定位opencode run 支持 --title 参数给 Session 起名字:

opencode run --title "refactor-auth" "重构认证模块"

这比自动生成的标题直观多了,在 Session 列表里一眼就能找到。

善用 CLI 的非交互模式opencode run 可以在脚本里调用,配合 --continue--fork 实现自动化工作流:

# 在已有 Session 上继续工作
opencode run --continue --format json "继续上次的重构"

# 从某个 Session 分支探索新方案
opencode run --session abc123 --fork "试试不同的数据库设计"

配合 SDK 做深度集成。如果你想在 OpenCode 之上构建自己的工具,SDK 提供了完整的 Session CRUD、消息发送、事件订阅等能力。比如你可以写一个 Slack Bot,把团队消息转发到 OpenCode Session 里,AI 的回复再发回 Slack。

OpenCode 的 Session 管理从持久化存储到分支探索,从上下文压缩到团队分享,形成了一套完整的对话生命周期管理方案。它解决了 AI 编程助手最大的痛点之一——对话状态的丢失和管理,让你能真正把 AI 当成一个持续协作的编程伙伴,而不是一次性的问答工具。


作者: itech001 来源: 公众号:AI人工智能时代 主页: https://www.theaiera.cn(每日分享最前沿的AI新闻和技术)

本文首发于 AI人工智能时代,转载请注明出处。

Enjoyed this article? Share it with others!