news 2026/9/12 2:40:15

oh-my-pi recall 工具深度解析:跨会话长期记忆检索的实现、作用域与配置指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
oh-my-pi recall 工具深度解析:跨会话长期记忆检索的实现、作用域与配置指南

oh-my-pi recall 工具深度解析:跨会话长期记忆检索的实现、作用域与配置指南

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

本文围绕 oh-my-pi 的recall工具展开:它是 coding agent 面向长期记忆后端的单次检索入口,负责把自然语言查询翻译成对 Hindsight 远程记忆服务或本地 Mnemopi 记忆库的召回请求,并将命中结果格式化为可注入上下文的纯文本列表。读完本文,你将掌握 recall 的注册与可见性规则、输入输出契约、两种后端的执行链路、bank 作用域模式、全部相关配置项与错误处理语义,并能据此正确配置和使用记忆检索能力。

工具定位:agent 的"主动回忆"入口

recall是 oh-my-pi coding agent 提供的记忆检索工具,在工具元数据中标注为approval = "read"strict = trueloadMode = "discoverable"。它被设计为单次执行(single-shot):不流式输出参数/结果更新,调用一次即返回完整结果。其模型面向的提示词(packages/coding-agent/src/prompts/tools/recall.md)给出了明确的使用指引:

Search long-term memory; return raw relevance-ranked matching entries. Use proactively before questions about past conversations, user preferences, project decisions, or topics where prior context improves accuracy. When in doubt, recall first.

即:在回答涉及历史对话、用户偏好、项目决策或任何"有先例可循"的问题之前,应当主动召回;拿不准时先 recall。提示词还区分了 recall 与 reflect 的职责边界:recall返回具体事实条目,reflect则跨多条记忆做综合归纳。

注册与可见性:什么条件下 recall 才会出现

工具的可用性由会话设置memory.backend决定。核心实现位于 packages/coding-agent/src/tools/memory-recall.ts:

static createIf(session: ToolSession): MemoryRecallTool | null { const backend = session.settings.get("memory.backend"); if (backend !== "hindsight" && backend !== "mnemopi") return null; return new MemoryRecallTool(session); }

注册规则可以归纳为以下几点:

  • 仅在memory.backend"hindsight""mnemopi"时注册"off""local"两种取值下该工具完全不可见。
  • 在无限制会话中,若显式给出了工具列表,注册逻辑会自动为任一受支持后端附带recall/retain/reflect三件套;受限工具列表不会被擅自扩宽。
  • 在普通tools.xdev会话中,discoverable 的内置工具可能以xd://recall形式呈现;若用户显式请求了该工具,则保持顶层可用。

测试 packages/coding-agent/test/memory-tools.test.ts 对这条规则做了直接验证:memory.backend不等于hindsight时三个工厂返回null;等于hindsightmnemopiMemoryRecallTool.createIf(...)返回工具实例。

输入输出契约:一次查询,一份结构化结果

输入

字段类型必填说明
querystring自然语言检索查询。工具会原样透传,唯一例外是 Mnemopiper-project-tagged模式可能额外执行一次内部共享 bank 的兜底查询

有命中时的输出

  • content[0].type = "text"
  • content[0].text = "Found <n> relevant memory/memories (as of YYYY-MM-DD HH:MM UTC):\n\n<bullet list>"
  • details = {}

时间戳由 packages/coding-agent/src/hindsight/content.ts 的formatCurrentTime(...)生成,严格使用 UTC,格式为YYYY-MM-DD HH:MM

Hindsight 子弹列表formatMemories(...)生成(见 content.ts):每条为- <text> [<type>] (<mentioned_at>)typementioned_at仅在对应字段存在时追加。

Mnemopi 子弹列表formatScopedRecallWithIds(...)生成(见 packages/coding-agent/src/mnemopi/state.ts):每条为- <content> (id: <id>) [<source>] (<YYYY-MM-DD>) c:<score>,其中:

  • id 不可用时渲染为(id unavailable)
  • source、日期、c:score在缺失时省略;
  • score 取自result.score ?? result.importance,格式化为一位小数。

内容预览与截断语义

Mnemopi 的召回内容是预览,默认上限 500 字符,定义在 packages/mnemopi/src/core/beam/recall.ts:

export const RECALL_CONTENT_PREVIEW_CHARS = 500;

