mcpField
K
keymem

keymem

本地服务
github.com/donggyun112记忆与知识31 周前更新

关于 keymem

keymem 是面向 LLM Agent 的本地关联记忆 MCP 服务,以概念与记忆相连的图结构为基础。Agent 可沿共享键路径找回与查询词不相似但相关的信息,并控制进入上下文的记忆。

主要功能

  1. 01多对多键与记忆图
  2. 02由 Agent 引导的多跳回忆
  3. 03新鲜度与确认状态跟踪
  4. 04更正记录与版本保留
  5. 05本地 JSON 存储与嵌入

适用场景

  1. 01通过概念关联找回事实
  2. 02逐步浏览已存记忆
  3. 03检查记忆是否仍然有效
  4. 04更正信息并保留旧版本

MCP 相关

它在本地作为 MCP 服务运行,向兼容的 Agent 提供记忆回忆、导航、存储和确认能力。其 MCP 流程让 Agent 按步骤在键与记忆之间移动。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"donggyun112-keymem": {
"command": "npx",
"args": [
"-y",
"keymem"
]
}
}
}
启动命令npx -y keymem

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmkeymem0.17.0

工具

11

收录的工具列表 · 更新于 2 周前

参数类型必填说明
querystring是
top_knumber否
injectboolean否
namespacestring否
inject_top_knumber否
inject_prefer_depthboolean否
inject_explore_shallowboolean否

详细介绍

keymem

npm version Node.js License: MIT

面向大模型 Agent 的联想记忆层——通过关联而非仅凭相似度进行回忆。

keymem 链式回忆——每一跳调用一次 recall(),沿着 Key → Memory → Key 前进;跳过的分支仍可通过一次调用访问

大多数 Agent 记忆都是向量存储。它会找出听起来和你的查询相似的内容,却会漏掉所有与你的查询相关联的内容。

keymem 会将记忆存储在键图中。因此,搜索 “Newton” 仍能找到 “strawberries”——Newton → apple → fruit → strawberry。路径存在于图中,而非嵌入空间。它作为 MCP 服务在本地运行,因此任何兼容 MCP 的 Agent 都能进行类人的联想回忆,无需外部数据库。

兼容: Claude Desktop · Claude Code · 任何兼容 MCP 的大模型 Agent


为什么需要联想记忆?

向量存储记忆通过嵌入相似度检索内容。当你需要的内容与输入的词语并不相似时,这种方式就会失效:

Query: "Newton"
Similarity search finds: "Newton discovered gravity"  ✅
Similarity search misses: "user likes strawberries"   ❌

人可以自行完成这一步联想——Newton 让他们想起苹果,苹果是水果,而他们喜欢草莓。keymem 也能完成同样的联想,因为键图中存在这条路径:Newton → apple memory → fruit key → strawberry memory。“Newton”和“strawberry”之间没有嵌入距离上的关联;将它们连接起来的是一串共享键。

这就是核心理念:记忆不是按距离排序的孤岛,而是 Agent 可以遍历的 N:M 键/值图中的节点。


工作原理

Key Space (concepts)         Value Space (memories)
[apple]   ────────┬─────────→      ↑ same memory
[gravity] ────────┘
                  │
[apple]   ────────┼─────────→ "apples are red fruit"
[fruit]   ──────┬─┘
[red]     ──────┤
                │
[fruit]   ──────┼─────────→ "user likes strawberries"
[strawberry]────┘

记忆存放在值空间中,通过独立的键空间访问——一条记忆可以通过多个键访问,一个键也可以指向多条记忆。

recall("Newton") 会返回匹配的键簇,例如 [Newton] 和 [apple],并在最强匹配键下被动返回一条 Top-1 记忆。这条记忆包含 matched_key、validity 和 connected_keys,因此 Agent 可以立即作答,也可以通过 read_key(fruit) 继续探索。

默认的 MCP 流程仍然是 Key → Memory → Key,但第一步 Key → Memory 只需一次调用即可完成。只有一条记忆会自动进入上下文;后续步骤仍由 Agent 主动决定。

顶部的动画展示了这一流程在实际存储中的运行方式:调用三次 recall(),从个人资料键一路遍历到验证理念,加载三条记忆(约 2.4k 个 token)。Agent 跳过的分支仍可通过一次调用访问;而自动展开同一个包含 12 条记忆的枢纽,则会耗费约 8k 个 token。图掌握路径;Agent 决定步伐。源码:docs/demo/chain-walk.html。


快速开始

keymem 是一个 MCP 服务(CLI),不是库。 使用 npx -y keymem 运行(推荐——始终使用最新版),或通过 npm i -g keymem 全局安装命令。不要将其作为依赖通过 npm i keymem 添加到你的应用中:它捆绑了 openai、zod 和 MCP SDK,因此在已有项目中只会重复安装这些依赖树(还可能与你应用中的 zod/openai 版本冲突)。npm 在软件包页面上显示的 npm i keymem 命令适用于库,不适用于此处。

