mcpField
N
nexusmem

nexusmem

本地服务
nexus-mem.vercel.app记忆与知识154 周前更新

关于 nexusmem

nexusmem 将命令行历史、Git 变更和项目文档保存在本地 SQLite 中,供编码 Agent 按相关性检索。它记录命令退出码及来源,并可关联失败与后续修复;数据保留在本机。

主要功能

  1. 01索引命令、退出码、Git 差异和文档
  2. 02按相关性与时间排序检索
  3. 03在失败命令出现时提供先前经验
  4. 04可选关联失败与后续修复
  5. 05本地 SQLite 存储

适用场景

  1. 01找回曾尝试但未成功的命令
  2. 02为编码 Agent 提供项目历史上下文
  3. 03追踪失败命令及其修复过程

MCP 相关

README 未说明 MCP 能力。

配置

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

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

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

安装包
npmnexusmem0.10.5

工具

6

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

参数类型必填说明
asOfstring否ISO-8601 日期/时间。将结果限制为在此时刻或之前记录的节点——即“截至当时记忆中有什么”,而非“当时发生了什么”。省略则执行常规的不受限读取。
querystring是自由文本问题或搜索词
budgetinteger否返回上下文块中的最大 token 数。默认值为 2000。
allProjectsboolean否搜索此机器上 NexusMem 曾运行过的所有代码库,而不只是 projectRoot。当答案可能位于其他项目中时使用(例如,在其他地方解决过的模式、以前以相同方式失败过的工具)。每个结果都会标记其所属代码库。
projectRootstring是代码库根目录的绝对路径

详细介绍

NexusMem

CI npm npm downloads License: MIT Node yaminbkk/NexusMem MCP server Listed on AiList

NexusMem:init、sync --github,以及对本仓库自身历史记录的查询——找到一个真实问题、解决它的 PR,以及提交该 PR 的 commits

你的编码 Agent 可以读取 git log,却读不到你上周二尝试过但没成功的那四种方法。

NexusMem 会将你机器上实际发生的事情(Shell 命令及其退出码、细致到每个变更文件的补丁的 Git 历史、项目文档,以及可选的助手对话记录)记录到本地 SQLite 数据库中,并按需返回经过排序且符合令牌预算的相关内容。所有数据都保存在磁盘上。无需账号、云服务,也没有遥测。

值得关注的是 Shell 历史记录。Git 会告诉 Agent 哪些内容已发布,Shell 历史则会告诉它尝试过什么、按什么顺序尝试,以及哪些命令以非零状态退出。这些信息在其他地方无从获取,而且终端的滚动缓冲区一旦滚动覆盖,它们就会消失。

NexusMem 的工作方式:Git 提交、包含实际退出码的 Shell 命令、文档和 GitHub 讨论串流入本地 SQLite 索引;索引会返回经过排序且符合令牌预算的历史记录片段,让你的编码 Agent 能给出真实引用,而不是猜测

目录: 试用 · 精确捕获 Shell 命令 · 失败 → 修复链 · 检索方式 · 会话摘要 · GitHub issues 和 PR · 在 Agent 中使用 · 成本 · 过时信息与来源 · 局限 · 命令 · 跨项目回忆 · 磁盘上的数据 · 开发

试用

在任意 Git 仓库内运行:

npx nexusmem init
npx nexusmem sync

然后问它一个问题。以下是此仓库的真实输出,共 5 条结果中的前 2 条:

$ nexusmem query "windows spawn failure"

Relevant history for: windows spawn failure

- 2026-08-09 [observed] fix: distinguish a failed git spawn from "not a git repository"
  readRepoInfo collapsed three unrelated failures into one error: git running and reporting
  the path is not a work tree, git not being installed, and the process failing to spawn at
  all. Dogfooding hit the third case in two separate sessions...
- 2026-08-09 [authored] README.md — Before a tagged release
  - [ ] Retry on transient process-spawn failures on Windows

[observed]/[authored] 是来源标签(参见过时与来源) ——提交是直接观察到的事件,文档章节则是可能过时的书面声明。

提交和文档章节会相互排序,且不超出你设定的令牌预算。 输出过程中没有任何模型对内容进行摘要;排序器只决定不发送哪些内容。(一个可选来源——会话摘要——确实会在摄取时运行本地模型,但绝不会在输出时运行。你查询的始终是已存储的文本。)

