OpenCode Session 记忆管理机制分析
2026/4/27大约 6 分钟
OpenCode Session 记忆管理机制分析
概述
OpenCode 采用了一个基于 SQLite 的分层记忆管理架构,通过 session、message 和 part 三层结构来组织对话历史,并实现了自动压缩(compaction)和修剪(pruning)机制来管理长对话的上下文。
核心数据结构
1. Session(会话层)
数据库表: SessionTable (packages/opencode/src/session/session.sql.ts)
{
id: text().primaryKey(),
project_id: text().notNull(), // 关联到项目
parent_id: text(), // 支持 fork 子会话
slug: text().notNull(),
directory: text().notNull(),
title: text().notNull(),
version: text().notNull(),
share_url: text(),
// 摘要信息
summary_additions: integer(),
summary_deletions: integer(),
summary_files: integer(),
summary_diffs: json<Snapshot.FileDiff[]>(),
// 回退信息
revert: json<{ messageID, partID?, snapshot?, diff? }>(),
// 权限规则
permission: json<PermissionNext.Ruleset>(),
// 时间戳
time_created: integer(),
time_updated: integer(),
time_compacting: integer(),
time_archived: integer(),
}特点:
- 每个 session 属于一个 project
- 支持父子关系(通过
parent_id实现 fork) - 存储代码变更摘要(additions/deletions/files)
- 支持回退到历史状态
2. Message(消息层)
数据库表: MessageTable
{
id: text().primaryKey(),
session_id: text().notNull(), // 关联到 session
time_created: integer(),
time_updated: integer(),
data: json<InfoData>(), // 存储消息元数据
}消息类型:
User: 用户消息- 包含 agent、model、system prompt、tools 配置
- 可选的输出格式(text 或 json_schema)
- 摘要信息(title、body、diffs)
Assistant: 助手消息- 关联到父消息(
parentID) - 记录使用的 model 和 provider
- 统计 token 使用和成本
- 可能包含错误信息
- 标记是否为摘要消息(
summary: boolean)
- 关联到父消息(
3. Part(部分层)
数据库表: PartTable
{
id: text().primaryKey(),
message_id: text().notNull(), // 关联到 message
session_id: text().notNull(), // 冗余存储便于查询
time_created: integer(),
time_updated: integer(),
data: json<PartData>(), // 存储 part 内容
}Part 类型 (共 12 种):
text: 文本内容file: 文件附件(图片、PDF 等)tool: 工具调用及结果reasoning: 推理过程snapshot: 代码快照patch: 代码补丁agent: Agent 信息compaction: 压缩标记subtask: 子任务retry: 重试记录step-start/step-finish: 步骤边界
记忆管理机制
1. 自动压缩(Auto Compaction)
触发条件 (packages/opencode/src/session/compaction.ts):
async function isOverflow(input: {
tokens: MessageV2.Assistant["tokens"];
model: Provider.Model
}) {
const context = input.model.limit.context
const count = tokens.total || (input + output + cache.read + cache.write)
const reserved = config.compaction?.reserved ??
Math.min(20_000, maxOutputTokens(model))
const usable = model.limit.input
? model.limit.input - reserved
: context - maxOutputTokens(model)
return count >= usable // 接近上下文限制时触发
}压缩流程:
- 检测到 token 溢出时,创建一个特殊的
compactionpart - 启动一个新的 assistant 消息(
mode: "compaction",summary: true) - 使用专门的 "compaction" agent 生成摘要
- 摘要包含:
- 目标(Goal)
- 重要指令(Instructions)
- 发现(Discoveries)
- 已完成工作(Accomplished)
- 相关文件列表(Relevant files)
- 插件可以通过
experimental.session.compactinghook 自定义压缩逻辑
关键配置:
COMPACTION_BUFFER = 20_000: 预留的 token 缓冲区- 可通过
config.compaction.auto禁用自动压缩 - 可通过
config.compaction.reserved自定义预留空间
2. 工具调用修剪(Tool Call Pruning)
触发条件:
const PRUNE_MINIMUM = 20_000 // 最少修剪 20k tokens
const PRUNE_PROTECT = 40_000 // 保护最近 40k tokens 的工具调用修剪策略:
- 从最新消息向前扫描
- 保护最近 2 轮对话
- 保护最近 40k tokens 的工具调用
- 保护特定工具(如 "skill")
- 遇到已有摘要的消息时停止
- 将旧工具调用的输出标记为
compacted,显示为 "[Old tool result content cleared]"
实现:
async function prune(input: { sessionID: string }) {
// 向后扫描,累计 token 数
// 超过 PRUNE_PROTECT 的工具调用标记为待修剪
// 如果累计修剪量 > PRUNE_MINIMUM,则执行修剪
for (const part of toPrune) {
part.state.time.compacted = Date.now()
await Session.updatePart(part)
}
}3. 消息摘要(Message Summary)
Session 级别摘要:
- 统计整个 session 的代码变更
- 计算 additions、deletions、files 数量
- 存储完整的 diff 信息到
storage/session_diff/{sessionID}.json - 通过 snapshot 对比计算(
step-start到step-finish)
Message 级别摘要:
- 每个用户消息可以有自己的摘要
- 包含该消息及其对应 assistant 响应的 diff
- 用于快速预览单次交互的影响
跨 Session 的全局记忆管理
1. Project 层级
数据库表: ProjectTable (packages/opencode/src/project/project.sql.ts)
{
id: text().primaryKey(),
worktree: text().notNull(), // 工作目录
vcs: text(), // 版本控制系统
name: text(),
icon_url: text(),
icon_color: text(),
time_created: integer(),
time_updated: integer(),
time_initialized: integer(),
sandboxes: json<string[]>(),
commands: json<{ start?: string }>(),
}特点:
- 所有 session 通过
project_id关联到同一个 project - Project 是跨 session 共享状态的基础单元
- 支持多个 worktree(工作树)
2. 全局存储层
Storage 系统 (packages/opencode/src/storage/storage.ts):
- 基于文件系统的 JSON 存储
- 路径:
~/.opencode/data/storage/ - 支持读写锁(
Lock.read/Lock.write) - 用于存储跨 session 的数据(如 session_diff)
Database 系统 (packages/opencode/src/storage/db.ts):
- SQLite 数据库:
~/.opencode/data/opencode.db - WAL 模式,支持并发读写
- 外键约束确保数据一致性
- 级联删除(删除 session 自动删除关联的 message 和 part)
3. Session Fork 机制
实现 (packages/opencode/src/session/index.ts):
export const fork = fn(
z.object({
sessionID: Identifier.schema("session"),
messageID: Identifier.schema("message").optional(),
}),
async (input) => {
// 1. 复制原 session 的配置
const original = await get(input.sessionID)
const title = getForkedTitle(original.title) // "xxx (fork #1)"
// 2. 创建新 session,设置 parentID
const session = await createNext({
directory: Instance.directory,
title,
})
// 3. 复制消息历史(可选截止到某个 messageID)
const msgs = await messages({ sessionID: input.sessionID })
for (const msg of msgs) {
if (input.messageID && msg.info.id >= input.messageID) break
// 复制 message 和 parts,生成新 ID
}
return session
},
)用途:
- 从某个历史点创建分支对话
- 保留原 session 的上下文
- 支持实验性探索而不影响主线
4. 权限系统(Permission)
PermissionTable:
{
project_id: text().primaryKey(),
time_created: integer(),
time_updated: integer(),
data: json<PermissionNext.Ruleset>(),
}特点:
- Project 级别的权限配置
- Session 可以覆盖 project 的权限
- 用于控制文件访问、工具使用等
没有的全局记忆机制
当前不支持的功能
跨 Session 的语义记忆
- 没有向量数据库或嵌入存储
- 无法自动检索相关历史对话
- 每个 session 是相对独立的
知识图谱
- 没有实体关系提取
- 没有跨 session 的知识积累
- 无法自动关联相关讨论
用户偏好学习
- 没有持久化的用户画像
- 没有跨 session 的风格学习
- 每次对话都是"新鲜"的
自动上下文注入
- 不会自动从其他 session 注入相关信息
- 需要手动 fork 或引用历史 session
可能的扩展方向
添加向量存储
- 为每个 message 生成 embedding
- 支持语义搜索历史对话
- 自动注入相关上下文
Project 级别的知识库
- 提取代码库的结构信息
- 记录常见问题和解决方案
- 跨 session 共享学习成果
Skill 系统扩展
- 当前有 skill 机制(
packages/opencode/src/skill/) - 可以扩展为跨 session 的可复用知识单元
- 当前有 skill 机制(
总结
OpenCode 的 session 记忆管理采用了分层存储 + 自动压缩的策略:
优点:
- 清晰的三层结构(session/message/part)
- 自动处理长对话的上下文溢出
- 支持 fork 和回退
- 高效的 SQLite 存储
局限:
- 缺乏跨 session 的语义记忆
- 没有自动的知识积累机制
- 每个 session 相对孤立
设计哲学:
- 以 project 为中心组织 session
- 通过压缩和修剪管理单个 session 的记忆
- 通过 fork 机制实现上下文复用
- 依赖显式的文件系统和代码快照而非隐式的语义记忆