# Optional global install (npx needs none). This puts a `keymem` command on PATH that
# MCP clients can spawn. Run bare, it starts a stdio MCP server and waits for a client —
# so point your MCP config at `keymem` (or just use `npx -y keymem` as shown below).
npm i -g keymem
Claude Desktop

将以下内容添加到 claude_desktop_config.json:

OpenAI 嵌入:

{
  "mcpServers": {
    "keymem": {
      "command": "npx",
      "args": ["-y", "keymem"],
      "env": {
        "OPENAI_API_KEY": "your-openai-api-key"
      }
    }
  }
}

本地嵌入(无需 API 密钥)——推荐使用 bge-m3:

{
  "mcpServers": {
    "keymem": {
      "command": "npx",
      "args": ["-y", "keymem"],
      "env": {
        "EMBEDDING_BACKEND": "local",
        "LOCAL_EMBEDDING_MODEL": "bge-m3"
      }
    }
  }
}

bge-m3(多语言,推荐)首次运行时会自动下载约 570MB,之后会缓存。省略 LOCAL_EMBEDDING_MODEL 即可使用更轻量的默认模型(fast-multilingual-e5-large)。跨编码器重排是核心回忆流程的一部分,首次使用时会下载第二个模型;只有在要禁用它时才将 "KEYMEM_RERANK": "false"。

插件(推荐 — Claude Code 和 Codex)

此仓库也是一个插件市场,因此安装一次即可配置好所有内容:MCP 服务(由守护进程支持的适配层)、每次提交提示词时被动呈现相关记忆的 UserPromptSubmit 钩子,以及包含回忆/记忆协议的 keymem 技能。

Claude Code:

/plugin marketplace add donggyun112/keymem
/plugin install keymem@keymem

Codex CLI:

codex plugin marketplace add donggyun112/keymem
codex plugin add keymem@keymem

Codex 会提示一次是否信任该钩子;请批准,否则推送路径将保持静默。插件默认使用本地 bge-m3 嵌入(首次运行时会自动下载约 570MB,无需 API 密钥)。如需使用 OpenAI 嵌入,请改用下方的手动配置——插件 MCP 服务只能读取其声明的环境变量。同步钩子最多等待 /inject 9 秒(设置 KEYMEM_HOOK_TIMEOUT_MS 可调整);触发回忆时,本地嵌入和重排可能会让提示词处理多耗费几秒。

Claude Code(手动配置)
# OpenAI embeddings
claude mcp add keymem -e OPENAI_API_KEY=your-key -- npx -y keymem

# Local embeddings (no API key required) — bge-m3 recommended (auto-downloads ~570MB on first run)
claude mcp add keymem -e EMBEDDING_BACKEND=local -e LOCAL_EMBEDDING_MODEL=bge-m3 -- npx -y keymem
Codex CLI(手动配置)
# OpenAI embeddings
codex mcp add keymem --env OPENAI_API_KEY=your-key -- npx -y -p keymem@latest keymem-shim

# Local embeddings (no API key required)
codex mcp add keymem --env EMBEDDING_BACKEND=local --env LOCAL_EMBEDDING_MODEL=bge-m3 -- npx -y -p keymem@latest keymem-shim

请使用 keymem-shim 入口点(而非裸用 keymem):它会运行推送路径钩子所使用的共享守护进程。对于钩子,请将以下内容添加到 ~/.codex/config.toml:

[[hooks.UserPromptSubmit]]
[[hooks.UserPromptSubmit.hooks]]
type = "command"
command = "node /absolute/path/to/keymem/hooks/keymem-hook.mjs"
timeout = 10

Codex 只会转发其 MCP 条目中声明的环境变量,因此请通过 --env 传递所有 KEYMEM_* / SUPER_MEMORY_* 覆盖项。

就这么简单——回忆和记忆功能会立即生效。Agent 会在首次回复前调用 recall,使用 read_key/read_memory 导航,并通过 remember 保存。

如需在 Claude Code 中可靠地主动保存,请将以下内容添加到 ~/.claude/CLAUDE.md(MCP 提示词不会自动应用为 Claude Code 的持久指令):

## keymem

- Before the first reply and whenever the topic changes, call `recall` silently with short noun-keyword queries.
- Before ending every reply, check whether this turn revealed a durable fact: a name, preference, decision, correction, project fact, or goal.
- If it did, call `remember` or `remember_batch` silently in the same turn with 3-6 diverse keys. A durable fact left unsaved is a bug.
- Use `correct` when existing information changes. Save nothing only when the turn revealed nothing durable.
- Treat `read_memory` as retrieval, not confirmation. Use its `validity.status`: qualify `aging`, and never assert `stale` as current without checking an external source or asking the user.
- Call `confirm_memory` only after an explicit current user assertion, an authoritative current source, or direct observation — never merely because a read succeeded.
- Never mention memory lookup or saving to the user.