要了解实际积累了什么,以下是此仓库运行两天后执行 nexusmem status 的结果:

527 node(s)  2026-08-08 .. 2026-08-09
       321  shell_command
       130  conversation_turn
        60  doc_section
        16  git_commit

16 条提交记录。321 条 Shell 命令。任何带终端的 Agent 都已经能检索到这些提交记录,其他内容则不行。

只有在使用 --conversation 同步此语料库时,才会出现 conversation_turn 这一行。助手转录内容是唯一默认关闭的来源,只有选择启用后才会收集,因为其中最可能包含粘贴进去的凭据。默认安装会索引 Git 提交、提交差异、Shell 命令和文档。

要求:Node 22 或更高版本,以及 Git。Node 20 无法使用,因为 better-sqlite3 没有为其提供预构建二进制文件,而且 Node 20 已于 2026 年 4 月停止维护。Ollama 是可选的,只影响语义搜索(见下文)。

可选:精确捕获 Shell 命令

抓取到的历史文件(PSReadLine、.bash_history、.zsh_history)能提供命令文本,除此之外信息不多。此钩子还能提供工作目录、退出代码和真实时间戳:

nexusmem hook install

它会包装现有的 PowerShell 提示符,而非替换它;可重复执行,且运行 nexusmem hook remove 即可彻底撤销。

退出代码正是值得安装此钩子的原因。失败的命令比成功的命令更能传递信号;没有此钩子,就无法区分两者。

可选:编码 Agent 尝试过什么

上面的 shell hook 只能看到你输入的命令。编码 Agent 运行的命令会通过它自己的工具执行,因此永远不会到达交互式提示符——这意味着 Agent 遇到的死路,也就是最值得记住的内容,恰恰是 NexusMem 看不到的。

nexusmem agent install     # writes Claude Code's hooks; --project keeps it to this repo

之后重启 Claude Code。从那时起,在运行过 NexusMem 的任何代码库中:

  • 它会记录 Agent 运行的每条命令、命令结果,以及运行命令前刚编辑过的文件,因此一次尝试会显示为“编辑了 a.ts,然后 npm test 失败”,而不是“npm test 失败了两次”;
  • 命令失败时,它会在当时告知 Agent:这条完全相同的命令以前是否在这里失败过、每次失败前编辑了什么,以及最终是什么修复了问题;
  • 会话开始时,它会在后台同步,并列出那些失败后没有记录修复方案的命令。

除此之外,它会保持安静。没有匹配项,就不会有消息;每个会话中,每次失败最多提示一条,每个会话最多提示五条;这条处理路径不会进行嵌入、调用模型或发起网络请求。如果 NexusMem 缺失或损坏,hook 不会打印任何内容并以 0 退出,Agent 会继续运行,就像没有安装它一样。

命令在写入前会经过脱敏,方式与 shell hook 完全相同;匹配则基于原始命令的哈希值,因此两条仅在机密内容上有所不同的命令绝不会被混淆。

nexusmem agent status 会显示 hook 是否已安装;nexusmem agent remove 会移除 NexusMem 自己的条目,并保留其他工具的 hook。

请在 Claude Code 运行所在的同一环境中安装。写入设置文件的内容是一条字面命令——包含 Node 可执行文件的绝对路径和指向此 NexusMem 副本的绝对路径——Claude Code 会将该字符串交给它自己的环境中的 shell 执行。如果 Claude Code 是 Windows 二进制文件,却在 WSL 中安装(或反过来),hook 虽然已配置、看起来也已安装,却不会捕获任何内容:路径在实际运行 hook 的环境中无法解析,而无法启动的 hook 不会在任何地方报错。nexusmem agent status 会指出已安装命令中有哪些路径在你查询的机器上不存在;如果它指向的 NexusMem 被移动、升级到其他位置或卸载,也会显示相同提示。

失败 → 修复链(可选加入)

nexusmem sync --link-failures

正常同步后,此功能会遍历每个失败的 shell_command(退出码非零),并通过两种独立的启发式方法查找后来解决问题的内容:其一,在同一项目和工作目录中查找一条文本完全相同、且在 24 小时内以 0 退出的后续命令(相同命令重试);其二,在附近的对话轮次或会话摘要中查找全文匹配度最高的内容,要求失败命令的每个重要词都出现,而不只是其中一个(对话桥接)。一次失败可能会通过其中任一种、两种或都不通过而建立关联。

