OpenCode 持久记忆实战指南:用 @vectorize-io/opencode-hindsight 让 Agent 跨会话记住一切
2026/9/13 11:38:18 网站建设 项目流程

OpenCode 持久记忆实战指南:用 @vectorize-io/opencode-hindsight 让 Agent 跨会话记住一切

【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight

OpenCode 是终端原生的 AI 编码智能体,但默认情况下每个会话都是"冷启动"——昨天定下的技术决策、项目约定与架构背景在下一次会话中全部丢失。本文基于 Hindsight 项目官方博客与技术文档,深入讲解@vectorize-io/opencode-hindsight插件的安装配置、自动 Hook 与显式工具的底层实现,帮助你为 OpenCode 搭建一套持久化长期记忆系统。

TL;DR

  • OpenCode 会话默认无状态,每次对话都从零开始
  • @vectorize-io/opencode-hindsight通过三个显式工具(hindsight_retainhindsight_recallhindsight_reflect)和自动 Hook 提供持久记忆
  • 记忆会在会话开始时被注入系统提示词,让 Agent 在你说出第一句话之前就拥有上下文
  • 会话空闲时自动捕获对话,记忆可跨越上下文窗口压缩(compaction)而保留
  • 支持 Hindsight Cloud(零配置)或自托管两种模式

问题:会话之间没有任何持久记忆

OpenCode 是一个强大的终端原生编码智能体,你可以安装插件、接入各种模型供应商并完成真实工作。但每个会话都是相互隔离的——没有任何机制让 Agent 记住之前会话中发生的事情。

让 Agent 使用特定的测试框架,它照做了;下一次会话,它完全不记得。向它解释项目架构、部署流程、命名约定,会话一结束全部消失。

对于一次性任务这没问题;但对于依赖连续性的长期项目工作,这是实实在在的短板。

方案:Hindsight 记忆层 + OpenCode 插件

Hindsight 是 AI Agent 的记忆层:它从对话中抽取事实(facts),在查询时进行语义检索,并能基于累积的上下文综合出有依据的回答。@vectorize-io/opencode-hindsight插件把 Hindsight 直接接入 OpenCode 的插件系统,支持两种工作模式:

自动模式—— Hook 在后台处理一切:

  • 会话开始时召回相关记忆(注入系统提示词)
  • 会话空闲时保留(retain)对话内容
  • 上下文窗口压缩时保留记忆

显式模式—— Agent 可直接调用的三个工具:

  • hindsight_retain—— 存储特定的事实或决策
  • hindsight_recall—— 检索记忆中相关的上下文
  • hindsight_reflect—— 基于累积记忆综合出有依据的回答
Session start │ ▼ ┌─────────────────────────────────────┐ │ System prompt │ │ + recalled memories (automatic) │ ├─────────────────────────────────────┤ │ OpenCode Agent │ │ ├── hindsight_retain (explicit) │ │ ├── hindsight_recall (explicit) │ │ └── hindsight_reflect (explicit) │ └─────────────────────────────────────┘ │ ▲ retain recall │ │ ▼ │ ┌───────────────────────────────┐ │ Hindsight bank │ └───────────────────────────────┘

自动 Hook 覆盖大多数使用场景;显式工具则用于 Agent(或你)需要精细控制的时候。

安装与配置

1. 安装插件

npm install @vectorize-io/opencode-hindsight

2. 在 opencode.json 中注册

{ "plugin": ["@vectorize-io/opencode-hindsight"] }

注意:根据 hindsight-integrations/opencode/README.md,OpenCode 会在启动时自动安装"plugin"数组中列出的插件,此时无需手动执行 npm installopencode.json可以是项目级配置,也可以是全局配置~/.config/opencode/opencode.json

3. 指向一个 Hindsight 服务端

方案 A:Hindsight Cloud(零配置)

在 Hindsight Cloud 注册,生成 API Token,然后设置两个环境变量:

export HINDSIGHT_API_URL="https://api.hindsight.vectorize.io" export HINDSIGHT_API_TOKEN="hsk_your_token"

方案 B:自托管

本地或在自己的基础设施上运行 Hindsight,参考仓库内的 安装说明 启动服务:

uvx hindsight-server export HINDSIGHT_API_URL="http://localhost:8888"