在 Codex 中,请将相同内容放入 ~/.codex/AGENTS.md。(插件安装会将其作为 keymem 技能提供,因此无需在此处重复添加。)

对于其他 MCP 客户端,请将 MCP 提示词 memory_system_prompt 纳入 Agent 的持久系统指令中。它要求在首次回复前进行 3 次盲目 recall() 调用——当没有钩子被动呈现内容时,这样做是合适的。如果你确实自行配置了 UserPromptSubmit 等效推送路径,请设置 KEYMEM_HOOK_INSTALLED=true,这样提示词就会根据已呈现的内容作出响应,而不是在此基础上盲目猜测(插件的 .mcp.json 会自动设置此项)。

手动配置 / 开发
git clone https://github.com/donggyun112/keymem
cd keymem
pnpm install

创建 .env:

OPENAI_API_KEY=your-openai-api-key
OPENAI_EMBEDDING_MODEL=text-embedding-3-small

或者使用本地嵌入(无需 API 密钥):

EMBEDDING_BACKEND=local
LOCAL_EMBEDDING_MODEL=fast-multilingual-e5-large  # default; best fit for Korean/multilingual keys
pnpm dev
# or:
pnpm build
pnpm start

要求:

  • Node.js 20+
  • 本地开发需要 pnpm
  • 使用 OpenAI 嵌入需要 OpenAI API 密钥;使用本地嵌入则需要 fastembed

功能

  • N:M 键/值图谱 — 记忆与用于索引它们的概念属于不同空间,并以多对多方式连接。一条记忆可通过多个键访问;一个键可关联多条记忆。
  • 由 Agent 驱动的“键 → 记忆 → 键”导航 — Agent 有意遍历图谱,而不是将其压缩成一次不透明的相似度搜索。
  • 联想式多跳回忆 — 通过沿共享键的链路遍历,找到嵌入距离无法关联的记忆。
  • 感知确认状态的新鲜度 — 读取操作会学习有用路径,但不会认证旧内容;明确的证据会根据四种衰减配置刷新事实。
  • 单步版本控制 — 更正会保留其直接前一版本,并记录被取代的内容。
  • 键类型 — concept 键按相似度匹配;name/proper_noun 键精确匹配,因此「동건」不会仅仅因为较短就匹配「뉴턴」。
  • 跨语言键合并(IDF) — 파이썬 和 Python 会归并到同一个规范集群中,而不是导致键空间碎片化。
  • 赫布式链接学习 — Agent 实际遍历的路径会得到强化(「共同激活,连接增强」),让有用的关联更容易访问。
  • 混合检索(可选直接模式)— 对键和内容进行 Kiwi BM25 检索 + 键嵌入 + 倒数排名融合,并结合深度/确认新鲜度调节和可配置的多跳扩展。
  • 交叉编码器重排序(核心默认配置)— bge-reranker-v2-m3 会对被动 Top-1 池和兼容的直接模式候选项重新评分。
  • 本地优先 — 所有数据都存储在本地 JSON 图谱中;无需外部数据库。支持 OpenAI 或完全本地的嵌入模型(自动下载)。
新鲜度与深度

每条记忆都有一个深度分数 0.0 → 1.0,但检索和确认是彼此独立的:

阶段深度行为
浅< 0.3明确确认较少;排名提升幅度很小。
中0.3–0.7已多次确认;排名适度提升。
深> 0.7已充分确认;排名提升幅度最大,但仍可更正,且会受时间影响。

read_memory() 会递增访问元数据,并可能强化所遍历的键边,但它不会更改深度、last_confirmed_at、confirmation_count 或新鲜度。只有 confirm_memory(memory_id, evidence) 才会刷新有效性并将深度提高 +0.05;可接受的证据包括用户明确且当前的陈述、权威的当前来源或直接观察。仅仅成功读取绝不算作确认。

新鲜度会根据记忆的 decay_profile,从 last_confirmed_at 起逐渐衰减:

配置默认半衰期适用场景
transient7 天临时计划或可用性等快速变化的状态
standard90 天一般事实;默认配置
stable365 天缓慢变化的事实
permanent不衰减刻意视为永恒或不可变的事实

每个记忆视图都包含一个 validity 载荷,其中包含 freshness、status(fresh、aging 或 stale)、age_days、last_confirmed_at、confirmation_count、decay_profile、verification_recommended 和 verification_required。fresh 表示新鲜度至少为 0.5;aging 表示新鲜度至少为 0.125 且低于 0.5;stale 表示低于 0.125,未经验证不得将其作为当前事实断言。衰减是柔性的:它通过 0.2 + 0.8 × freshness 降低排名,但绝不会删除记忆。TTL 仍是唯一的自动过期机制。