查询结果会显示这两种关联。最初,对话桥接启发式只要匹配任意一个共同词就会建立关联;在这个仓库自身的真实历史记录中试用后发现,大约一半的关联都是错误的——像“npm”这样常见的共同词就足以将无关讨论关联起来。要求每个重要词都出现后,这个问题得到了解决:在同一语料中再次试用时,对全部结果(而非抽样结果)进行全文人工审核后,确认每条关联都是正确的,不只是摘要看起来正确。

当一条已关联的失败记录出现在结果集中时,它的修复记录会紧跟其后,并沿用失败记录自身的相关性分数,而不必单独匹配查询。这正是此功能的意义所在:查询某件事为什么失败时,不应该还需要单独猜出修复方案所用的词语。此功能也支持跨项目查询——query --all-projects 会使用记录该关联的项目自己的数据库,将失败与修复链起来,因为关联始终只属于发现它的项目。

$ nexusmem query "why did npm whoami fail"

- 2026-08-12 [observed] shell: npm whoami  (exit 1)
- 2026-08-12 [observed] shell: npm login   (exit 0)  -- linked as the fix

检索工作原理

每种来源都会规范化为相同的 MemoryNode 结构,因此提交记录、shell 命令和文档章节可以在同一标准下竞争。检索会通过 FTS5 执行 BM25;如果可以访问嵌入模型,还会通过 sqlite-vec 执行向量搜索,并使用 Reciprocal Rank Fusion 融合结果,只依据排名位置,绝不使用原始分数——BM25 的代价和向量距离处于彼此无关且没有上限的尺度上,只有位置是两者共有的标准。

随后,排名会将三个因素相乘:

score = relevance × signal^0.215 × recency^0.288

relevance 来自查询。signal(fix: 提交记录的排名高于 chore:;失败的命令排名高于成功的命令)和 recency 则是先验因素,在任何查询出现之前就已存在。每个因素的下限都是 [floor, 1],而不是 [0, 1],因此某个维度较弱不会让强匹配的分数归零。

指数会限制信号和时效性这两个因素合计能够推翻相关性的程度:在两者的整个取值范围内,最多相差 2 倍,并且这个上限共同应用于两个先验因素,而不是分别应用。这样设计是有意为之——分数会将两个先验因素相乘,所以即使分别把每个因素限制在 2 倍,二者仍可能合力推翻 4 倍的差距;这对在活跃工作日中创建的新近高信号提交记录影响最大。暴露出这个问题的情况是:两条互不相关、同日创建的 fix: 提交记录,排名竟高于实际回答查询的文档章节。完整推导请参阅 retrieval/rank.ts。

如果没有 Ollama,系统会跳过向量搜索,只使用 BM25——这是完全受支持的状态,并非降级状态;sync 和 query 都会成功,只是执行的工作更少。

会话摘要(可选,使用本地模型)

启用 sources.session.enabled 后,每个已结束的会话都会生成一个提炼后的节点,与原始对话并列保存——记录已作出的决定及其原因,而不是四十轮单独的对话。它使用本地 Ollama 聊天模型(默认是 qwen2.5:3b);不会自动下载任何内容,也不会将任何内容发送到本机以外。

nexusmem scan-session --dry-run

这会在脱敏和预算裁剪后,打印会话将使用的确切提示词,而不会调用模型。

有三项机制可以控制成本。会话只有在静默时间达到 settleMinutes(默认 30 分钟)后才会生成摘要,因此进行中的会话不会在每次同步时都重新生成摘要。提示词会经过哈希处理,如果哈希未变化,就完全跳过模型调用——在本仓库中,稳定状态下对 14 个已有摘要的会话执行同步只需 0.25 秒,且不会调用模型。maxSessions(默认 10)则限制每次运行中交给模型处理的会话数量;其余会话会标记为排队,并在下次同步时处理。

在 14 个真实会话上使用 qwen2.5:3b 测得的实际效果。 摘要本身效果不错:能够按提示词要求的形式,列出决定及其原因。标题则不太可靠——模型大约只有三分之一的时间能生成可用标题,其余时候会输出对话式开场白、多余的项目符号,或简单的“会话摘要”。这些标题都会被拒绝,并回退为开启会话的问题的第一行;即使不够精致,这一行也总是具体的。模型在处理长会话和非英语对话记录时,遵循提示词的表现最差。如果你很看重标题,可以改用更大的模型(qwen2.5:7b);请设置 sources.session.model。