RecallOptions.contentPreviewChars可覆盖此值;设为0或负数表示不裁剪。显式工具路径使用默认值。被裁剪的预览以结尾——这对应提示词中提到的truncated: true/full_length内部标记。重要实践:预览不等于全文,执行memory_edit update之前,必须先用read memory://<id>拉取完整行,避免覆盖未看到的字节(这正是 mnemopi/state.ts 中getScopedMemory(...)支撑memory://<id>URL 的原因)。值得注意的是,虽然内部召回行携带truncatedfull_length,但本工具返回的是格式化文本、details = {},并不暴露这两个字段。

无命中时的输出

  • content[0].text = "No relevant memories found."
  • details = {}
  • useless = true,允许调用方/渲染器将该结果视为"无贡献上下文",从而不占用有效上下文预算。

执行流程:从工厂到结果的全链路

工具的执行入口封装在untilAborted(...)中(支持调用信号取消),随后按后端分叉。整体流程(memory-recall.ts):

  1. MemoryRecallTool.createIf(...)memory.backend"hindsight""mnemopi"时暴露工具;
  2. execute(...)untilAborted(...)包裹整个操作;
  3. 后端为mnemopi时走本地召回链路;
  4. 后端为hindsight时走远程 HTTP 召回链路;
  5. 任一后端失败都以logger.warn("recall failed", ...)记录,并按需以Error实例重新抛出。

Mnemopi 本地链路

Mnemopi 是本地 SQLite 记忆后端,无网络依赖(除非配置的本地运行时 provider 在召回时执行 embedding/LLM 工作)。执行要点:

  • 读取session.getMnemopiSessionState(),后端未启动则抛出Mnemopi backend is not initialised for this session.
  • 调用state.recallResultsScoped(params.query)
  • 作用域召回会对每个解析出的 recall bank 执行recallEnhanced(query, recallLimit, { includeFacts: true, channelId: bank })(见 mnemopi/state.ts),按 id/content 合并去重、排序(score → 时间戳 → 内容),最终截断到recallLimit
  • 结果以带 id 的格式输出,供后续memory://<id>全文读取与memory_edit操作使用。

Hindsight 远程链路

Hindsight 是远程记忆服务。执行要点:

  • 读取session.getHindsightSessionState(),未启动则抛出Hindsight backend is not initialised for this session.
  • 调用state.client.recall(...),携带bankId、查询、配置的budgetmaxTokenstypes以及 bank 作用域标签过滤器;
  • HindsightApi.recall(...)POST /v1/default/banks/{bank_id}/memories/recall发起请求;
  • 结果经formatMemories(...)格式化为纯文本列表。

注意一个细节:Hindsight 侧state.client.recall(...)的默认预算(raw API 层)是"mid",而本工具会用配置覆盖(见下文 Limits 小节),配置优先级高于 client 默认。

取消与副作用

  • 取消:工具调用信号被取消时,通过untilAborted(...)中止;
  • 网络副作用:Hindsight 为一次POST /v1/default/banks/{bank_id}/memories/recall;Mnemopi 无网络调用;
  • 会话状态副作用:显式工具路径在成功时不更新lastRecallSnippet、不刷新系统提示词——这与后端自动召回(auto-recall)行为不同,后者会更新lastRecallSnippet并把<memories>块注入开发者指令。

工具召回 vs 自动召回:两条并行的检索路径

文档强调,recall工具本身"不从不远的轮次组合上下文"(explicit query-only recall)。更丰富的查询组合逻辑存在于后端会话状态的自动召回路径中:

  • Hindsight:HindsightSessionState.beforeAgentStartPrompt(...)/maybeRecallOnAgentStart(...)(见 packages/coding-agent/src/hindsight/state.ts);
  • Mnemopi:MnemopiSessionState.beforeAgentStartPrompt(...)/maybeRecallOnAgentStart(...)(见 packages/coding-agent/src/mnemopi/state.ts)。

自动召回在首次 turn 的提示词生成前触发:用composeRecallQuery(...)把最近recallContextTurns轮(按 user 消息切分、剥离记忆标签)拼进 "Prior context:" 块,再用truncateRecallQuery(...)在字符预算内截断——始终保留最新用户消息,优先丢弃更早的上下文行(实现见 content.ts)。命中后,Hindsight 端生成<memories>块并刷新系统提示词。

显式工具路径与自动召回共享同一套底层检索能力,但互不干扰:工具调用不会更新lastRecallSnippet,自动召回的<memories>/<mental_models>块也不会因工具调用而变化。

作用域模式:记忆按项目隔离的三种形态

recall 的检索范围由后端的作用域(scoping)配置决定,两个后端各有实现。