例如,使用 remember(..., decay_profile:"transient") 保存临时计划;用户明确表示计划仍然有效后,调用 confirm_memory(memory_id, evidence:"user")。不要仅仅因为 read_memory 返回了该记忆就进行确认。

关键类型

并非所有键都应采用相同的匹配方式。名称不应仅因语义相似而匹配——“동건”不应仅仅因为和“뉴턴”都是简短的韩语词就与之匹配。

类型匹配方式用途
concept(默认)嵌入相似度 ≥ 阈值(OpenAI 为 0.28/本地为 0.60)主题、类别、属性
name仅精确匹配人名
proper_noun仅精确匹配品牌、地点

当 name/proper_noun 键成为连接许多记忆的枢纽键时,还会受到 IDF 惩罚(×0.5),以防它们污染无关搜索。

版本控制(单步前序版本)
"user lives in Seoul"   (depth: 0.4 → weakened to 0.12, preserved)
        ↑ superseded by
"user moved to Busan"   (depth: 0.0, new)

keymem 会保留紧邻的前序版本,而不是将其覆盖。无论其层级多深,被取代的记录都会从活动检索中排除,新版本则成为当前版本。之后的更正会清除祖父版本,因此来源记录仅保留一个前序版本,而非完整历史存档。

键合并
Add key "파이썬"  → finds existing "Python" (similarity 0.87 > threshold 0.85)
                 → reuses existing key instead of creating duplicate

防止键空间碎片化。不同语言或不同表述中的同一概念会保持统一。

Agent 驱动的检索(默认)

默认 MCP API 会从 Key Space 跨到一个 Value,同时保留显式图导航:

  1. recall(query, context) 会返回排序后的键簇,以及最强键下排名第一的记忆。原始 context 用于在该键内为记忆排序。
  2. 记忆是一个被动预览,包含 validity、matched_key 和所有 connected_keys[].key_id。它不会增加访问次数或深度、强化链接、学习别名或确认时效性。
  3. Agent 会检查问题需要哪些事实。如果预览中包含这些事实,就可以使用预览;否则,它会通过 read_key(key_id, missing_fact_query) 跟进某个关联键以查找缺失事实,或直接检索该事实。当证据完整或没有剩余的有希望路径时,Agent 就会停止。不会自动注入后续记忆。
  4. read_memory(memory_id, via_key_id) 仍是显式的完整读取路径。它会更新访问元数据,并且只强化经过的边;它不会确认或加深记忆。

语义上合并的键会作为别名保留在同一个规范键簇中(例如 Python + 파이썬)。推荐的 bge-m3 配置默认启用保守的短键合并;可通过 KEYMEM_SHORT_KEY_MERGE 覆盖或禁用该功能。至少关联三个活动记忆的键会作为枢纽呈现,并附带 is_hub、memory_count 和 specificity 元数据,而不会被 IDF 隐藏。可通过 KEYMEM_KEY_HUB_MIN_LINKS 覆盖枢纽阈值。

关联式回忆引擎(库 API,由 recallInject 在内部使用)

MemoryGraph.recall() 是单次调用的排序检索路径,由 recallInject() 使用,也可供库调用方使用。它通过 倒数排名融合(RRF_K = 60)组合两条候选路径:

  • Kiwi BM25: 使用 Node 中官方的 kiwi-nlp WASM 分词器,对记忆键和内容进行词法搜索。韩语形态会在本地分词;无需 Python 进程。
  • 键匹配: 查询嵌入与概念键匹配;精确名称和别名提供字面入口。键链接会引导至候选记忆。

记忆正文没有嵌入。交叉编码器重排序仍处于启用状态,并直接为查询—内容对评分。图扩展、深度和确认时效性仍会影响排序。默认 MCP recall 工具使用显式键导航,因此 Agent 可以有意驱动后续跳转。

Hebbian 链接学习

读取完整记忆是一种写入,而不只是读取。在默认流程中,recall() 和 read_key() 是只读操作;read_memory(memory_id, via_key_id) 会重塑所选路径:

  • 遍历的 via_key_id → memory_id 链接会得到强化(+0.1,上限为 3.0)。
  • 完整读取会递增访问元数据,但既不会改变深度,也不会改变 last_confirmed_at,因此 永远不会刷新新鲜度。随着时间流逝,新鲜度会持续衰减。有证据支持的 confirm_memory() 会增加深度,并通过推进 last_confirmed_at 刷新新鲜度。

强化仅作用于 Agent 实际遍历的键,而不是附加到记忆的所有键。这就是字面意义上的 Hebbian 规则(“共同激活,共同连接”),可防止通过不同概念访问记忆时,无关关联也随之增强。权重被限制在 [0.1, 3.0]。

链接权重会影响 read_key() 的排序,因此反复选中的路径会变得更容易访问。recall()/recallInject() 保留原有的匹配链接强化和已探索链接衰减行为。