GitHub issues 和 PR(可选)

启用 sources.github.enabled 后,此仓库的 github.com 远程端上的每个 issue 和 PR 都会成为一个节点(标题、首篇帖子和所有评论合并在一起),与讨论在 shell/对话历史中留下的原始内容并列。默认关闭——并非出于敏感性考虑,而是因为它是第一个具有实际外部依赖的来源:它通过 gh CLI 读取,因此需要安装并完成身份验证的 gh(gh auth login),而且会发起实时网络调用,而不只是读取磁盘上已有的内容。无论是仓库没有 github.com 远程端,还是 gh 未通过身份验证,都会静默跳过。

nexusmem scan-github

会预览一次同步将生成的节点,与其他 scan-* 命令相同。maxThreads(默认值为 100)和 maxCommentsPerThread(默认值为 100)限制单次同步的开销;since 会作为独立游标跟踪,因此重复同步时只会重新读取发生变化的讨论串。我们已用此仓库自身的 14 个真实 issue/PR 进行试用:摄取耗时不到一秒,查询“labelled retrieval regression corpus”时,正确地将 issue #8——提出该请求的 issue——排在第一位。

在 Agent 中使用

{
  "mcpServers": {
    "nexusmem": {
      "command": "npx",
      "args": ["-y", "nexusmem", "mcp"]
    }
  }
}

通过 stdio 提供三个工具:search_memory 返回打包后的上下文块,sync_project 负责摄取,get_status 报告当前记忆的内容。每个工具都需要显式传入 projectRoot,因为 MCP 工具调用不携带 shell 工作目录。如果尚未设置仓库,sync_project 会代为运行 init。

成本表现

这类工具中有两个数字容易混淆,因此这里将它们分开说明。

打包器效率是指排序器从自己的候选集中精简内容的比例。在此仓库的语料库中,这一比例为 81–84%。它有助于调优排序器,但不能用来说明你的账单会减少多少,因为基准是虚构的:如果没有 NexusMem,这些候选内容本来就不会进入你的上下文窗口。

端到端节省是指将 NexusMem 实际发送的打包上下文,与完整读取其排序结果中判定与查询相关的同一批文件相比,节省了多少。该数据通过 scripts/benchmark.ts(npm run bench)测得;任何人克隆此仓库并将其指向已同步的语料库,都可以从头重跑:

语料库提交数查询集相比完整文件内容相比这些文件的 git log -p
此仓库6216 条真实提示词,逐字取自本项目自身的历史记录95%(中位数 94%)98%(中位数 97%)
vitejs/vite9,56716 条机械抽样的查询——见下文99%(中位数 98%)~100%(中位数 ~100%)——见注意事项

两者都超过了最初设定的 >70% 目标(“相比发送完整上下文,减少 API 令牌开销”);vite 的测试则是首次在该目标始终声称适用的规模上进行测量。

引用这两个数字前,请先阅读测量方法——实际主张比表面看起来更有限:

  • 以 NexusMem 自身的排序结果为评估基准,而非外部标准答案:文件集由该查询对应的打包节点所涉及的文件构成。这衡量的是检索选出候选集后,打包步骤节省了多少;并不会独立验证该候选集是否正确。
  • 查询集是机械生成的,并非挑选出来的(见 scripts/benchmark.ts):vite 的查询集均匀抽样自说明充分的 fix/feat/perf/refactor 提交,以及包含理由的文档标题;此仓库的查询集则逐字复用了真实历史提示词,其中有几条是宽泛的任务说明,而非具体问题——这也是它的数据低于 vite 的部分原因。
  • git log -p 基准可能非常庞大——vite 的一条查询基准达到 750 万个令牌,因为其解析出的文件集中有个文件拥有这么长的历史记录。到了这种规模,“直接读取文件历史记录”根本不再是可行的替代方案。
  • 取代旧的 ~40% 数字:旧数字是针对此仓库的两个手动挑选查询,按未说明的基准手工统计得出的。它并非错误,只是说明不充分——现在以明确的方法和可复现结果的脚本取代了它。

