oh-my-pi 记忆编辑指南:深入解析 `memory_edit` 的 update / forget / invalidate 操作与 memory:// 协议
2026/9/11 19:54:50 网站建设 项目流程

oh-my-pi 记忆编辑指南:深入解析memory_edit的 update / forget / invalidate 操作与 memory:// 协议

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

导读

memory_edit是 oh-my-pi(coding agent with the IDE wired in)在启用 Mnemopi 本地长期记忆后端时提供的记忆维护工具:它允许 Agent 按 ID 对长期记忆执行update(更新工作记忆)、forget(硬删除工作记忆)、invalidate(软作废旧记忆,可指定替代记忆)三种操作。本文以 memory-edit.md 为骨架,结合 memory-edit.ts、mnemopi/state.ts、Mnemopi Beam 存储层实现与契约测试,讲清每条编辑规则背后的源码依据、参数语义与安全操作流程。读完你将掌握:何时该用哪种编辑操作、为什么 fact 记忆只读、为什么 update 前必须先read memory://<id>,以及每一步在仓库源码中的具体落点。

前置条件:Mnemopi 后端与工具注册

memory_edit并非默认可用。它的工厂函数在 memory-edit.ts 中做了严格的门控:

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

即只有当memory.backend配置为mnemopi时,工具才会被注册进内置工具表(见 tools/index.ts 的memory_edit: MemoryEditTool.createIf)。契约测试 memory-tools.test.ts 验证了这一点:offhindsight后端下createIf返回null,而mnemopi后端下retain/recall/reflect/edit四个工厂均返回工具实例。

启用 Mnemopi 的最小配置(详见 mnemosyne-memory-backend.md):

memory: backend: mnemopi

可选的常用参数:

mnemopi: scoping: per-project-tagged # global / per-project / per-project-tagged autoRecall: true # 会话首轮自动召回 autoRetain: true # 自动留存已完成的对话轮次 retainEveryNTurns: 4 # 自动留存的最小用户轮次间隔 recallLimit: 8 # 提示词块中最大召回条数

记忆属于“背景上下文”而非指令:当它与当前用户消息或工具输出冲突时,以当前用户消息和工具输出为准(见 mnemosyne-memory-backend.md)。这也是为什么编辑记忆需要谨慎——你修改的是影响未来会话召回的背景信息。

memory_edit的输入参数与操作语义

工具的参数模式定义在 memory-edit.ts:

const memoryEditSchema = type({ op: type("'update' | 'forget' | 'invalidate'").describe("memory edit operation"), id: type("string").describe("memory id from recall output"), "content?": type("string").describe("replacement content for update"), "importance?": type("number").describe("replacement importance for update (0–1)"), "replacement_id?": type("string").describe("replacement memory id for invalidate"), });
参数类型必填含义
op枚举'update' \| 'forget' \| 'invalidate'要执行的编辑操作
idstring来自recall输出的记忆 ID
contentstring否(update 时需二选一)update的替换内容
importancenumber (0–1)否(update 时需二选一)update的替换重要性
replacement_idstringinvalidate时记录的替代记忆 ID

三种操作的语义,与文档 memory-edit.md 一一对应:

  • update:面向工作记忆(working memory),替换其内容和/或重要性。适用于更正过时、不准确或表述不佳的既有记忆。update是整体替换(wholesale replace)——传入content会整段覆盖旧内容,因此必须先读取完整内容再合并,防止丢失未预览的尾部。
  • forget:永久删除工作记忆(hard delete),面向需要硬删除的内容。删除是物理性的:Beam 层的forgetWorking会执行DELETE FROM working_memory并顺带清理该记忆的派生工件(见下文源码剖析)。
  • invalidate:软作废(softly supersede),面向仍可能有用历史的过时记忆。它不删除任何行,只打上失效标记并记录替代记忆 ID,历史仍可被追溯。

工具在调用前对参数做了一层前置校验(memory-edit.ts):若op === "update"contentimportance都未提供,直接抛错"memory_edit update requires content or importance."。同时importance会被夹紧到[0, 1]区间(Math.max(0, Math.min(1, params.importance)),见 memory-edit.ts),与模式描述中的(0–1)约定一致。