架构

│                      Key Space                          │
│   [name] [동건] [programming] [python] [fruit] [red]   │
│      ↓      ↓         ↓           ↓       ↓      ↓     │
│   [vec]  [exact]    [vec]       [vec]   [vec]  [vec]   │
                         │ N:M links
                         ↓
│                     Value Space                         │
│   "user's name is Donggeon"     depth: 0.85  (deep)    │
│   "user likes Python"           depth: 0.30  (medium)  │
│   "user likes strawberries"     depth: 0.05  (shallow) │

默认 MCP 导航:

  1. 嵌入查询,并匹配规范键概念和精确别名。
  2. 返回包含 memory_count、is_hub 和 specificity 的键集群;不返回记忆内容。
  3. 在 read_key() 中,根据链接权重、深度和确认新鲜度对所选键的记忆句柄排序。
  4. 通过 read_memory() 返回完整内容和相邻键集群;仅当提供 via_key_id 时,才强化遍历的边。
  5. 重复调用 read_key(next_key_id),有意遍历图。

recall() 算法(混合式,可配置为 1–5 跳;默认为 2;由 recallInject() 在内部使用):

  1. 使用 Kiwi 对查询和记忆键/内容进行分词,然后检索 BM25 候选项。
  2. 使用查询嵌入和字面名称/别名匹配来匹配概念键,然后沿其记忆链接继续检索。
  3. 使用 RRF(RRF_K = 60)融合词法排名与键路径排名。
  4. 应用深度和确认新鲜度,并在配置的跳数范围内通过共享键和显式 related_to 链接进行扩展。
  5. 使用交叉编码器对候选项重新排序,并返回排序后的结果。匹配链接强化和已探索链接衰减保留其原有行为。
存储和重复项处理

只有键会保留嵌入向量。加载时会忽略现有记忆正文的向量,并在下一次成功保存后将其省略;键向量、记忆文本、链接和历史记录都会保留。如果需要使用旧版本重新打开存储,请在升级前备份。

重复项检测会在现有命名空间和活动记忆规则范围内,对规范化后的记忆文本进行精确比较。释义内容会作为独立记录保存。自动确认要求规范化后的话语文本与正文文本完全一致。新记忆不会根据正文向量相似度获得链接或矛盾标记;请提供键和显式关系,并对已变更的事实使用 correct。现有显式链接和已记录的矛盾仍可读取。

键相似度阈值

嵌入阈值适用于键,而非记忆正文。src/embedding.ts 中的 getThresholdProfile() 提供特定于模型的默认值;使用 KEYMEM_KEY_RECALL、KEYMEM_KEY_MERGE、KEYMEM_KEY_AUTOLINK(自动键别名提升)和 KEYMEM_SHORT_KEY_MERGE 调整键路由和规范化。未经校准的模型会在发出警告后回退到 BGE 配置。

旧版正文余弦设置(KEYMEM_CONTENT_RECALL、KEYMEM_CONTENT_RECALL_SHORT、KEYMEM_MEMORY_DEDUP、KEYMEM_CONTRADICTION、KEYMEM_MIN_SCORE、KEYMEM_GATE_Z 和 KEYMEM_KEY_GATE)不再控制记忆检索或写入。BM25 分数、键余弦分数、RRF 排名和交叉编码器分数的尺度各不相同;不要将旧的正文余弦阈值用作词法相关性阈值。

环境变量默认值说明
KEYMEM_AUTOKEYtrue自动键自我修复:从实际使用中学习缺失的搜索词。设为 false 可禁用。
KEYMEM_AUTOKEY_PROMOTE_N3将查询并入键空间前,必须经过的 (key, query) 对路由确认选择次数。
KEYMEM_AUTOKEY_CONFIRM_FLOOR0.45可用于路由确认学习的查询↔键余弦相似度下限。通过同一个键重复选择,可以学习低于门限的查询别名;这只确认路由,不代表内容仍然新鲜。调低(例如 0.40)可纳入更多处于临界值的释义;设为 ≥ 检索阈值可禁用此功能。
KEYMEM_AUTOKEY_MAX_ALIASES8每个键最多提升的已学习别名数。
KEYMEM_AUTOKEY_PRUNE_AGE2592000从未命中的已学习别名在守护进程自动 TTL 清理中被清除前的秒数(30 天)。
KEYMEM_CLEANUP_INTERVAL_MS3600000 (1h)守护进程自行清理过期记忆/别名的频率。stdio 路径则在启动时清理一次(没有可供安排清理的长期运行循环)。
KEYMEM_DECAY_TRANSIENT_DAYS7transient 记忆的半衰期(天)。必须是有限且大于零的值。
KEYMEM_DECAY_STANDARD_DAYS90默认 standard 配置的半衰期(天)。必须是有限且大于零的值。
KEYMEM_DECAY_STABLE_DAYS365stable 记忆的半衰期(天)。必须是有限且大于零的值。