有一样东西无法用百分比表示:Shell 命令和对话轮次没有廉价的 grep 等效方案。没有东西记录它们时,它们就会消失,而不只是变得更难查找。

如需了解这些数字与类似工具自身声明的比较,请参阅 docs/competitor-comparison.md(与 projectmem 对比)和 docs/competitor-comparison-yesmem.md(与 YesMem 对比,包括原生 Windows 支持与其文档所述的 WSL2 要求)。

在约 530 个节点的语料库上,预热后运行 10 次的 p50 延迟:

操作
BM25 检索(FTS5)~1.1 ms
Vector KNN(sqlite-vec)~3.2 ms
融合、排序、打包~0.6 ms
查询嵌入(本地 Ollama)~55–77 ms
端到端混合检索~56 ms

所有 SQLite 操作加起来约为 5 ms。此流程中唯一值得优化的是嵌入调用,而它运行在其他人的进程中。

时效性与来源

记忆层需要具备两项能力,而这个记忆层目前只部分具备:区分观察到的事实和猜测,以及在结论受到反驳后将其废弃。本节介绍已有的功能和缺失的功能。

每个节点都带有 provenance,这是在采集器摄取数据时为其设置的四级信任层级:observed(已落地的提交、Shell 命令的实际退出码)> authored(文档章节——人类自己写下的声明)> recorded(对话轮次——逐字记录,但讲的是事件而非事件本身)> derived(会话摘要——模型提炼的内容)。每条查询结果都会显示该层级作为标签,并影响检索权重——信任度越低,节点随时间推移在排名中的权重衰减越快。这个排序是设计主张;具体的衰减比例只是经验判断,并非经过测量的最优值。

nexusmem stale

列出年龄达到一定时长(默认 45 天以上)的非 observed 节点,且没有任何信息确认它们仍然成立——这是一种基于时间和来源的启发式判断,而非基于内容。它不会写入任何内容;哪些候选项确实有误,由你决定。任何 SLM 已标记的候选项(见下文)都会附上现有的 likely superseded by 建议——查看这些建议不花费任何代价,因此普通命令依然即时运行且可离线使用。

nexusmem mark-stale <oldNodeId> --supersedes <newNodeId>

将 newNodeId 关联为 oldNodeId 的替代节点。从此之后,排序器会降低旧节点的权重(旧节点仍可查询,只是通常会排在替代节点之后)——不会删除任何内容,这与 forget 不同。

nexusmem stale --check-contradictions

对于每个候选项,找到最相似的较新节点(本地嵌入搜索),并询问本地 SLM(默认使用 Ollama、qwen2.5:3b)它是否确实与较旧节点相矛盾——这是对实际内容的比较,而不只是看时间。匹配结果会在候选项下方显示为 likely superseded by <id> <title> -- <reason>。每项判断(无论结论如何)都会被缓存,因此已判断过的节点对不会再次发送给模型;除此之外不会写入任何内容——supersedes 仍由你通过 mark-stale 设置。

这也会在 sync 期间自动运行——每次最多进行 3 项新判断(可通过 .nexusmem/config.json 中的 contradictions 块配置;设置 autoCheck: false 可关闭),且只有在嵌入提供方本来就可访问时才会运行;由于有缓存,重复同步无需额外花费。

新建议和未处理建议会显示在同步摘要、nexusmem status(flagged 行)以及普通命令 nexusmem stale 中。

**此功能不会做什么:**它只是由一个小型模型对一对较旧和较新的节点做出是或否的判断,并非已验证事实——请将匹配结果视为需要核查的线索,而不是结论。它还只会将候选项与通过嵌入相似度找到的节点进行比较;如果某个听起来无关的节点与其矛盾,这种情况就不会被发现。全面的矛盾检测(而不仅是向量搜索碰巧找到的节点对)仍是一个未解决的问题,而且这里的任何功能都不会自动替代节点。

provenance 与 trust_state 是两个不同的问题:来源说明声明来自何处,但绝不表示是否有人核查过该声明。

nexusmem review <nodeId> --verify
nexusmem review <nodeId> --reject

