内容
# WikiMoth
<p align="center">
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/wikimoth-logo.png" width="88" alt="WikiMoth">
</p>
### 连接点。同样的方式,每次都是这样。
**[wikimoth.com](https://wikimoth.com)** · `pip install wikimoth`
[](https://github.com/juliangeymonat-jpg/wikimoth/actions/workflows/ci.yml)
[](https://pypi.org/project/wikimoth/)
[](https://www.npmjs.com/package/wikimoth-mcp)
[](LICENSE)
**适用于Claude和代理的确定性、token最小化、可审计的内存。** 将WikiMoth指向一个包含`[[wikilink]]`注释的文件夹(例如Obsidian仓库或Claude自己的内存文件夹),它会跟随作者链接到答案,显示确切的笔记链,并为读者提供大约99%的token减少。纯Markdown,无GPU,无向量数据库,检索循环中无LLM。
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/connect-the-dots.png" alt="一个问题,三个跳转:WikiMoth跟随您的作者链接到包含答案的笔记。平面搜索在第一个关键词匹配处停止。">
```bash
pip install wikimoth
wikimoth install # 捕获:将您的Claude Code会话转换为[[wikilink]]仓库
wikimoth serve # 浏览仓库 + 查看“是什么内存提供了这个答案”
```
---
## 为什么不让Claude管理自己的上下文?
我们对这一点进行了基准测试。一个浏览笔记文件夹并修剪自己的上下文的代理可以达到**相同的答案**,包括多跳(我们的运行中为12/12)。但是,它需要付出代价:**4到6个模型往返**和大约**10倍的计费token**,因为它在每一步都重新发送增长的转录记录。WikiMoth在**一次确定性传递中**检索相同的笔记链,**没有模型在循环中**,并显示确切的笔记链。
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/agent-vs-wikimoth.png" alt="相同的答案,工作量更少:让Claude修剪自己的上下文需要4到6个模型往返,大约10倍的计费token和大约9秒的回答时间;WikiMoth在毫秒内通过一次确定性检索传递完成,无需模型调用,带有可审计的笔记链。两者都达到正确的答案12/12。">
<sub>实际运行,Claude Sonnet 4.6,12个多跳问题,可重复的仓库。计数器上的~10倍是一个读者双方的计数器;它是特定于语料库的,不是普遍规律。用`python scripts/run_agentic_benchmark.py`复制它。</sub>
---
## 为什么选择WikiMoth
大多数代理内存要么是*粘贴整个笔记文件夹到上下文中*(昂贵,模型在中间迷失)要么是*LLM汇总的相似性搜索*(有损,不确定:相同的问题下周可能返回不同的内存)。WikiMoth采取了一种不同的策略:您的笔记**是**存储(纯Markdown),图是**作者**(您的`[[wikilinks]]`,无需嵌入训练或漂移),检索是**代码,而不是模型**,因此它是可重复的,您可以准确看到每个笔记为什么被选择。
| | **WikiMoth** | BM25 | 向量RAG | claude-mem | LLM Wiki(Karpathy) |
|---|:--:|:--:|:--:|:--:|:--:|
| 连接点(多跳超过作者`[[链接]]`) | ✅ | ❌ | ❌ | ❌ | ✅ *(代理)* |
| 确定性检索(相同查询→相同结果) | ✅ | ✅ | ✅ | ❌ | ❌ |
| 检索无需LLM调用 | ✅ | ✅ | ✅ | ~ | ❌ |
| 可审计的笔记链(哪些笔记产生了答案) | ✅ | ~ | ❌ | ❌ | ~ |
| 直接查找召回@8(真实仓库) | 1.00 | 1.00 | 1.00 | ~ | ~ |
| 无GPU / 无向量DB / 无索引构建 | ✅ | ✅ | ❌ | ~ | ✅ |
| 纯Markdown存储(在任何编辑器中打开) | ✅ | ~ | ❌ | ❌ | ✅ |
| token最小化vs转储仓库 | ✅ −99% | ✅ −99% | ✅ −99% | ✅ | ~ |
| 确定性,无API的自动捕获 | ✅ | ❌ | ❌ | ❌ | ❌ |
| **不使用LLM的卫生**(冲突·重复·过时·取代) | ✅ | ❌ | ❌ | ~ | ❌ |
<sub>LLM Wiki *遵循*链接并跳过向量数据库,像WikiMoth一样,但一个**LLM编写和读取** wiki,因此检索是代理的(每个召回的LLM调用,不可重复),而其策划的页面更丰富。`~` = 部分 / 未独立基准。</sub>
优势在于**组合**,而不是更高的召回率:WikiMoth *匹配*基本搜索并添加连接点 + 确定性 + 审计轨迹 + 纯Markdown存储。参见[诚实的限制](#诚实的限制)以了解它与竞争对手的联系和区别。
### 与Karpathy的LLM Wiki比较
WikiMoth与Andrej Karpathy的*LLM Wiki*模式共享相同的底层:纯Markdown `[[wikilink]]`注释,无向量数据库,但采用**引擎**。在LLM-Wiki模式中,**LLM编写和读取** wiki:丰富的、引用来源的页面,但召回是**代理的**(它会花费LLM调用,并且路径不可重复)。WikiMoth在**代码中计算边**,并使用**固定算法,无LLM调用**进行检索 → 每次都得到相同的笔记链,可重复和审计。它们是互补的,而不是竞争的:将WikiMoth指向Karpathy风格的wiki,您可以获得确定性的多跳检索。
## 快速入门(阅读)
```python
from wikimoth import MemoryRAG, EchoReader
rag = MemoryRAG(reader=EchoReader()) # 无API的默认读取器
rag.index("path/to/your/wikilink/vault") # 笔记 → ~400-token块,构建图
chunks, tokens = rag.retrieve("a connect-the-dots question?", top_k=8)
print(f"{len(chunks)} chunks, {tokens} tokens to feed the reader")
print(rag.answer("a connect-the-dots question?")) # 检索 → 紧凑 → 读取
```
用真正的Claude答案替换(仅在构造时触及API):
```python
from wikimoth import MemoryRAG, ClaudeReader
rag = MemoryRAG(reader=ClaudeReader(model="claude-sonnet-4-6")) # 需要ANTHROPIC_API_KEY
```
## 查看提供答案的内存:`wikimoth serve`
```bash
wikimoth serve # 提供http://127.0.0.1:8765(仅本地)
wikimoth serve --vault PATH --port 8080
```
一个零依赖的本地Web查看器(纯标准库,无Flask,无JS框架,无网络):
- **浏览和搜索**您的笔记,
- 作者 **`[[wikilink]]`图**(检索器行走的相同边),
- 更重要的是,**“是什么内存提供了这个答案”**:输入一个问题,查看WikiMoth将提供给读取器的确切笔记链,每个块的跳转距离、token计数和`−N%`与转储整个仓库的比较。仅检索:无LLM调用,无API密钥,确定性。
因为存储是纯Markdown,您可以同样在Obsidian或VS Code中打开同一个仓库;查看器是一个方便的工具,不是锁定。
## 在代理循环中:`wikimoth mcp`
`wikimoth serve`是为您准备的。MCP服务器是为**模型**准备的:它通过模型上下文协议公开相同的确定性检索,因此Claude自己调用它,而不是您手动获取上下文。
```bash
# 1. 安装到运行Claude Code的Python中
python -m pip install wikimoth
# 2. 验证命令解析(打印状态,然后退出)
python -m wikimoth status
# 3. 将MCP服务器注册到Claude Code
claude mcp add wikimoth -- python -m wikimoth mcp
```
步骤2是重要的检查事项:如果`python -m wikimoth status`打印状态行,那么`python -m wikimoth mcp`将为Claude运行。使用**相同的`python`**在所有三个步骤中(在某些系统上它是`python3`);这是唯一需要匹配的内容。
如果您更喜欢Node世界或没有Python设置?一行代码,无需匹配工具链:
```bash
claude mcp add wikimoth -- npx -y wikimoth-mcp
```
[`wikimoth-mcp`](https://www.npmjs.com/package/wikimoth-mcp)启动器查找具有WikiMoth的Python(或`uvx`安装一个),注入vault路径,以便服务器永远不会读取客户端工作目录中的空文件夹,并将MCP通道原样传递。相同的`npx -y wikimoth-mcp`作为任何`mcpServers`配置(Claude Desktop,Cursor,Windsurf)中的服务器命令;设置`WIKIMOTH_VAULT`为您自己的vault。
现在Claude有一个`recall(query)`工具。向它提问一些存在于您的笔记中的问题,它调用`recall`;WikiMoth遍历`[[链接]]`并返回确切的笔记链(无需检索LLM调用,token最小化,每次返回相同的结果),Claude从中回答。`status`工具报告连接的vault。对于任何其他MCP客户端,请使用`python -m wikimoth mcp`作为服务器命令(stdio传输);指向特定的vault `--vault PATH`。
`python -m wikimoth mcp`是可移植的表单(它在安装的包的任何地方运行)。裸`wikimoth mcp`也有效,当控制台脚本在您的PATH上时。它是纯标准库:一个手工JSON-RPC 2.0_stdio服务器,无MCP SDK依赖。
mcp-name: io.github.juliangeymonat-jpg/wikimoth
## 捕获:会话 → 笔记(写入一半)
检索需要一个`[[wikilink]]`vault;手动编写一个是摩擦。`wikimoth.capture`通过安装Claude Code生命周期钩子自动构建它,从而将每个会话转换为**一个**确定性Markdown笔记。
**重要的不变性:** 笔记的`[[wikilinks]]`(图边)由代码计算(字符串/路径匹配),**永远不会由模型**计算。LLM可以*可选地*起草摘要文本(`WIKIMOTH_LLM_PROSE=1`),但它发出的任何`[[...]]`都被剥离,永远不会被解析为边。所以图是可重复的(相同会话 + vault → 相同边)并且是可审计的。默认捕获是完全确定性的,并且**零API调用**。
```bash
wikimoth install # 在./.claude/settings.json中写入5个钩子(绝对解释器路径)
wikimoth install --user # ~/.claude/settings.json
wikimoth install --vault PATH # 选择笔记存储位置(设置WIKIMOTH_VAULT)
wikimoth status # vault, note/session/buffer计数,钩子状态
wikimoth uninstall # 再次删除钩子
```
生命周期:**SessionStart**回忆最近的会话进入上下文 · **UserPromptSubmit / PostToolUse**缓冲会话 · **Stop / SessionEnd**写入一个笔记。捕获的笔记与读取管道索引的笔记完全相同;捕获和检索闭合环。
## 保持内存诚实:卫生套件
一个只增长的内存会腐烂。笔记会过时,两个笔记开始相互矛盾,同一个事实被保存两次,一个旧事实被替换但从未被淘汰。其他代理内存工具通过**使用LLM**静默覆盖旧状态来解决这个问题。WikiMoth提供六个命令**确定性地**显示它,**永远不会删除任何东西**:git是您的审计轨迹。
| 命令 | 查找 | 写入? |
|---|---|:--:|
| `wikimoth conflicts` | 两个笔记对同一个事实断言不同的值(类型感知,有效时间精度) | 否 |
| `wikimoth lint` | 损坏的链接,孤儿,存根,过时笔记,替换循环 | 否 |
| `wikimoth dedup` | 准确 + 近似重复笔记(MinHash + LSH,确认为确切Jaccard) | 否 |
| `wikimoth decay` | 笔记变冷:旧的,链接很少,召回很少(一个审查队列,自动删除) | 否 |
| `wikimoth recall --as-of <date>` | 您的内存在过去某个日期断言的内容(双时态时间旅行,无数据库) | 否 |
| `wikimoth supersede OLD NEW` | 退休一个事实:使其无效,不要删除 | **是** |
三个原则贯穿所有这些:
- **使无效,不要删除。** `supersede`在frontmatter中标记笔记已过时,并链接到其替换项。
旧正文被检索循环忽略,但其`[[链接]]`到当前笔记保持活动,因此查询如果落在过时笔记上**跳转免费到新笔记**。永远不会`rm`d;
- **工具永远不会猜测。** 每个命令发出*候选人*。一个判断不机械的判断,即两个笔记真正相互矛盾,是留给调用模型,从不嵌入到工具中。所以输出是可重复的,您可以准确看到每个候选人被标记的原因。
- **双时态,无数据库。`recall --as-of 2026-01-01`仅使用frontmatter有效性窗口重放vault中的内容,无需事件日志,无向量存储,无迁移。
与WikiMoth的其他部分相同的不变性:纯标准库,确定性(字节相同的输出),只写单个`的卫生套件暴露了这一点。
| 命令 | 查找 | 写? |
|---|---|:--:|
| `wikimoth conflicts` | 两个笔记对同一个事实断言不同的值(类型感知,有效时间精度) | 否 |
| `wikimoth lint` | 损坏的链接,孤儿,存根,过时笔记,替换循环 | 否 |
| `wikimoth dedup` | 准确 + 近似重复笔记(MinHash + LSH,确认确切Jaccard) | 否 |
| `wikimoth decay` | 笔记渐渐消失:旧的,链接很少,召回很少(一个审查队列,而不是自动删除) | 否 |
| `wikimoth recall --as-of <date>` | 您的内存在一个过去日期上的断言(双时态时间旅行,无数据库) | 否 |
| `wikimoth supersede OLD NEW` | 退休一个事实:使其无效,而不是删除 | **是** |
三个原则贯穿所有这些:
- **使无效,不要删除。** `supersede` 在frontmatter中将笔记标记为过时,并链接到其替换项。
旧正文从检索循环中删除,但其`[[链接]]`到当前笔记的链接仍然活动,以便查询如果落在过时笔记上**免费跳转到新笔记**。不会`rm`d;
历史存储在git中。
- **工具永远不会猜测。** 每个命令都发出*候选人*。一个判断不机械的判断,即两个笔记真正相互矛盾,是留给调用模型的任务,而不是工具内置。
所以输出是可重复的,您可以准确看到每个候选人被标记的原因。
- **双时态,无数据库。** `recall --as-of 2026-01-01` 从frontmatter有效性窗口中重放了vault在那一天的断言。
与WikiMoth的其他部分一样,不变的属性:纯标准库,确定性(字节相同的输出),
除了单个`supersede`写入器外,读取时不写入,纯Markdown您可以区分。七个命令也通过MCP公开(`list_conflicts`,`list_lint`,`list_duplicates`,`list_fading`,`supersede`,
以及`recall`获得`as_of` / `show_superseded`),因此代理可以自行清理其内存。
## 安装
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/zero-infra.png" alt="检索循环中:0个GPU,0个向量数据库,0个LLM调用">
WikiMoth的核心是**纯标准库**(`dependencies = []`):检索引擎,分块器,wikilink图,管道和捕获都包含在`wikimoth/`下;无需额外安装,无需GPU,无需
向量数据库。
```bash
pip install wikimoth
# 可选额外功能:
pip install "wikimoth[hybrid]" # 可选BM25种子检索器变体
pip install "wikimoth[claude,tokens]" # 真实Claude读取器 + 准确的tiktoken计数
```
额外功能:`hybrid` = BM25种子检索器(`rank_bm25`)· `claude` = `anthropic`读取器 ·
`tokens` = 准确的token计数(`tiktoken`)· `dense` = 密集基准基线 · `headroom` =
可逆CCR压缩。
## 工作原理
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/pipeline.png" alt="管道:通过链接行走检索,压缩到笔记链(约5k个token),读取纯Markdown,带有审计跟踪,然后将新事实捕获回。">
**检索 → 紧凑 → 读取。** `index()`将每个笔记分割成约400个token块(~50个重叠),
保留每个块的笔记身份,以便`[[wikilink]]`图仍然跨块连接(块粒度上的多跳)。
`GraphRetriever(source="wikilinks")`从词汇上进行种子选择,然后行走作者链接,
因此一个段落*不是*与问题在词汇上相似但*可以通过链接到达*仍然被拉取。
一个可选的压缩阶段(可逆的CCR via
[`chopratejas/headroom`](https://github.com/chopratejas/headroom))在付费读取器之前进一步缩小段落;
如果未安装headroom,它会退化为无操作。
纯导航中心(类似目录的`MEMORY.md`)可以作为**仅图边**进行索引(`exclude_content`,默认为`("MEMORY.md",)`):它的`[[链接]]`构建边并成为BFS航站楼,
但其自身块永远不会到达读取器。
每个阶段都可以通过`MemoryRAG(retriever=…, compactor=…, reader=…)`进行构造函数注入,因此您可以
交换检索器(例如BM25种子检索器`HybridRetriever`),压缩器或读取器。
## 基准测试:提供给读取器的token
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/token-cost.png" alt="回答一个问题所需的token:大约482,000个token用于粘贴整个vault,而WikiMoth的笔记链为大约5,000个token,减少了99%的token。">
`wikimoth.benchmark.harness`在**相同**的vault和问题上测量*提供给读取器的token*(您实际支付的费用):
| | 提供给读取器 | 状态 |
|---|---|---|
| `dump` | 整个vault | 基线 |
| `deterministic` | wikilink图检索 | 已实现 |
| `deterministic_compacted` | 检索 + Headroom | 已实现 |
| `agentic` | 一个LLM浏览并修剪其上下文 | 已实现(Claude工具使用) |
默认情况下不运行付费API调用;每个臂的读取器默认为无API的`EchoReader`。
## 诚实的限制
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/multi-hop.png" alt="链接型答案达到:BM25 0%,向量/密集 0%,WikiMoth 100%,在链接型语料库上。 在直接查找中,所有三个都达到召回率@8 = 1.00。">
<img src="https://raw.githubusercontent.com/juliangeymonat-jpg/wikimoth/main/docs/img/determinism.png" alt="同一个查询运行五次:WikiMoth返回一个不同的结果,基于LLM的内存每次运行都会变化。">
WikiMoth的价值在于**确定性,可审计,token最小化,纯Markdown**内存,具有真正的多跳能力,而不是“比BM25更好的检索”。具体来说:
- **-99% 是相对于*转储vault*而言的**(≈ 5k vs ~482k 个token在一个真正的356笔记vault中),而不是相对于BM25的;
获胜的是针对现实情况(粘贴一切/天真整个笔记RAG),并且是确定性的。
- **在一个典型的真实vault中,检索 ≈ BM25**。直接查找召回率@8 均为 1.00。多跳/连接点赢得(0% → 最高 100%,在普通vault中flat search得分为 0)出现在**策划的,链接密集的**语料库中;在普通vault中,混合检索 **永远不会比BM25差**,在召回率上严格优于BM25。
- **确定性**是任何静态检索器固有的属性(BM25/密集也是如此);WikiMoth的确定性优势在于**对抗LLM汇总内存**(每次运行结果不同)。
- **对抗让模型修剪其上下文**(`agentic`分支,实际运行对抗Claude Sonnet 4.6,12个多跳问题):代理达到相同的答案,多跳包含在内(12/12)。
区别在于成本。它需要4到6次付费往返和大约10倍的计费token每个问题,因为它在每次步骤中都会发送不断增长的转录记录,而WikiMoth从一次确定性传递中回答,没有检索循环中的模型调用和可审计的笔记链。
倍数是语料库特定的,不是规律。复制它:`python scripts/run_agentic_benchmark.py`.
## 可移植性和许可证
`MemoryRAG(retriever=…, compactor=…, reader=…)`;默认为`GraphRetriever(source="wikilinks")` /
`NoOpCompactor` / `EchoReader`. 任何满足小协议的对象都可以接入。
Apache-2.0;参见[LICENSE](LICENSE)。 2026 Julian Geymonat 版权所有。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器