对于跨语言检索,请保留多语言键和别名(例如 딸기 和 strawberry)。仅搜索正文中的词汇不会翻译查询。


MCP 工具

工具集包含 10 个工具:

工具描述
recall(query, top_k?, namespace?, context?, explain?)返回排序后的键,以及一条被动的 Top-1 记忆,其中包含 validity、matched_key 和 connected_keys。explain:true 还会返回命名空间计数。
browse_keys(namespace, hubs_only?, limit?, offset?)在 recall 未命中条目时,浏览命名空间中的活跃键词汇表,优先显示枢纽键。
read_key(key_id, query?, namespace?, limit?, offset?)列出与某个键关联的已排序记忆 ID 和元数据。传入原始查询可按相关性排序;支持对枢纽键进行分页。
read_memory(memory_id, via_key_id?, namespace?)读取完整记忆内容、关联键和 validity。更新访问记录;如果提供了 via_key_id,还会更新经过的那条边;绝不会确认或加深记忆内容。
confirm_memory(memory_id, evidence, namespace?, source?)在用户明确提供证据、提供权威来源或直接观察后,刷新当前记忆的新鲜度并加深记忆。仅读取不算证据。
remember(content, keys, key_types?, namespace?, ttl_seconds?, decay_profile?, related_to?)保存包含关键概念的记忆,可选设置 TTL,以及 transient、standard、stable 或 permanent 衰减配置。
correct(memory_id, content, keys?, key_types?, ttl_seconds?, decay_profile?, related_to?)进行版本化更新。保留紧邻的前一个版本,但将其设为非活跃;未指定的 TTL/配置继承自前一个版本。
dismiss(memory_id, key_id, namespace?)负面反馈:事实本身没问题,但不应通过此键显示。削弱这条边(设有下限,不会断开),并取消其待处理的别名学习。
forget(memory_id)永久删除
remember_batch(items)保存多条记忆;每个条目都接受 ttl_seconds 和 decay_profile

TTL 已过期的记忆以及键/记忆/链接计数不再是面向 Agent 的工具:守护进程会自行清理过期记忆(启动时执行一次,之后每隔 KEYMEM_CLEANUP_INTERVAL_MS 执行一次;stdio 路径则只在启动时清理一次);键/记忆/链接统计数据已在每轮对话中被动注入 SERVER_INSTRUCTIONS,因此单独调用 memory_stats() 纯属重复。

分数包含 score_kind。键召回会通过键余弦相似度或有界词法相关性提供 key_relevance。content_relevance 是有界 BM25(s / (1 + s)),不是余弦相似度。read_key 使用 within_key_rank;有交叉编码器时采用交叉编码器相关性,否则采用有界词法相关性。直接召回使用 rrf_fused_rank;RRF 的 rank_score 是排名融合值,而非相似概率。

此外,还提供了 memory_system_prompt MCP 提示词模板——将其纳入提示词,指示 Agent 静默召回、使用多样化的键,并且绝不向用户提及记忆系统。


本地嵌入模型

**BGE-M3(推荐,支持多语言)——自动下载:**设置 LOCAL_EMBEDDING_MODEL=bge-m3(别名:bgem3、baai/bge-m3、fast-bge-m3)。首次使用时,如果模型不存在,会自动获取——量化 ONNX 模型(约 570MB,来自 onnx-community/bge-m3-ONNX)以及分词器/配置(来自 BAAI/bge-m3)——并缓存到 ~/.keymem/models/bge-m3。无需手动下载:

EMBEDDING_BACKEND=local
LOCAL_EMBEDDING_MODEL=bge-m3
# optional — point at an existing model dir to skip the download (backward compatible):
# LOCAL_EMBEDDING_MODEL_PATH=/absolute/path/to/model-dir   # dir with model.onnx + tokenizer files
# LOCAL_EMBEDDING_MODEL_FILE=model.onnx                    # optional; default is model.onnx
# optional: KEYMEM_EMBED_THREADS=4                         # ONNX intra-op threads (default: a quarter of the machine, max 6)

首次运行时会下载约 570MB,之后会重复使用缓存。如果 LOCAL_EMBEDDING_MODEL_PATH 已包含模型,则会直接使用,不进行下载(部分目录会自动修复——仅获取缺失文件)。在线 API 后端(OpenAI)和 fastembed 内置模型不受影响。

**bge-m3 在自己的 ONNX 会话中运行,不通过 fastembed。**输入使用实际的词元长度。其向量空间指纹为 local:bge-m3+nopad;指纹变更会触发键向量迁移。BENCHMARKS.md 中较早的填充/延迟和检索测量结果描述的是先前的正文嵌入引擎。 交叉编码器重排(核心功能): 默认 recall MCP 工具的 Top-1 路径会使用 bge-reranker-v2-m3,按照候选池中最强的键重新评分。recall()/recallInject() 的结果也会重新排序。该模型(约 570 MB,量化版)会在首次使用时自动下载,并缓存于 ~/.keymem/models/reranker。