记录你对某个节点的判断,与上文的 SLM 矛盾检查器相互独立(检查器只会写入建议,不会写入判断)。--reject 会降低节点在排名中的权重——与 mark-stale 一样,只降权、不删除;节点仍可查询,只是通常会排在更匹配的结果之后。此后,每条返回该节点的查询结果都会将这两种判断之一显示为 [verified]/[rejected] 标签。--verify 只是添加标签,不会提升排名。每个节点最初都处于 candidate 状态(无标签),直到经过审核;重新同步绝不会覆盖已经设置的判断。

何时会失效

  • 没有钩子的 Shell 历史记录没有作用域。 抓取的历史记录不包含目录上下文,因此会归到你运行 sync 时所在的仓库。只读取尾部的一段记录,因此只是近似结果,并非保证。
  • 日语和中文依赖向量处理阶段。 FTS5 的 unicode61 分词器会按空白字符拆分文本,因此没有空格分隔边界的语言无法获得有效的 BM25 召回结果。
  • 变基会遗留节点。 重写后的历史记录会使节点对应的提交变得不可达。这些节点描述的是真实事件,因此并没有错,但目前还无法定向清理。sync --rebuild 会强制重新扫描。
  • 多行 PowerShell 输入会被当作单独的命令读取。 在提示符处跨多行输入的函数不会被还原。
  • 如果两次同步之间历史记录文件的开头被截去,抓取回退机制生成的 ID 就会发生变化。 安装钩子即可解决。
  • 会话摘要标题取决于模型是否遵循指令, 而 3B 模型往往不会。回退机制会尽量让标题具体,而非笼统;预期情况请参阅上文。
  • 更改嵌入模型会重新嵌入所有内容。 两个模型生成的向量无法比较,而且 nodes_vec 不记录每行数据的来源,因此 sync 会全部丢弃并重建,而不是对混合数据进行排序。发生这种情况时会有提示。节点不会受到影响,BM25 也会始终正常工作。
  • 差异索引有范围限制,而且会有意舍弃部分内容。 首次同步会索引最近 200 个提交的补丁(后续同步只会遍历 cursor..HEAD);合并提交不会贡献补丁,因为其补丁仅以组合格式存在,而此解析器不会读取这种格式;此外还会跳过二进制文件、锁文件和构建输出,以免依赖版本更新淹没语料库。所有这些仍会记录为 git_commit 节点。超过 limits.maxBodyChars 的补丁会被截断,因此非常大型的更改末尾部分不会被索引。相关上限位于 config.json 的 sources.diff 下。
  • 跨项目召回更偏重广度。 每个仓库的结果会按排名融合,因此即使某个项目的最佳匹配质量一般,也仍会贡献一个排名第 1 的条目;而不同列表中的排名第 1 条目权重相同。即使某个仓库对你的问题没有太多相关内容,添加它仍会让它的一些结果占用预算。信号、时效性和预算会对此加以控制;没有针对每个项目的质量权重。
  • 项目注册表是索引,而非事实来源。 它可能指向已移动或已删除的数据库;这些情况会被报告并跳过,而不会被静默清理,因为驱动器未挂载并不代表项目已删除。
  • 对话分块尚未经过评估。 在标题边界处分割较长的回复确实带来了可衡量的改善,但从未经过系统测试。
  • 单个结果中,分块节点的同级节点数量上限未经调优。 conversation_turn 和 doc_section 都会将一条回复或一个文件拆分为多个节点;打包后的结果中,最多只能同时出现其中 2 个。实际发现:针对“token”的查询,排名前 12 的结果中有 9 个是同一条分节很多的回复中的不同片段,挤掉了真正回答问题的节点。上限设为 2 是基于判断,而非经测量得出的最优值;与上文排名先验预算的设定依据相同。
  • 先验预算的大小是基于判断的,而非经测量得出的最优值。 现在会联合限制先验项,而非逐项限制,这堵住了一个实际存在的 4× 缺口(参见排名部分);但 2× 预算本身从未使用带标签的相关性数据集进行调优,因为目前并没有这样的数据集。这是一个合理的常量,而非测量结果。经过测量的是其变化方向:针对本仓库自身记忆中的 4 个真实查询,改用联合上限后,有 3 个查询的答案所在章节排名上升(“为什么要在向量搜索之前使用 BM25”的原理章节从第 4 名升至第 1 名),且没有任何查询的正确首选结果被挤掉。
  • forget 仅针对单个仓库,而非全局生效。 它的拒绝列表保存在其运行所针对的那个 .nexusmem/memory.db 中(以及该仓库中同一作用域下的过时先验身份;--prune-source 也是如此)。如果某个值从多个仓库泄漏到 Shell 历史记录中,就需要在每个仓库中运行一次 forget——所有已同步项目之间没有共享的全机器级拒绝列表。
  • 拒绝列表不会自动随克隆或还原而保留。 .nexusmem/ 按设计被 git 忽略,因此 deny_list 不会随 git clone / git push 一起传输;但 Git 历史本身是全可移植的,每次克隆都会复制,新运行的 sync 也会据此重新生成数据。队友的全新检出、新设备或还原的备份都不会有任何保护:被遗忘的值会在首次同步时重新出现。已于 2026-08-17 在实际环境中确认,这并非仅根据代码推断。forget --export <path> / forget --import <path> 可解决此问题:导出会将有效条目写入纯文本 JSON 文件,你可以通过自行控制的渠道传输该文件(切勿使用 Git——该文件的敏感程度与其中保存的值完全相同);导入则会在新的检出中重新应用这些条目,并删除已重新同步的副本。此操作有意设计为手动执行,不会在每次 sync 时自动进行。