Hindsight bank 作用域

模式行为
global无标签过滤,检索全局共享 bank
per-project每个项目标签一个独立 bank id(git 主检出根目录 basename;仓库外则为 cwd basename)
per-project-tagged共享 bank id +project:<项目标签>过滤器,tagsMatch = "any",使项目标签记忆与无标签全局记忆都能浮出

Hindsight 默认 scoping 为per-project-tagged(见 packages/coding-agent/src/hindsight/config.ts),且该模式下的 recall 携带recallTagsrecallTagsMatch标签过滤参数。

Mnemopi bank 作用域

Mnemopi 无标签过滤召回能力,因此per-project-tagged被映射为"项目本地写 bank + 共享可召回 bank"的组合(见 packages/coding-agent/src/mnemopi/config.ts):

模式行为
global只读共享 bank
per-project读取由绝对 cwd basename + 绝对 cwd 哈希派生的 bank
per-project-tagged同时读 cwd 派生项目 bank 与共享 bank,合并结果

Mnemopi 默认 scoping 为per-project。项目 bank id 的稳定性契约值得注意(config.ts):它仅由绝对 cwd 派生<sanitized-basename>-<Bun.hash(projectRoot).toString(36)>),不依赖 git 根目录查找——早期版本基于 git 根解析,导致增删.git会改变 bank id 并碎片化记忆(issue #2412)。

此外,per-project模式还会执行安全旧 bank 扫描:探测<dbDir>/banks/下所有working_memory行都属于当前绝对 cwd 的 bank,将其加入召回集合以抢救旧方案下的记忆;扫描上限为 64 个候选目录(LEGACY_BANK_SCAN_LIMIT),缺失目录、不可读或损坏的 SQLite 文件会被静默跳过(config.ts)。

per-project-tagged模式下,共享 bank 还可能收到一次额外的兜底查询:剥离项目 bank 的字面 token 后重新检索,使被项目术语"污染"的查询仍能命中宽泛的全局记忆(deriveSharedRecallFallbackQuery,见 mnemopi/state.ts)。

会话作用域

recall 读取跨会话的记忆数据,但使用当前会话缓存的配置与作用域。子代理(subagent)别名继承父代理的后端作用域:Hindsight 与 Mnemopi 的 alias 状态共享父级的 bank、scope、config 与 client(例如 hindsight/state.ts 中aliasOf字段的注释说明),因此子代理调用 recall/retain/reflect 会持久化到与父代理相同的 bank。

配置项与上限:一张表掌握全部开关

可用性前提

  • 工具可用性要求memory.backend"hindsight""mnemopi"默认值为"off",即开箱默认不启用记忆检索。

Hindsight recall 相关设置

设置项默认值说明
hindsight.recallBudget"mid"召回预算档位(low/mid/high
hindsight.recallMaxTokens1024召回结果的最大 token 数
hindsight.recallTypes["world", "experience"]召回的记忆类型过滤
hindsight.recallTimeoutMs30_000recall 请求客户端截止时间(毫秒)

环境变量(如HINDSIGHT_RECALL_BUDGETHINDSIGHT_RECALL_MAX_TOKENSHINDSIGHT_RECALL_TIMEOUT_MS)的优先级高于持久化设置(hindsight/config.ts 明确"env wins"),便于 CI/生产环境按 shell 覆盖。测试桩 memory-tools.test.ts 也以mid/1024/["world","experience"]/30_000作为默认值验证。

Mnemopi recall 相关设置

设置项默认值说明
mnemopi.recallLimit8合并去重后返回的最大结果数,运行时钳制为至少 1
mnemopi.scoping"per-project"作用域模式
内容预览上限500字符/条RECALL_CONTENT_PREVIEW_CHARS定义

显式工具路径不应用的配置

以下四个配置只影响后端自动召回的查询组合,显式工具路径直接透传用户query,不适用:

  • hindsight.recallContextTurns
  • hindsight.recallMaxQueryChars
  • mnemopi.recallContextTurns
  • mnemopi.recallMaxQueryChars

也就是说,自动召回会对查询做"多轮上下文拼接 + 字符截断",而工具调用则完全忠实于用户给出的查询文本(Mnemopiper-project-tagged的共享 bank 兜底查询除外)。

错误处理语义:失败的边界在哪里

  • 后端未初始化memory.backend == "mnemopi"但无会话状态时抛Mnemopi backend is not initialised for this session.;Hindsight 同理抛Hindsight backend is not initialised for this session.(memory-recall.ts)。
  • Hindsight 失败:HTTP、fetch 与超时失败统一转为HindsightError;HTTP 错误携带statusCode与解析后的details
  • Mnemopi 失败:按目标 bank 逐个捕获并记录日志(Mnemopi: scoped recall target failed)。健康的 bank 仍贡献结果;仅当所有目标都失败时:单目标失败抛出原始错误,多目标失败抛出带各 bank 详情的AggregateError,而不会静默转成空结果(mnemopi/state.ts)。
  • 非 Error 异常归一化:工具捕获的非Error失败统一转为new Error(String(err))后重抛。

此外,文档与源码都强调了一个自动召回侧的关键保障:Hindsight 自动召回引擎的失败不会逃逸到会话生命周期——maybeRecallOnAgentStart/beforeAgentStartPrompt内部吞掉异常并记日志(测试 memory-tools.test.ts 专门验证了"后台自动召回引擎失败不逃逸"与"agent-start 召回失败在其内部 bank 守卫之前被包含")。

与 retain、reflect 的分工

  • 共享后端:recall、retain、reflect 共用同一个记忆后端,存储、子代理别名、bank 作用域、任务设定与心智模型行为详见 docs/tools/retain.md。
  • 心智模型:recall不会主动拉取 Hindsight 心智模型(mental models)。后端会把<mental_models>块独立于召回结果缓存在 agent 的开发者指令中,因此无需本工具重复获取。
  • Mnemopi 的<memories>:可能由自动召回写入开发者指令;显式工具调用不会更新该块。
  • 综合归纳:本工具只返回命中条目、不做跨条目的综合。需要远程 Hindsight 综合归纳时用reflect;Mnemopi 的reflect变体是"本地召回 + 格式化"。

实战建议小结

  1. 配置启用:将memory.backend设为"hindsight"(远程)或"mnemopi"(本地 SQLite),二者默认均为关闭("off");"local"也不注册 recall。
  2. 查询写作query是自然语言检索词,工具原样透传(自动召回才会拼接多轮上下文),因此在工具调用时尽量把关键实体、时间、项目名写进单条查询。
  3. 预览不等于全文:看到以结尾的 Mnemopi 预览后,先用read memory://<id>读全文,再决定是否memory_edit update
  4. 按项目隔离:Hindsight 默认per-project-tagged(共享 bank + 标签过滤),Mnemopi 默认per-project(cwd 派生 bank);需要跨项目全局记忆时切换作用域模式或依赖per-project-tagged的共享 bank 兜底查询。
  5. 超时与预算:Hindsight recall 超时默认 30s,结果 token 上限 1024,可通过hindsight.recallTimeoutMs/hindsight.recallMaxTokens或对应HINDSIGHT_*环境变量调整;Mnemopi 结果条数默认 8 条(mnemopi.recallLimit)。

【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 2:39:57

SpringBoot+Vue高校选题管理系统开发实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 2:38:54

JIT咨询服务怎么选?拆解丰田模式中国化改造的落地关键

1. 先搞清楚一件事&#xff1a;JIT咨询服务商&#xff0c;你选的到底是工具还是体系这几年制造业圈子有个特别奇怪的现象&#xff1a;一说要上JIT&#xff08;准时制生产&#xff09;&#xff0c;老板们第一反应就是找咨询公司。这个方向没错&#xff0c;丰田模式确实是从JIT起…

作者头像 李华
网站建设 2026/9/12 2:31:15

本地部署大模型实战:Ollama、Transformers、llama.cpp与量化全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 2:30:30

基于U-Net的路面裂缝检测识别系统设计与工程部署实践

简介&#xff1a;这是一套基于MATLAB的路面裂缝检测识别系统设计资源&#xff0c;面向深度学习、图像处理方向的工程实践与课程设计&#xff0c;可用于道路病害自动化检测和算法验证。压缩包内共18个文件&#xff0c;以14个.m源码为主&#xff0c;覆盖主程序、图像处理函数与裂…

作者头像 李华
网站建设 2026/9/12 2:26:56

嵌入式四大方向本质:MCU、Linux应用、驱动与硬件的能力坐标系

1. 嵌入式四大方向到底指什么&#xff1f;先别急着选&#xff0c;得看清每条路的“地基”在哪“嵌入式四大方向&#xff0c;到底怎么选&#xff1f;”——这问题我每天在技术群、面试现场、甚至咖啡馆里被问至少五次。不是因为大家懒&#xff0c;而是刚入行时看到的全是碎片&am…

作者头像 李华