工具执行时会调用会话状态的editScopedMemory(memory-edit.ts),并把结果(含所在 bank / store)格式化为人类可读的返回文本;对not_foundnot_editable两种失败场景返回不同提示,其中 fact 记忆会提示Read it with memory://<id>.(memory-edit.ts)。

编辑的分层路由:从工具到 Beam 存储

一次memory_edit调用跨越三个层次,理解这条链路有助于定位任何异常行为:

  1. 工具层(Tool)MemoryEditTool.execute负责参数校验、importance 夹紧、调用state.editScopedMemory(op, id, {...})、格式化结果文本(memory-edit.ts)。
  2. 会话状态层(Session State)MnemopiSessionState.editScopedMemory负责在多个 bank 之间按顺序解析目标记忆、判定可编辑性、分派具体操作(mnemopi/state.ts)。
  3. 存储层(Beam)Mnemopi门面把update/forget分别转发给beam.updateWorking/beam.forgetWorking(memory.ts),invalidate直接调用beam.invalidate(beam/index.ts)。

会话状态层:作用域解析与可编辑性判定

editScopedMemory是编辑语义的核心裁决者。它先把会话可触及的 bank 去重排序为「retain 目标、recall 目标、global bank」(mnemopi/state.ts),然后按顺序查找目标记忆,命中即中止(first hit wins)。对每条命中的记忆,它根据memory_store字段判定归属:

  • memory_store === "fact":返回not_editable——fact 表是只读的,任何编辑操作都不修改 facts 表,因此即使 ID 能解析也必须精确报告为不可编辑(mnemopi/state.ts,注释引用了 issue #4725)。
  • opupdateforgetstore !== "working":返回not_found——这两类操作只面向工作记忆(mnemopi/state.ts)。
  • update命中工作记忆:调用target.memory.update(id, content, importance)(mnemopi/state.ts)。
  • forget命中工作记忆:调用target.memory.forget(id)(mnemopi/state.ts)。
  • 其余走invalidate:调用target.memory.beam.invalidate(id, replacementId),可同时作用于工作记忆与情景记忆(episodic memory)(mnemopi/state.ts)。

若所有 bank 都未命中,返回not_found;若中途遇到不可编辑目标但最终没有成功操作,则返回首个ineligible(即not_editablenot_found),保证错误信息尽量精确(mnemopi/state.ts)。

存储层:三种操作的真实 SQL

Beam 存储层的实现把文档语义落到具体 SQL(store.ts):

  • updateWorking(store.ts):动态拼接 SET 子句——提供content时更新content并置空embed_text(触发重新嵌入调度scheduleEmbedding);提供importance时更新importance。两者都未提供则返回false。更新成功后使查询缓存失效(invalidateCaches),保证后续召回能看到新内容。
  • forgetWorking(store.ts):在事务中执行DELETE FROM working_memory WHERE id = ? AND session_id = ?,命中后调用purgeWorkingMemoryArtifacts清理关联工件,并同样使缓存失效。注意 WHERE 条件带session_id——删除被限定在调用者所属会话作用域内。
  • invalidate(store.ts):执行两条UPDATE——先尝试working_memory,未命中再尝试episodic_memory,将valid_until置为当前时间、superseded_by置为replacement_id。这是一种软删除:行仍然存在,但召回查询统一以valid_until IS NULL OR valid_until > nowsuperseded_by IS NULL为过滤条件(见 recall.ts),因此被作废的记忆不会再进入召回结果,历史却完整保留。superseded_by字段让替代记忆与被替代记忆之间形成可追溯的链接。

从 schema 看,working_memoryepisodic_memory两张表都定义了valid_until TIMESTAMP DEFAULT NULLsuperseded_by TEXT DEFAULT NULL(schema.ts),这正是invalidate软作废机制的物理基础。

只读的 fact 记忆与 not_editable

文档明确:recall结果中标记为[facts]的是只读事实,任何编辑操作都会返回not_editable(memory-edit.md)。仓库证据有两处:

  1. 存储层注释直接声明:memory_store: "fact"标记为只读——不允许 update/forget/invalidate(store.ts)。
  2. 状态层的editScopedMemory对 fact 专门返回not_editable,且即使后续 bank 也找不到该 ID,仍保留这个精确错误而非退化为not_found(mnemopi/state.ts)。

对 fact 记忆,正确做法是用read memory://<id>检视内容(只读查看),而不是编辑。这保证了事实性知识(例如实体关系、已知约束)不会被 Agent 随意改写。

为什么 update 前必须read memory://<id>

这是本工具最重要的安全约束,文档用加粗的 MUST 强调(memory-edit.md):

MUST read full memory beforeupdate. Recall previews clipped: trailingmarks truncation;full_lengthoriginal size.updatereplaces content wholesale → updating a preview deletes its unseen tail. Firstread memory://<id>; pass merged content incontent.

原因链如下:

  1. 召回结果是预览(preview)recall返回的是截断的内容预览,超出预览上限的部分以尾随标记,并附带truncated: truefull_length(原始长度)字段(见 recall.md)。
  2. update 是整体替换updateWorking直接用新content覆盖旧内容(store.ts),没有增量合并机制。因此若拿一段被截断的预览去 update,被省略的尾部会被永久抹掉。
  3. memory://<id>返回完整行getScopedMemory从 retain/recall/global 各 bank 按序查找并组装完整行(含 content、source、timestamp、importance、veracity、metadata 等),其设计目的正是支撑memory://<id>读取,让 Agent 在整体替换前看到未裁剪的全文(mnemopi/state.ts 的注释明确引用 issue #4443)。

memory://属于内部 URL 协议族(agent://artifact://memory://skill://等,见 internal-urls/types.ts)。其中memory://<id>这一命名空间只有在memory.backend=mnemopi时可用——在 Hindsight 后端下read memory://<id>会得到明确纠错提示(memory-protocol.ts)。而memory://root则指向项目级记忆摘要文件(system-prompt.md),与本工具的按 ID 编辑无关。

安全的 update 流程

  1. recall定位目标记忆,记录其id
  2. read memory://<id>获取完整内容(而非被截断的预览)。
  3. 在完整内容基础上合并你的修改,构造新的content
  4. 调用memory_editop: "update", id: <id>, content: <合并后的完整内容>

如何选择:invalidate 与 forget 的取舍

文档给出的决策准则(memory-edit.md):

Preferinvalidatefor stale memory whose history may still be useful. Useforgetonly for content requiring hard deletion.

场景推荐操作理由
记忆过时但仍可能有追溯价值(如决策沿革、曾被采纳后被推翻的方案)invalidate软作废:保留历史,可选replacement_id关联替代记忆,召回自动过滤
需要物理删除的内容(隐私、错误数据、敏感信息)forget硬删除:DELETE行并清理派生工件,不可恢复
记忆内容不准确或表达欠佳,主体仍有价值update保留同一 ID,整体替换内容/重要性

invalidatereplacement_id参数把新旧记忆串成superseded_by链:invalidateSQL 会把superseded_by写成替代记忆的 ID(store.ts),使“谁取代了谁”在数据层面可查询。这一软删除设计也避免了硬删除导致的引用悬空:作废行仍可被合并、压缩或人工审计读到,只是不再出现在面向 Agent 的召回结果中。

错误信息速查

memory_edit的可能返回状态(来自editScopedMemory的结果与工具格式化逻辑,memory-edit.ts):

状态含义提示文本示例
updated/deleted/invalidated操作成功,并附带所在 bank(及 store)Memory <id> updated in bank <bank> (<store>).
not_found在任何作用域 bank 中都找不到该 IDMemory <id> was not found[ in bank …].
not_editable命中 fact 只读记忆Memory <id> is a read-only fact…; cannot be edited. Read it with memory://<id>.

此外工具层会先抛参数错误:memory_edit update requires content or importance.(memory-edit.ts);若会话尚未初始化 Mnemopi 状态,则抛Mnemopi backend is not initialised for this session.(memory-edit.ts)。

总结

memory_edit是 oh-my-pi Mnemopi 记忆体系中的“写操作”入口,与recall(读)、retain(写新)、reflect(综合)共同构成完整的记忆生命周期管理。它的设计处处体现数据安全优先:fact 只读、update 强制先读全文、invalidate 软作废优于 forget 硬删除、SQL 更新带session_id/scope作用域约束。在使用时牢记三条铁律即可:只编辑recall返回的 ID;update前先read memory://<id>;能invalidate就别轻易forget

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

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

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

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

立即咨询