opencode-memory-dream
v0.4.0
Published
Auto-memory and auto-dream plugin for OpenCode - persistent project knowledge with periodic consolidation
Maintainers
Readme
OpenCode Auto-Memory & Auto-Dream 插件文档
文档版本:v1.0 | 日期:2026-04-13 | 状态:初稿
目录
1. 需求背景
1.1 问题定义
AI 编程助手在长周期协作里会遇到一个根本问题:会话结束后,上下文容易断裂。用户曾明确说过的偏好、项目中的非代码事实、时间线信息、事故背景、外部参考资料,往往不会自然沉淀下来。下一次进入新会话时,模型即使“知道如何写代码”,也不一定还“记得为什么这么写”。
在实际工作中,最容易丢失的不是源码本身,而是这些无法从代码和 git 直接反推的长期上下文,例如:
- 用户是谁、负责什么、偏好怎样的回答方式
- 当前项目为什么要做、截止时间是什么、谁在推进什么事项
- 某次事故发生在哪一天、复盘何时完成、哪些外部文档最关键
1.2 目标
本插件的目标,是在 OpenCode 的插件体系上复刻一套可落地的持久化记忆系统,让模型具备以下能力:
- 在对话结束后自动提取值得长期保留的信息
- 在新一轮对话开始前,自动召回与当前问题最相关的记忆
- 定期对已有记忆做整理、去重、合并和过期清理
- 支持用户显式要求“记住某件事”或“忘掉某件事”
1.3 插件定位
当前包名为 opencode-memory-dream,运行时插件 ID 为 opencode-memory-plugin。它是一个 OpenCode server plugin,不提供独立 UI,而是通过 hook、后台子会话和工具定义,增强现有对话流。
2. 插件定位与能力概览
2.1 能力全景
插件不是单点工具,而是一套完整的“记忆生命周期”方案,包含四条主链路:
| 能力 | 触发方式 | 作用 |
|------|---------|------|
| 隐式信号捕获 | chat.message | 从新消息中提取时间、身份、职责、技术栈等 durable signal |
| 自动记忆提取 | session.idle | 在对话告一段落后创建子会话,把最近消息沉淀为持久化记忆文件 |
| 记忆召回增强 | experimental.chat.system.transform | 根据当前用户问题注入相关记忆和捕获信号,提高连续性 |
| 自动 / 手动 dream 整理 | session.idle / dream tool | 周期性整理记忆库,合并重复、清理陈旧、更新索引 |
除此之外,插件还提供四个显式工具:
remember:立即保存显式记忆forget:删除或更新已记忆内容dream:手动触发一次整理dream_config:查看、开启、关闭或切换自动 dream
2.2 适合保存什么
插件明确区分“值得记忆的长期上下文”和“应从代码中实时获取的信息”。
适合记忆的内容:
- 用户身份、职责、目标、偏好
- 项目背景、事故时间线、里程碑、外部事实
- 用户对回答方式和协作流程的反馈
- 外部系统或文档的引用线索
不适合记忆的内容:
- 代码结构、文件路径、实现细节
- git 历史、commit 事实
- 已经写进仓库的规范和约定
- 当前会话的临时任务状态
3. 核心架构
3.1 总体链路
用户新消息
↓
chat.message
├─ 记录 last query
└─ 抽取 captured signals(temporal / identity / responsibility / stack)
模型开始下一轮响应
↓
experimental.chat.system.transform
├─ 注入 memory-policy
├─ 注入 MEMORY.md 索引(可选)
├─ 注入与当前 query 相关的 curated memories
├─ 注入 captured signals 摘要
└─ 注入上一轮后台维护反馈
会话空闲
↓
session.idle
├─ 运行 auto-memory extraction
├─ 记录 dream 候选 session
└─ 满足门槛时触发 auto-dream consolidation3.2 关键设计原则
设计一:主代理负责对话,子会话负责整理
插件不会让主对话线程直接承担记忆整理逻辑,而是在空闲时创建受限子会话完成提取或 consolidation。这让主链路保持轻量,也便于把记忆操作限制在 .opencode/memory 目录内。
设计二:显式记忆与隐式记忆分层
- 显式记忆:只有当用户明确要求“记住/忘掉”时,才调用
remember/forget - 隐式记忆:依靠后台 auto-memory 自动沉淀 durable context
这种分层避免了模型把每一句用户输入都机械保存成长期记忆。
设计三:先捕获原始信号,再决定是否晋升为 curated memory
插件先把明显的 durable signal 写入 captured-signals.json,再在 auto-memory 阶段结合最近对话窗口和现有记忆清单,决定哪些内容值得晋升为正式 .md 记忆文件。这一层设计让系统既不容易漏掉高价值事实,也不会过早把低置信度内容写死。
4. Auto-Memory:自动记忆提取
4.1 触发机制
Auto-memory 在根会话收到 session.idle 事件后触发,只处理根会话,跳过所有子会话,避免背景任务再次触发背景任务。
触发后,插件会:
- 拉取当前会话消息
- 依据
.extraction-state.json找到尚未处理的新消息窗口 - 扫描现有记忆文件,生成 manifest
- 创建名为
Auto-Memory Extraction的子会话 - 用受限权限提示词驱动该子会话只在 memory 目录内读写
- 对比前后快照,识别新增或变更的记忆文件
4.2 核心能力
增量处理
插件不会每次都重扫完整会话,而是用 lastProcessedMessageID 维护游标,仅处理上次提取之后的新消息。
并发合并
若上一次 extraction 尚未结束,新触发不会并行再跑一份,而是把最新请求合并为 pending,待当前轮结束后再补跑一次,避免重叠写入。
主代理直写保护
如果最近消息中已经出现对 memory 目录的直接写入,auto-memory 会跳过本轮提取,避免后台子会话覆盖主代理刚完成的记忆修改。
降级兜底
当模型提取失败、超时或子会话异常时,插件会退回本地 fallback 逻辑,至少把时间线或系统概览这类高价值事实沉淀下来,而不是直接丢失本轮信息。
4.3 Prompt 约束
提取子会话使用的 prompt 明确要求:
- 仅允许在 memory 目录内
Read / Grep / Glob / Write / Edit - 不允许使用 Bash
- 只基于最近消息窗口做判断
- 优先更新已有记忆,避免制造重复文件
- 把相对时间转换为绝对日期
这使得 auto-memory 更像一个“受控资料整理器”,而不是无边界的通用代理。
4.4 捕获信号的角色
在正式 extraction 之前,插件会从新消息中尝试提取四类高价值信号:
| 类别 | 典型内容 |
|------|---------|
| temporal | 事故发生日期、复盘完成日期、带日期的项目事实 |
| identity | 用户姓名、身份、公司、角色 |
| responsibility | 用户负责的模块、维护范围、当前职责 |
| stack | 前后端、数据库、缓存、部署等技术栈线索 |
这些信号先作为原始记录保存,随后在 extraction 中被用作高置信度输入。
5. Auto-Dream:周期性记忆整理
5.1 目标
Auto-memory 负责“把新信息写进去”,Auto-dream 负责“让记忆库长期保持干净”。它解决的是另一个问题:随着使用时间增长,记忆文件会越来越多,可能重复、过时、互相矛盾,MEMORY.md 索引也可能变臃肿。
Dream 的职责是:
- 合并重复或主题重叠的记忆文件
- 用新信息覆盖旧事实
- 删除陈旧、无用或冲突条目
- 更新
MEMORY.md索引,控制规模
5.2 自动触发门槛
默认配置下,自动 dream 需要同时满足以下条件:
- 距离上一次 dream 已超过
24小时 - 期间累计了至少
5个 dream 候选根会话 - 当前不是另一个 dream 正在执行的状态
此外,插件还带有两个保护机制:
- 扫描节流:候选 session 扫描每
10分钟最多做一次 - best-effort 锁文件互斥:通过
.dream-lock结合 PID 与 stale-reclaim 语义,尽量避免正常运行中出现重叠 dream
5.3 执行方式(两层 consolidation pipeline)
满足门槛后,dream 不再直接进入一次性语义整理,而是采用两层流水线:
- 确定性规范化层(deterministic normalization)
- 语义 consolidation 层(semantic consolidation)
先跑确定性层,再根据剩余问题决定是否进入语义层。
5.4 确定性规范化层
这一层只处理结构性、可确定修复的问题,不依赖模型语义判断。当前覆盖:
- 索引结构清理:移除
MEMORY.md中失效链接、越界链接、重复链接等无效项 - frontmatter 规范化:为缺失或不完整的记忆文件补齐合法 frontmatter
- 精确重复折叠:对内容完全重复的 memory 文件执行去重并同步索引
如果在这一层已经完成全部可修复项,dream 会直接结束,不触发语义层。
5.5 语义 consolidation 层(按需触发)
当确定性层结束后仍存在高歧义、需要语义判断的问题(当前主要是 contradiction bundles / candidates)时,才会触发语义 consolidation:
- 汇总自上次 dream 以来的候选根会话
- 生成 session summary(而不是注入全部转录)
- 创建名为
Auto-Dream Consolidation的子会话 - 仅授予 memory 目录内的有限读写权限
- 让子会话基于记忆清单、异常摘要、候选摘要做保守整理
这里的 contradiction flow 不再只给模型一组松散的“左右文件冲突提示”,而是优先构造成 contradiction bundles。每个 bundle 会把同一主题下的冲突证据按结构化字段组织起来,让 semantic dream 在同一推理上下文中直接处理:
subjectattributecompeting current claimshistorical claimspreferred current value when locally inferable(仅在本地规则能够 infer 出更可信当前值时提供)
这意味着 dream 在进入语义层时,看到的是“哪个主体的哪个属性存在当前态冲突、哪些说法应视为历史、以及本地是否已经推断出一个更可能保留的当前值”,而不是只看到未分组的 contradiction candidate 列表。这样可以更稳定地区分“当前事实互相冲突”和“新事实覆盖旧历史事实”。
更重要的是,contradiction bundles 不再只是 reasoning context,而是 semantic edit targets。语义 dream 在执行时必须按 subject + attribute 粒度完成一次 current-state 决策,并对 memory 文件落实编辑结果:
- 基于同一
subject + attribute的 competing claims,选择一个 winning current-state value - 将其余 competing current-state claims 明确判定为 stale
- 将 stale current-state wording 从 memory 文件中删除,或改写为 historical wording
只有当 memory 文件里同一 subject + attribute 最终只剩一个 current-state value(其余表述已被移除或历史化)时,这轮 contradiction resolution 才算真正完成。
5.6 Dream 结果语义
dream 的完成态现在显式区分:
completed_changed:本次 consolidation 实际修改了一个或多个 memory 文件(返回changedFiles)completed_noop:本次 consolidation 成功执行,但无需修改任何文件completed_unresolved:语义 consolidation 已运行,但仍存在未解决的 contradiction bundles,不会再被误报为普通 noop
这使“执行成功但无改动”和“执行成功且已改动”在工具层可被稳定区分。
6. 检索增强与显式记忆工具
6.1 检索增强
在 experimental.chat.system.transform 阶段,插件会根据当前 query 做两类召回:
Curated memory 召回
从 .md 记忆文件中按分数选出最相关内容。打分策略是词法命中 + 语义词组扩展 + 类型提示 + 新鲜度加权的混合规则,不依赖向量库。
当前内置的语义词组重点覆盖:
- 技术栈 / 前后端 / 数据库 / 部署
- 事故 / 时间线 / 日期 / 复盘
- 偏好 / 风格 / 规则
- 名字 / 身份 / 角色 / 公司
这套规则已经覆盖中英文混合问法,测试里也验证了中文提问下的召回效果。
Captured signal 召回
如果 curated memory 还不够完整,插件会进一步补充 captured-signals.json 中与当前 query 相关的原始信号,避免“记忆文件只覆盖了一半答案”的情况。
6.2 显式工具
remember
当用户明确要求“记住某件事”时调用,直接生成标准 frontmatter 的记忆文件并同步更新 MEMORY.md。
支持的 memory type:
userfeedbackprojectreference
forget
当用户明确要求“忘掉某件事”时调用。它先用当前查询去做相关性匹配,再优先删除最准确命中的记忆文件,而不是暴力清空整个记忆库。
dream
允许用户手动触发一次整理,不依赖自动门槛。
dream_config
支持四种动作:
statusenabledisabletoggle
自动 dream 的开关状态保存在 .dream-settings.json,默认启用。
6.3 系统提示里的行为约束
插件会额外注入一段 memory-policy,明确要求模型:
- 只有在用户明确提出记忆管理需求时才调用
remember/forget - 只有在用户明确问及自动 dream 状态时才调用
dream_config - 不要因为用户随手提到一个事实,就立即显式保存
这使工具行为更可控,也减少误触发。
7. 存储结构与数据格式
7.1 存储根目录
插件的持久化目录默认位于:
<worktree-or-directory>/.opencode/memory其中根目录解析策略为:
- 如果当前是
global项目且directory不是根路径,则优先使用directory - 否则优先使用
worktree - 再次退化时使用
directory
这样可以兼容普通仓库会话和全局临时会话两种场景。
7.2 目录内容
典型结构如下:
.opencode/memory/
├── MEMORY.md
├── captured-signals.json
├── .dream-settings.json
├── .dream-lock
├── .dream-sessions.json
├── .extraction-state.json
├── user_*.md
├── feedback_*.md
├── project_*.md
└── reference_*.md7.3 记忆文件格式
每个正式记忆文件都使用 frontmatter:
---
name: release freeze
description: release freeze date and handling guidance
type: project
---
Release freeze starts on 2026-04-20.
Why: This date controls risky changes.
How to apply: Avoid planning invasive refactors after this point.MEMORY.md 则作为轻量索引,每条记录是一行链接:
- [Release freeze](project_release_freeze_xxxxxxxx.md) — release freeze date and handling guidance8. 安装与配置方式
8.1 安装方式
如果环境可以访问 npm,可以直接通过 OpenCode 的插件安装流程接入:
opencode plugin [email protected] -g也可以手动将它写入工作区的 .opencode/opencode.jsonc:
{
"plugin": [
"[email protected]"
]
}如果你在本地开发插件,也可以直接使用本地路径:
opencode plugin /absolute/path/to/opencode-memory-plugin -g -f8.2 离线安装包
如果用户处在内网、无法直接访问 npm,推荐使用插件自带的离线 bundle:
npm run bundle:offline该命令会在 release/ 下生成两份产物:
opencode-memory-dream-0.4.0-offline/opencode-memory-dream-0.4.0-offline.tar.gz
把目录或压缩包发给离线用户后,对方只需要解压并执行:
./install.sh或者手动安装:
opencode plugin /path/to/opencode-memory-dream-0.4.0-offline/plugin -g -f这个离线 bundle 已经包含插件运行时需要的依赖,适合无法同步内源、也无法直接 npm install 的环境。
8.3 带选项的配置
插件支持通过 tuple 传入 server plugin 选项:
{
"plugin": [
[
"opencode-memory-dream",
{
"dreamMinHours": 24,
"dreamMinSessions": 5,
"retrievalMaxFiles": 4,
"retrievalMinScore": 3,
"includeIndexFallback": true,
"dreamLockStaleMs": 3600000
}
]
]
}8.4 配置项说明
| 配置项 | 默认值 | 作用 |
|-------|-------|------|
| dreamMinHours | 24 | 两次自动 dream 之间的最小小时数 |
| dreamMinSessions | 5 | 触发自动 dream 所需的最少候选根会话数 |
| retrievalMaxFiles | 4 | 每轮最多注入多少份相关记忆 |
| retrievalMinScore | 3 | 记忆召回最低分阈值 |
| includeIndexFallback | true | 是否把 MEMORY.md 作为索引上下文一起注入 |
| dreamLockStaleMs | 3600000 | dream 锁过期时间,默认 1 小时 |
9. 当前边界与后续方向
9.1 当前边界
当前实现已经完整跑通,但也有明确边界:
- 检索仍是规则打分,不是向量检索
- 隐式信号捕获目前只覆盖
temporal / identity / responsibility / stack四类 - 记忆是否陈旧主要依赖时间和新信息覆盖,还没有更强的事实冲突判定
- 插件只提供 server 能力,没有单独的可视化 memory 管理界面
9.2 已有实现成熟度
从源码和测试覆盖来看,当前版本已经具备较完整的工程闭环:
- 验证了 extraction 游标持久化
- 验证了模型失败时的 fallback memory 写入
- 验证了中文/英文混合提问下的记忆召回
- 验证了全局会话与工作区会话下的存储根目录解析
- 验证了 dream 开关的持久化行为
9.3 后续可继续增强的方向
- 引入更强的冲突检测与事实合并策略
- 为 memory 库增加可视化管理面板
- 把 captured signals 扩展到更多 durable categories
- 在更大规模记忆库场景下引入向量检索或分层索引
总结
opencode-memory-dream 不是“给模型加一个 remember 命令”这么简单,而是在 OpenCode 插件框架内实现了一套完整的长期记忆机制:
- 前台会话负责正常协作
- 后台子会话负责提取与整理
- 系统提示负责召回与约束
- 显式工具负责用户可控的记忆管理
如果说普通会话式 AI 擅长“解决眼前问题”,那么这个插件要补齐的,就是它在长期协作里最欠缺的部分:持续记住那些代码之外、但对协作真正重要的事实。