# optional: KEYMEM_RERANK=false              # disable the core reranker
# optional: KEYMEM_RERANK_MODEL_PATH=/dir    # use an existing model directory
# optional: KEYMEM_RERANK_POOL=30            # candidates re-scored (default 30)
# optional: KEYMEM_RERANK_THREADS=4          # ONNX intra-op threads (default: a quarter of the machine, max 6)

默认启用。如果模型无法加载,recall 会回退到融合排序。查询分解仍由调用方负责。

任务条件证据选择: 扩展召回仍保持关联式,但对于比较类查询,会在最终证据边界进行投影,使两个具名实体都获得与任务相关的支持。KeyMem 会在移除实体名称后推导比较维度(例如 nationality 或 founded),保留图候选池和关联分数不变,并保留原始关联胜出项以供强化。此功能默认启用;可通过 KEYMEM_TASK_EVIDENCE_SELECTION=false 禁用。

重排器未找到门限(KEYMEM_RERANK_MIN_SCORE): 在 recall()/recallInject() 中,如果交叉编码器的最高 logit 低于此下限,则拒绝完整结果。

KEYMEM_RERANK_MIN_SCORE=0   # reject when top rerank logit < 0 (bge-reranker-v2-m3 scale)

⚠️ 注意事项。(1)默认未设置,即不启用门限。(2)logit 的取值范围取决于模型;0(≈ sigmoid 0.5)适用于 bge-reranker-v2-m3(实测:同语言的已找到结果约为 +3.9,未找到结果约为 −5 至 −6)——使用其他重排器时请重新校准。(3)仅适用于同语言内容。 即使相关,跨语言相关性 logit 也可能偏低(KR 查询 ↔ EN 记忆约为 −5.4),因此门限会在文字体系不匹配(KR↔Latin)时自动绕过,以避免误拒跨语言命中——这意味着跨语言内容必须可通过双语键访问(["Jiwoo","지우"]),而跨语言“未找到”的精确度是已知限制。如果无法为键添加双语标记,请关闭此功能。

前缀行为: BGE-M3 不使用 passage:/query: 前缀——嵌入会原样传入。所有其他本地模型(e5、BGE-en、MiniLM)仍会照常使用前缀。

多语言键嵌入: 仍支持 bge-m3。历史模型比较无法证明当前 Kiwi BM25 架构的质量或未找到行为。

如果未设置 OPENAI_API_KEY,且未设置 EMBEDDING_BACKEND,服务器会自动使用本地 fastembed 后端。 仅用于英文或希望降低本地资源占用时,请设置 LOCAL_EMBEDDING_MODEL=fast-bge-base-en-v1.5 或 fast-bge-small-en-v1.5。

安全切换后端。 图会记录嵌入指纹(后端 + 模型 ID),用于标识嵌入所在的向量空间。启动时,如果当前后端的指纹或维度与存储值不同,图会自动迁移——使用新后端重新嵌入每个键,同时保留内容、链接、深度和访问历史(并先写入 graph.json.bak.* 备份)。指纹很重要,因为两个模型的维度可能相同,但生成的向量并不兼容(例如,fast-multilingual-e5-large 和 bge-m3 均为 1024 维);仅检查维度无法发现这种切换,会悄然破坏所有相似度计算。可通过 KEYMEM_AUTO_MIGRATE=false 禁用。通过 OpenAI 重新嵌入会产生一次性 API 费用,费用与键的数量成正比。

将预指纹(旧版)图迁移到维度相同的其他模型。 指纹机制启用前写入的图没有记录向量空间,因此无法自动检测切换到维度相同的其他模型。设置 KEYMEM_FORCE_REEMBED=true 并启动一次,即可无条件重新嵌入并写入指纹;随后请移除此设置(若一直保留,每次启动都会重新嵌入)。从现有 e5 图迁移到 bge-m3 时,正需要执行这一次性操作。


数据存储

所有数据均存储在本地。无需外部数据库。

~/.keymem/
└── graph.json              # canonical keys, aliases, memories, weighted links

设置 KEYMEM_DATA_DIR 以使用其他存储目录。

将记忆关联到其来源对话。 保存记忆时,keymem 会将当前主机会话信息写入其 source(host_session / host_agent / host_turn),以记录来源。keymem 不提供读取这些对话记录的工具——它只会在内部读取记录,以确定当前会话。各操作系统的存储位置会自动检测,并遵循各 Agent 的环境变量覆盖设置:

  • Claude Code — ~/.claude/projects/**/{session_id}.jsonl($CLAUDE_CONFIG_DIR)
  • Codex — ~/.codex/sessions/**/rollout-*-{session_id}.jsonl($CODEX_HOME)