自托管模式下 API Token 为可选项。插件源码中的默认值是DEFAULT_HINDSIGHT_API_URL = "https://api.hindsight.vectorize.io"(见 src/config.ts),因此插件实例化本身不会因 URL 未配置而失败;如果 Cloud URL 未配置 API Key,请求会在调用时失败并返回清晰可操作的错误信息,而不是静默禁用插件。

4. 启动 OpenCode

opencode

插件会自动激活。在第一次会话中,它会连接 Hindsight 并开始捕获上下文。

运行机制:一次会话的完整生命周期

插件初始化时(见 src/index.ts),会完成三件事:加载配置、创建HindsightClient、通过deriveBankId解析出当前记忆库(bank)ID,然后注册三个工具与三个 Hook。模块级PluginState会跨会话存活(OpenCode 服务进程只加载一次模块),用于记录回合数、已注入回忆的会话、各会话最后一次保留的回合。

以下是插件激活后一次会话的生命周期:

  1. 会话开始——session.created事件触发,标记此会话用于记忆注入
  2. 首次 LLM 调用——experimental.chat.system.transformHook 执行一次召回查询("project context and recent work"),并把匹配的记忆注入系统提示词
  3. 会话进行中—— Agent 可按需显式调用hindsight_retainhindsight_recallhindsight_reflect
  4. 会话空闲——session.idle事件触发对话记录的自动保留(auto-retain)
  5. 上下文压缩—— 如果上下文窗口填满,插件先保留当前对话,再把召回的回忆注入压缩上下文,确保重要内容不丢失

自动保留使用由retainEveryNTurns(默认 3)控制的滑动窗口,避免冗余存储的同时保持近期上下文的新鲜度。

值得注意的实现细节

  • recall 不依赖事件顺序:源码注释(src/hooks.ts)明确指出,session.createdsystem.transform的相对触发顺序是 OpenCode 未文档化的实现细节,在不同版本间有差异,因此自动召回完全由system.transformHook 驱动,而不是session.created。同时用recalledSessions集合做去重标记——只有 API 往返成功(即使结果是 0 条)才会标记,瞬时失败会在下一条消息时重试。
  • 注入位置:召回内容会折叠进system[0]第一段系统提示词而非追加新段落(src/hooks.ts)。因为 OpenCode 会把system[]的每一项作为独立的系统消息发出,而部分模型只认第一条——追加的段落可能被静默丢弃。
  • 压缩时先保留后召回:压缩 Hook 先调用与空闲保留相同的retainSession逻辑,再基于最后一条用户消息构造召回查询(src/hooks.ts)。

配置体系详解

插件支持三个层级的配置(后者覆盖前者,即"later wins"):

  1. 配置文件——~/.hindsight/opencode.json,用户级默认值
  2. 插件选项——opencode.json内联配置,项目级设置
  3. 环境变量—— 用于 CI/CD 或按机器覆盖

这个加载顺序在 src/config.ts 的loadConfig中实现:默认值 → 用户配置文件 → 插件选项 → 环境变量覆盖。源码还会校验枚举类字段:非法的retainModerecallTagsMatchrecallBudget会打印错误并回退到默认值。

插件选项示例

{ "plugin": [ ["@vectorize-io/opencode-hindsight", { "hindsightApiUrl": "http://localhost:8888", "bankId": "my-project", "recallBudget": "high", "retainEveryNTurns": 5, "bankMission": "You are a coding assistant for a TypeScript monorepo. Focus on architecture decisions, test patterns, and deployment procedures." }] ] }

核心配置项

配置项默认值作用
autoRecalltrue会话开始时注入记忆
autoRetaintrue会话空闲时捕获对话
recallBudgetmid召回的回忆条数:lowmidhigh
retainEveryNTurns3自动保留频率(按用户回合计)
retainModefull-sessionfull-session每次整体 upsert 整段对话;last-turn创建分块窗口
bankMission(无)指导 Hindsight 抽取什么、如何 reflect
dynamicBankIdfalse从项目/Agent 上下文推导 bank ID

完整环境变量参考

以下变量在 src/config.ts 的ENV_OVERRIDES映射中定义:

变量说明默认值
HINDSIGHT_API_URLHindsight API 基础地址https://api.hindsight.vectorize.io
HINDSIGHT_API_TOKEN认证 API Key(无,Cloud 必需)
HINDSIGHT_BANK_ID静态记忆库 IDopencode
HINDSIGHT_AGENT_NAME动态 bank ID 中的 Agent 名opencode
HINDSIGHT_AUTO_RECALL会话开始时自动召回true
HINDSIGHT_AUTO_RETAIN会话空闲时自动保留true
HINDSIGHT_RETAIN_MODEfull-sessionlast-turnfull-session
HINDSIGHT_RECALL_BUDGET召回预算:low/mid/highmid
HINDSIGHT_RECALL_MAX_TOKENS召回结果的最大 token 数1024
HINDSIGHT_RECALL_MAX_QUERY_CHARS召回查询的最大字符数800
HINDSIGHT_RECALL_CONTEXT_TURNS召回查询附带的历史上下文回合数1
HINDSIGHT_RECALL_TAGS逗号分隔,过滤召回结果(无)
HINDSIGHT_RECALL_TAGS_MATCH标签匹配模式:any/all/any_strict/all_strictany
HINDSIGHT_RETAIN_TAGS逗号分隔,附加到每次保留(无)
HINDSIGHT_RETAIN_EVERY_N_TURNS自动保留频率3
HINDSIGHT_RETAIN_OVERLAP_TURNSlast-turn 模式的窗口重叠回合数2
HINDSIGHT_RETAIN_CONTEXT保留记录附带的上下文标签opencode
HINDSIGHT_DYNAMIC_BANK_ID启用动态 bank ID 推导false
HINDSIGHT_BANK_MISSIONBank 使命/上下文(无)
HINDSIGHT_BANK_ID_PREFIXBank ID 前缀(空)
HINDSIGHT_CHANNEL_ID动态 bank 的渠道维度(无)
HINDSIGHT_USER_ID动态 bank 的用户维度(无)

两个值得注意的点:一是recallMaxTokensrecallMaxQueryChars共同约束了召回的成本与上下文占用——查询过长时truncateRecallQuery会优先保留最新用户消息、从最旧的历史上下文开始裁剪(见 src/content.ts);二是debug选项刻意不提供环境变量(源码注释 src/config.ts 说明:环境变量在 OpenCode 的插件运行时中不可靠,尤其在 Windows 上)。调试请通过opencode.json插件选项或~/.hindsight/opencode.json设置"debug": true。插件日志走 OpenCode 自己的日志流(service=hindsight),用opencode --print-logs或在 OpenCode 日志文件中查看;错误信息与解析出的 API URL/bank 默认就会打印,无需开启任何开关。

三个显式工具

插件通过createTools(src/tools.ts)注册三个工具,Agent 可按需调用:

  • hindsight_retain(content, context?)—— 存入长期记忆。参数content为要记住的具体且自包含的信息,可选context说明信息来源。工具描述建议 Agent 记忆时"Be specific — include who, what, when, and why"。调用时会将retainContextretainTagsretainMetadata一并传入。
  • hindsight_recall(query)—— 搜索长期记忆。工具描述建议 Agent 在回答涉及过往对话、用户偏好、项目历史的问题前主动调用("When in doubt, recall first")。无结果时返回 "No relevant memories found.",有结果时返回Found N relevant memories (as of ... UTC)并附格式化条目。
  • hindsight_reflect(query, context?)—— 与 recall 返回原始记忆不同,reflect 会把记忆综合成连贯答案,适合"关于这个用户你知道什么?"或"总结我们的项目决策"这类问题,返回response.text作为综合回答。

三个工具都会通过ensureBankMission确保 bank 的使命(mission)在首次使用时被设置(源码 src/bank.ts 用内存中的missionsSet去重,插件是长生命周期进程,不会重复调用createBank)。

用 Bank ID 隔离记忆

默认情况下所有会话共享一个opencodebank。需要项目隔离时有两种方案:

静态 Bank ID—— 每个项目一个 bank:

export HINDSIGHT_BANK_ID="my-project"

动态 Bank ID—— 从上下文自动推导:

export HINDSIGHT_DYNAMIC_BANK_ID=true

动态模式下,bank ID 由粒度字段(默认agent::project)拼接而成。例如位于/home/user/my-app的项目会得到 bank IDopencode::my-app

多用户场景:

export HINDSIGHT_DYNAMIC_BANK_ID=true export HINDSIGHT_CHANNEL_ID="team-backend" export HINDSIGHT_USER_ID="alice"

这会产生opencode::my-app::team-backend::alice—— 每个用户、每个项目、每个渠道完全隔离。

动态 bank 的源码细节

deriveBankId(src/bank.ts)支持五个粒度字段:agentprojectgitProjectchanneluser

其中gitProjectproject的进阶替代:它通过git rev-parse --path-format=absolute --git-common-dir解析主工作树(main worktree)的名字,使得同一仓库的所有 linked worktree 共享同一个记忆库;当 git 不可用或目录不在仓库中时回退到工作目录名。如果你用git worktree开发,建议用以下配置让各 worktree 共享记忆:

{ "dynamicBankId": true, "dynamicBankGranularity": ["agent", "gitProject"] }

另外要注意:bank ID 只在插件加载时推导一次,维度是进程级的——在运行中的 OpenCode 进程内不会随会话变化。因此按用户隔离时,要在启动每个用户的 OpenCode 实例之前设置好HINDSIGHT_CHANNEL_ID/HINDSIGHT_USER_ID环境变量。仓库内还有一份更完整的多用户记忆模式参考:cookbook/README.md。

踩坑与边界情况

记忆反馈回路。插件在保留前会从对话记录中剥离<hindsight_memories><relevant_memories>标签(stripMemoryTags,见 src/content.ts),防止循环放大。如果你构建了把 OpenCode 输出直接喂给 Hindsight 的自定义工具,需要注意这一点。

首次会话冷启动。新 bank 的第一次会话没有可召回的回忆,Agent 从空白开始,记忆从第二次会话起才累积。要预填充 bank,可以直接使用 Hindsight API 或 CLI(参考 hindsight-api/README.md)。

压缩时机。压缩 Hook 会先保留当前对话再召回相关上下文。如果 Hindsight 服务端响应慢或不可达,压缩照常进行、只是不注入记忆——插件永远不会阻塞 Agent

上下文窗口预算。召回的回忆会消耗上下文窗口的 token。recallBudget: "high"意味着更多上下文,但留给对话的空间更少。长会话中mid(默认值)是较好的平衡。

权衡与替代方案

什么时候不要用:

  • 如果会话总是一次性问答、没有连续性,记忆捕获与召回的开销不值当
  • 如果处理敏感代码库、对话内容不应离开本机,请使用自托管的 Hindsight 实例

替代方案:

  • 手工上下文文件—— 在项目根目录维护CONTEXT.mdCLAUDE.md。可用,但需要人工维护,且无法跨会话扩展
  • Git 历史—— 提交信息与 PR 记录的是"改了什么",而不是"为什么这么决定"
  • 会话导出/回放—— 有些工具支持导出会话并回放,但你无法把 50 个历史会话都塞进一个上下文窗口

插件在"决策、偏好和上下文在数天或数周内持续累积"的长期项目工作中价值最大。另外,仓库内已有一个功能更全面的替代品值得了解:官方文档指出该插件已被 Coding Agents 插件 取代(一个覆盖 Claude Code、Codex、opencode、Cursor 等 CLI Agent 的包,使用所有 Agent 共享的 per-repo 记忆库)。本页面与已发布的 npm 包仍然可用,只是不再继续开发。

总结

  • OpenCode 会话默认无状态
  • Hindsight 插件通过自动 Hook(开始即召回、空闲即保留、压缩中保全)和显式工具(retain、recall、reflect)提供持久记忆
  • 配置分层:配置文件、插件选项、环境变量(后者优先级最高)
  • 记忆通过 bank ID 隔离——简单场景用静态 ID,多项目/多用户场景用动态 ID
  • 插件非阻塞,Hindsight 不可达时优雅降级

下一步

  • 安装插件:npm install @vectorize-io/opencode-hindsight
  • 阅读 插件完整 README 获取全部配置细节
  • 参考 OpenCode 集成参考文档 了解迁移到 Coding Agents 插件的方式
  • 查看 插件源码 与 测试用例 深入理解 Hook 行为
  • 设置一个贴合项目主题的bankMission—— 它显著提升抽取与召回的针对性
  • 在 cookbook 中探索更多跨 Agent 框架的记忆模式

【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询