命令

init、sync、query <text>(添加 --as-of <date> 可进行双时间读取,见下文)、status(添加 --share 可生成适合粘贴到其他地方的纯文本摘要)、projects、mcp、forget <value>、 scrub-secrets(重新遮蔽 0.10.5 之前版本已存储的机密信息——数据库行、FTS 索引、嵌入向量和 shell hook 日志;默认试运行,使用 --yes 才会执行,并会先备份每个数据库; --all-projects 涵盖所有已注册的仓库)、 stale(添加 --check-contradictions 可进行本地 SLM 内容检查,见上文)、mark-stale <nodeId> --supersedes <newNodeId>、review <nodeId> --verify|--reject(记录对单个节点的人工判定,见上文)、precheck(仅提供提示——警告暂存文件中存在尚未解决的过往失败或近期高频变更;除非使用 --strict,否则以 0 退出)、hook install|remove|status(PowerShell 退出代码 hook)、hook git install|remove|status(git 提交前 hook,会在每次提交前运行 precheck),以及 hook git-post install|remove|status(git 提交后 hook,会在每次提交后于后台运行完整的 sync,包括嵌入处理——以分离进程运行,因此不会让 git commit 本身等待;连续多次提交会通过 sync --auto 的锁合并为一次同步,而不会堆积)。agent install|remove|status 用于管理上文介绍的 Claude Code hook;agent recall 和 agent session-start 由这些 hook 运行,无需手动执行。

此外,还有八种试运行预览(scan-git、scan-diff、scan-shell、scan-docs、 scan-conversation、scan-session、scan-github、scan-structure),不会写入任何内容,只会打印摄取操作将会生成的结果——前七种会显示节点及其信号评分,scan-structure 会显示导入图的边。这是针对真实仓库调整评分并确定是否进行变更的建议方式。添加 --json 可将结果传递到其他地方。

每条命令都可以使用 -C <path> 指定另一个仓库。执行 sync 时,--conversation 和 --github 可在单次运行中启用相应的可选数据源,但不会持久保存设置;--no-embed 会跳过向量处理;--link-failures 会构建上文介绍的失败 → 修复链;--rebuild 会删除项目的节点并从头重新摄取。

sync --prune-source <name> 会删除整个采集器数据源(例如 shell:pwsh);forget <value> 则是更细粒度的补充操作——删除所有匹配某个精确字符串(或 --regex 模式)的节点,并且写入一条持续有效的拒绝列表条目,确保该值永远不会被重新摄取,即便之后执行 sync --rebuild,重新读取仅追加的 shell-hook 日志或扫描完整转录记录也不例外。每次删除都会留下仅包含哈希值的墓碑,绝不会保留被遗忘的内容。两者默认均为试运行;使用 --yes 确认执行。forget --list 会显示有效条目;forget --export <path> / forget --import <path> 可将这些条目迁移到同一仓库的另一个检出副本(见上文的限制)。 此机制的存在原因见 docs/forget-mechanism.md。

跨项目回忆

query --all-projects 会搜索你运行过 NexusMem 的所有仓库,而不仅仅是当前仓库,并为每条结果标注其来源仓库:

$ nexusmem query --all-projects "why was the retry budget raised"
scope   2 project(s): NexusMem, uploader

- 2026-08-12 [observed] [uploader] fix: raise the retry budget after the S3 upload timeouts
- 2026-08-12 [observed] [uploader] retry.ts @ 8d0f98b — fix: raise the retry budget after the S3 upload timeouts
  @@ -1 +1 @@
  -export const RETRY_BUDGET = 3;
  +export const RETRY_BUDGET = 5;
- 2026-08-09 [observed] [NexusMem] fix(git): retry a transient failure to spawn git

双时态读取

每个节点都有两个时钟:ts 是事件本身的时间(“当时发生了什么”),created_at 是存储实际记录该事件的时间(“当时存储中保存了什么”)——通常这两个问题的答案相同, 但如果同步延迟、几周后才补录事件,或队友的克隆一次性追上进度,情况就不同了。默认情况下, query/search_memory 回答第一个问题;--as-of <date> 则切换到第二个:

nexusmem query "why does the ranker cap joint priors" --as-of 2026-08-10

只会考虑在该时刻或之前记录的节点,即使它们描述的事件更早也一样。没有与之对应的写入操作—— 这是对 created_at 的读取时过滤,而不是快照,也无法查询后来已变更的值,因为节点只写入一次 (关于什么会改变节点的权重而非其记录,参见过时状态与来源)。

数据库仍按仓库分别存储——没有共享的全局存储,删除某个仓库的 .nexusmem/ 仍只会移除该仓库的记忆。其他仓库之所以能被找到,是因为 ~/.nexusmem/projects.json 中有一个简单索引,由 init 写入,并由每次 sync 刷新。nexusmem projects 会显示索引中的内容, --prune 会移除数据库已不存在的条目。

跨仓库排名使用每个项目的倒数排名融合,而非原始 BM25,因为 BM25 的成本是根据各自的语料库计算的, 在包含 50 个节点和 50,000 个节点的数据库中代表的含义不同。此方案的取舍详见失效场景。

MCP search_memory 工具接受相同的开关:allProjects: true。

磁盘上的内容

<repo>/.nexusmem/
  .gitignore     '*' — the workspace ignores itself, so init never edits a file it doesn't own
  config.json    validated on read; a corrupt config fails loudly rather than silently
  memory.db      SQLite in WAL mode

~/.nexusmem/
  projects.json      which repositories exist, for cross-project recall; a corrupt one reads as empty
  shell-history.jsonl  the hook's log, if you installed it

NEXUSMEM_HOME 可覆盖用户范围的目录。

节点 ID 根据 sha256(projectId + kind + naturalKey) 进行内容寻址,因此运行两次 sync 不会产生重复项,即使游标丢失,摄取也能保持正确。项目标识在存在规范化源 URL 时取自该 URL, 否则回退到绝对路径,因此同一仓库的两个克隆会共享一个记忆命名空间。

删除 .nexusmem/ 不会造成任何无法通过 sync 重建的数据丢失。

状态

摄取、混合检索、预算受限的内容打包和 MCP 服务均已可用,并通过了 701 项测试,测试运行于 Linux 和 Windows, 覆盖 Node 22 和 24。

开发

npm install
npm run typecheck
npm test
npm run build

测试以行为验证为主,而非基于快照,其中有几项是针对特定已观察到故障的回归测试。tests/git-errors.test.ts 会注入一个伪造的 spawn,以测试 Windows 进程启动故障;这类故障无法按需触发。

欢迎提交 Bug 修复、测试覆盖和小型聚焦功能——工作流程请参阅 CONTRIBUTING.md,也可以浏览标记为 good first issue 的问题,选择范围明确、独立完整的内容开始贡献。

关于构建过程

这个项目最初是一项实验,旨在探索面向编码 Agent 的本地上下文记忆引擎是否可行,并使用 Claude Code 进行原型开发。 代码通过 AI 辅助工作流编写;架构、设计决策和规格说明均由人主导。

有必要坦率说明这一点,因为它应该影响你阅读代码的方式,而不是影响你是否信任它。我们真诚欢迎审计、修正和 PR; 提交历史也刻意详细记录了各种设计背后的原因,包括先前假设被证明有误的情况。

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

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

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