访问受到限制。 对话记录存储在本地,可能包含敏感历史记录,因此只有在确认当前身份是所有者的个人本地 Agent 时,keymem 才会读取记录并写入来源信息——也就是说,已识别的主机注入了其会话环境变量(CLAUDE_CODE_SESSION_ID / CODEX_THREAD_ID),或你明确设置了 KEYMEM_TRANSCRIPT_ACCESS=true。否则(普通服务、远程部署、非所有者 Agent 或自定义 Agent)保存的记忆不会关联主机。设置 KEYMEM_TRANSCRIPT_ACCESS=false,即可在主机 Agent 环境下也强制禁用此功能。

当前会话通过以下两种方式确定:

  1. 确定性方式 — 主机会将其会话 ID 注入所启动的每个 MCP 服务,keymem 会直接读取该 ID:Claude Code → CLAUDE_CODE_SESSION_ID,Codex → CODEX_THREAD_ID(与 rollout 文件的会话 ID 相同)。关联精准无误,不需要猜测。
  2. 启发式回退 — 对于不提供会话 ID 的主机(例如 Claude Desktop),keymem 会使用最近修改的对话记录(并设有过期保护)。

基准测试

以下是对先前正文嵌入引擎的历史测量结果:在 HotpotQA 桥接问题上(使用真实外部多跳数据和标准答案标签,不依赖大模型评判;密钥由从未见过问题或答案的独立子 Agent 盲生成),键图检索到两个标准答案支持段落的比例为 63%,高于扁平语义检索的 53% 和词法检索的 35%(分别高出 10 个百分点和 28 个百分点)——这正是该方法旨在解决的关联但不相似场景。(如实说明:我自己手动推导的密钥将结果夸大到了 78%/60%;在非多跳的“比较”问题上,键图的效果略差;这衡量的是检索召回率,而非答案准确率。)读取路径的复杂度也是 O(1)(read_memory 的 p50 延迟:500 条记忆时约 45 毫秒 → 约 0.01 毫秒)。完整方法和注意事项见:BENCHMARKS.md。这些分数并不能验证当前的 Kiwi BM25 + 键嵌入引擎;目前尚未确定其生产环境质量评分。


限制

  • 线性扫描 — 适用于个人使用(约 10k 条记忆)。计划集成 FAISS/ChromaDB,以支持更大规模的数据。
  • 后续跳转的往返调用 — recall 会完成第一个 Key → Memory 跳转,但要沿着关联的键跳转到另一条记忆,仍需执行 read_key → read_memory。
  • 枢纽键的覆盖范围 — 宽泛的键可能关联许多记忆。read_key() 会对枢纽键的结果分页;Agent 必须选择继续翻页,还是沿着更具体的相邻键继续查找。
  • Agent 质量很重要 — 在 remember 中选择键会影响检索质量。调优系统提示词很重要。
  • 词法覆盖能力 — Kiwi BM25 本身不会翻译,也不会检测释义。多语言键、键嵌入和重排序有助于检索并排列候选项,但无法保证找回缺失的候选项。
  • 阈值校准 — 阈值针对各嵌入模型分别调优。新的或未经校准的模型会回退到 BGE 配置文件(并发出警告);可通过 KEYMEM_* 环境变量覆盖设置重新校准。

测试

pnpm test                        # unit tests (fast, no model download)
tsx test/scenarios.ts            # 21 end-to-end behavioral checks (local e5)
tsx test/robustness.ts           # threshold overrides + Hebbian pollution bounds
tsx test/migration.ts            # backend/dimension switch auto-migration (no brick)
tsx test/nhop.ts                 # N-hop chained traversal (recall hops parameter)
tsx test/depth-noise.ts          # deep-hop noise bounds + relative score floor
tsx test/live-multilingual.ts    # interactive multilingual recall demo

# Manual retriever-quality check (NOT part of pnpm test):
EMBEDDING_BACKEND=local npx tsx test/retriever-quality.live.ts
# For bge-m3: also set LOCAL_EMBEDDING_MODEL=bge-m3 LOCAL_EMBEDDING_MODEL_PATH=/abs/dir

scenarios.ts 和 robustness.ts 会测试实际的本地嵌入后端(直接/联想/跨语言召回、版本控制、深度增长、去重、TTL、Hebbian 学习、命名空间隔离)。引擎或模型变更后,必须重新运行测试,才能重新校准关键阈值并评估检索质量;历史上的通过结果无法验证此架构。


路线图

  • 支持 FAISS/ChromaDB,以满足扩展需求
  • 编码 Agent 配置文件(针对代码上下文采用不同的关键字策略)
  • 记忆导出/导入
  • 支持多用户

作者

donggyun112 — github.com/donggyun112

代码仓库:donggyun112/keymem · 欢迎提交 Issues 和 PR。

许可证

MIT © donggyun112

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。