Wikimoth

io.github.juliangeymonat-jpg由 GitHub 账号 juliangeymonat-jpg 发布v0.2.1更新于 Oct 24, 2026

Deterministic, token-minimal agent memory: multi-hop [[wikilink]] recall with no LLM in the loop.

已验证STDIO仅桌面文件与存储知识与记忆

概览

AI 生成的概览

Wikimoth 为助手提供确定、省 token 的记忆:沿本地 markdown 笔记库中作者手写的 [[wikilink]] 链接多跳检索,返回确切的笔记链。

可以做什么
召回:针对问题沿 markdown 笔记库的 [[wikilink]] 图检索,返回确切的笔记链,检索过程不调用模型,同一问题结果始终一致。状态:报告当前连接的笔记库。记忆整理:列出互相冲突的笔记、检查断链、孤立笔记、存根、过期笔记等问题,找出近似重复与逐渐冷落的笔记,并用新笔记取代旧笔记。按时间召回:依据 frontmatter 的有效期,回放笔记库在某个过去日期所断言的内容。
适用场景
适合以 [[wikilink]] markdown 笔记库(例如 Obsidian 笔记库或从 Claude Code 会话捕获的记忆文件夹)保存资料,并希望助手以低成本、可复现、可审计的方式从中检索,包括需要跨链接多跳的问题。也适合长期维护这份记忆的整洁。
运行要求
以 stdio 在本地运行。需要装有 wikimoth 包的 Python(pip install wikimoth,或使用 npx wikimoth-mcp 启动器,它会查找或通过 uvx 安装 Python)。将 WIKIMOTH_VAULT 设为要服务的笔记库绝对路径;WIKIMOTH_PYTHON 可选,用于固定解释器。必须已有 [[wikilink]] 笔记库;捕获钩子可从 Claude Code 会话生成一个。
安装前请注意
服务器会读取本地笔记文件夹,因此 WIKIMOTH_VAULT 只应指向你愿意让助手访问的笔记库。supersede 工具会写入笔记(标记旧笔记已被取代并链接到替代笔记);其他整理命令只输出候选。捕获钩子会写入 .claude/settings.json 并根据会话生成笔记。可选的 LLM 摘要撰写和 Claude 阅读器会产生付费 API 调用;默认检索路径不产生任何调用。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Wikimoth,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

WikiMoth

[WikiMoth]

Connects the dots. The same way, every time.

wikimoth.com · pip install wikimoth

[CI] [PyPI] [npm] [License: Apache-2.0]

Deterministic, token-minimal, auditable memory for Claude and agents. Point WikiMoth at a folder of [[wikilink]] notes (an Obsidian vault, or Claude's own memory folder) and it follows the authored links to the answer flat search can't reach, shows you the exact note-chain behind it, and feeds the reader ~99% fewer tokens than pasting the whole vault. Pure markdown, no GPU, no vector DB, no LLM in the retrieval loop.

[One question, three hops: WikiMoth follows your authored links to the note that holds the answer. Flat search stops at the first keyword match.]
bash
pip install wikimothwikimoth demo         # instant multi-hop recall over a bundled demo vault (no setup)

Already have a [[wikilink]] vault (an Obsidian vault, a notes folder)? Point WikiMoth at it and get the full connect-the-dots view in one command, no capture, no waiting:

bash
wikimoth serve --vault /path/to/your/vault      # browse + "what memory fed this answer"wikimoth recall --vault /path/to/your/vault "a connect-the-dots question"

Want it to build memory from your Claude Code sessions? Install the capture hooks; each session you run and close is written into a [[wikilink]] vault:

bash
wikimoth install      # capture: turn your Claude Code sessions into a [[wikilink]] vaultwikimoth serve        # once you have captured sessions, browse them

Why not just let Claude manage its own context?

We benchmarked exactly that. An agent that browses the notes folder and prunes its own context reaches the same answers, multi-hop included (12/12 in our run). It just pays for it: 4 to 6 model round-trips and roughly 10x the billed tokens per question, because it re-sends a growing transcript every step. WikiMoth retrieves the same note-chain in one deterministic pass, no model in the loop, and shows you the exact notes behind the answer.

[Same answer, far less work: letting Claude prune its own context takes 4 to 6 model round-trips, about 10x the billed tokens, and roughly 9 seconds per answer; WikiMoth does it in one deterministic retrieval pass with zero model calls in the loop, in milliseconds, with an auditable note-chain. Both reach the right answer 12 out of 12.]

Real run, Claude Sonnet 4.6, 12 multi-hop questions on a reproducible vault. The ~10x counts a reader on both sides; it is corpus-specific, not a universal law. Reproduce it with python scripts/run_agentic_benchmark.py. Full breakdown in Honest limits.


Why WikiMoth

Most agent memory is either paste the whole notes folder into context (expensive, and the model gets lost in the middle) or LLM-summarised similarity search (lossy, and non-deterministic: the same question can return different memory next week). WikiMoth takes a different bet: your notes are the store (plain markdown), the graph is authored (your [[wikilinks]], no embeddings to train or drift), and retrieval is code, not a model, so it's reproducible and you can read exactly why each note was chosen.

WikiMothBM25Vector RAGclaude-memLLM Wiki (Karpathy)
Connects the dots (multi-hop over authored [[links]])✅❌❌❌✅ (agentic)
Deterministic retrieval (same query → same result)✅✅✅❌❌
No LLM call to retrieve✅✅✅~❌
Auditable note-chain (which notes produced the answer)✅~❌❌~
Direct-lookup recall@8 (real vault)1.001.001.00~~
No GPU / no vector DB / no index build✅✅❌~✅
Plain-markdown store (open in any editor)✅~❌❌✅
Token-minimal vs dumping the vault✅ −99%✅ −99%✅ −99%✅~
Deterministic, API-free auto-capture✅❌❌❌❌
Hygiene without an LLM (conflicts · dupes · stale · supersede)✅❌❌~❌

LLM Wiki follows links and skips the vector DB like WikiMoth, but an LLM writes and reads the wiki, so retrieval is agentic (an LLM call per recall, not reproducible), while its curated pages are richer. ~ = partial / not independently benchmarked.

The edge is the combination, not higher recall: WikiMoth matches flat search on the basics and adds connect-the-dots + determinism + an audit trail + a plain-markdown store. See Honest limits for exactly where it ties and where it wins.

Compared to Karpathy's LLM Wiki

WikiMoth shares the substrate Andrej Karpathy's LLM Wiki pattern popularised: plain-markdown [[wikilink]] notes, no vector DB, but flips the engine. In the LLM-Wiki pattern an LLM writes and reads the wiki: rich, source-cited pages, but recall is agentic (it costs an LLM call and the path isn't reproducible). WikiMoth computes the edges in code and retrieves with a fixed algorithm, no LLM in the loop → the same note-chain every time, reproducible and auditable. They're complementary, not competing: point WikiMoth at a Karpathy-style wiki and you get deterministic multi-hop retrieval over it. (We don't claim to be "better" than the LLM Wiki: it curates richer pages; we retrieve deterministically.)

Quickstart (read)

python
from wikimoth import MemoryRAG, EchoReader
rag = MemoryRAG(reader=EchoReader())          # API-free default readerrag.index("/path/to/your/wikilink/vault")     # notes → ~400-token chunks, graph built
chunks, tokens = rag.retrieve("a connect-the-dots question?", top_k=8)print(f"{len(chunks)} chunks, {tokens} tokens to feed the reader")   # the headline win
# EchoReader is a deterministic stub for wiring/tests: it prints a diagnostic# `[echo] ...` line, NOT a natural-language answer. Swap in ClaudeReader (below)# for real prose. The retrieval + token numbers above are the same either way.print(rag.answer("a connect-the-dots question?"))

Swap in a real Claude answer (only touches the API when constructed):

python
from wikimoth import MemoryRAG, ClaudeReaderrag = MemoryRAG(reader=ClaudeReader(model="claude-sonnet-4-6"))   # needs ANTHROPIC_API_KEY

See what memory fed an answer: wikimoth serve

bash
wikimoth serve                 # serves http://127.0.0.1:8765 (local-only)wikimoth serve --vault PATH --port 8080

A zero-dependency local web viewer (pure stdlib, no Flask, no JS framework, no network):

  • browse + search your notes,
  • the authored [[wikilink]] graph (the same edges the retriever walks),
  • and the one that matters, "what memory fed this answer": type a question and see the exact note-chain WikiMoth would feed a reader, with per-chunk hop distance, token counts, and the −N% vs dumping the whole vault. Retrieval only: no LLM call, no API key, deterministic.

Because the store is plain markdown, you can equally open the same vault in Obsidian or VS Code; the viewer is a convenience, not a lock-in.

In the agent loop: wikimoth mcp

wikimoth serve is for you. The MCP server is for the model: it exposes the same deterministic retrieval over the Model Context Protocol, so Claude calls it itself instead of you fetching context by hand.

bash
# 1. install into the Python that runs your Claude Codepython -m pip install wikimoth
# 2. verify the command resolves (prints status, then exits)python -m wikimoth status
# 3. register the MCP server with Claude Codeclaude mcp add wikimoth -- python -m wikimoth mcp

Step 2 is the check that matters: if python -m wikimoth status prints a status line, then python -m wikimoth mcp will run for Claude too. Use the same python in all three steps (it is python3 on some systems); that is the one thing that has to match.

Prefer the Node world, or no Python set up? One line, no toolchain matching:

bash
claude mcp add wikimoth -- npx -y wikimoth-mcp

The wikimoth-mcp launcher finds a Python that has WikiMoth (or uvx-installs one on the fly), injects the vault path so the server never reads an empty folder from the client's working directory, and passes the MCP channel through untouched. The same npx -y wikimoth-mcp works as the server command in any mcpServers config (Claude Desktop, Cursor, Windsurf); set WIKIMOTH_VAULT to your vault.

Now Claude has a recall(query) tool. Ask it something that lives in your notes and it calls recall; WikiMoth walks the [[links]] and hands back the exact note-chain (no LLM call to retrieve, token-minimal, the same result every time), and Claude answers from it. A status tool reports the connected vault. For any other MCP client, use python -m wikimoth mcp as the server command (stdio transport); point it at a specific vault with --vault PATH.

python -m wikimoth mcp is the portable form (it runs wherever the package is installed). The bare wikimoth mcp works too when the console script is on your PATH. It is pure stdlib: a hand-rolled JSON-RPC 2.0 stdio server, no MCP SDK dependency.

mcp-name: io.github.juliangeymonat-jpg/wikimoth

Capture: sessions → notes (the write half)

Retrieval needs a [[wikilink]] vault; hand-authoring one is the friction. wikimoth.capture builds it automatically by installing Claude Code lifecycle hooks that turn each session into one deterministic markdown note.

The invariant that matters: a note's [[wikilinks]] (the graph edges) are computed by code (string/path matching), never by a model. An LLM may optionally draft the summary prose (WIKIMOTH_LLM_PROSE=1), but any [[...]] it emits is stripped, never parsed as an edge. So the graph is reproducible (same session + vault → same edges) and auditable. Default capture is fully deterministic and makes zero API calls.

bash
wikimoth install                 # writes 5 hooks into ./.claude/settings.json (absolute interpreter path)wikimoth install --user          # ~/.claude/settings.json insteadwikimoth install --vault PATH    # choose where notes go (sets WIKIMOTH_VAULT)wikimoth status                  # vault, note/session/buffer counts, hook statewikimoth uninstall               # remove the hooks again

Lifecycle: SessionStart recalls recent sessions into context · UserPromptSubmit / PostToolUse buffer the session · Stop / SessionEnd write one note. The captured notes are exactly what the read pipeline indexes; capture and retrieval close the loop.

Keep memory honest: the hygiene suite

A memory that only grows rots. Notes go stale, two notes start disagreeing, the same fact gets saved twice, an old fact is replaced but never retired. Other agent-memory tools resolve this with an LLM that silently overwrites the old state. WikiMoth ships six commands that surface it deterministically, and never delete anything: git is your audit trail.

commandfindswrites?
wikimoth conflictstwo notes asserting a different value for the same fact (type-aware, valid-time precision)no
wikimoth lintbroken links, orphans, stubs, stale notes, supersession cyclesno
wikimoth dedupexact + near-duplicate notes (MinHash + LSH, confirmed by exact Jaccard)no
wikimoth decaynotes going cold: old, rarely linked, rarely recalled (a review queue, never auto-delete)no
wikimoth recall --as-of <date>what your memory asserted on a past date (bitemporal time-travel, no DB)no
wikimoth supersede OLD NEWretire a fact: invalidate, don't deleteyes

Three principles hold across all of them:

  • Invalidate, don't delete. supersede marks a note superseded and links it to its replacement in frontmatter. The old body drops out of retrieval, but its [[link]] to the current note stays live, so a query that lands on the stale note hops free to the new one. Nothing is ever rm'd; the history lives in git.
  • The tool never guesses. Every command emits candidates. The one judgement that isn't mechanical, deciding whether two notes truly contradict, is left to the calling model, never baked into the tool. So the output is reproducible and you can read exactly why each candidate was flagged.
  • Bitemporal, no database. recall --as-of 2026-01-01 replays what the vault asserted on that date from frontmatter validity windows alone. No event log, no vector store, no migration.

Same invariants as the rest of WikiMoth: pure stdlib, deterministic (byte-identical output), read-only except the single supersede writer, plain markdown you can diff. The seven commands are also exposed over MCP (list_conflicts, list_lint, list_duplicates, list_fading, supersede, plus recall gaining as_of / show_superseded), so the agent can keep its own memory clean.

Install

[In the retrieval loop: 0 GPUs, 0 vector DBs, 0 LLM calls.]

WikiMoth's core is pure stdlib (dependencies = []): the retrieval engine, chunker, wikilink graph, pipeline and capture are all vendored under wikimoth/: nothing extra to install, no GPU, no vector DB.

bash
pip install wikimoth# optional extras:pip install "wikimoth[hybrid]"          # optional BM25-seeded retriever variantpip install "wikimoth[claude,tokens]"   # real Claude reader + exact tiktoken counts

Extras: hybrid = BM25-seeded retriever (rank_bm25) · claude = the anthropic reader · tokens = exact token counts (tiktoken) · dense = the dense benchmark baseline · headroom = reversible CCR compaction.

How it works

[Pipeline: retrieve by walking the links, compact to the note-chain (about 5k tokens), read plain markdown with an audit trail, then capture new facts back.]

retrieve → compact → read. index() splits each note into ~400-token chunks (~50 overlap), keeping per-chunk note identity so the [[wikilink]] graph still connects across chunks (multi-hop at chunk granularity). GraphRetriever(source="wikilinks") seeds lexically, then walks the authored links, so a passage not lexically similar to the question but reachable by a link still gets pulled. An optional compaction stage (reversible CCR via chopratejas/headroom) shrinks passages further before the (paid) reader; it degrades to a no-op if headroom isn't installed.

A pure-navigation hub (a table-of-contents like MEMORY.md) can be indexed as graph edges only (exclude_content, default ("MEMORY.md",)): its [[links]] build edges and it stays a BFS waypoint, but its own chunks never reach the reader.

Every stage is constructor-injectable via MemoryRAG(retriever=…, compactor=…, reader=…), so you can swap the retriever (e.g. the BM25-seeded HybridRetriever), the compactor, or the reader.

Benchmark: tokens fed to the reader

[Tokens to answer one question: about 482,000 to paste the whole vault vs about 5,000 for the WikiMoth note-chain, a 99% cut versus dumping the vault.]

wikimoth.benchmark.harness measures tokens fed to the reader (what you actually pay for) across arms over the same vault and questions:

armfeeds the readerstatus
dumpthe whole vaultbaseline
deterministicwikilink-graph retrievalimplemented
deterministic_compactedretrieval + Headroomimplemented
agentican LLM browses and prunes its own contextimplemented (Claude tool-use)

No paid API calls run by default; every arm's reader defaults to the API-free EchoReader.

Honest limits

[Link-only answers reached: BM25 0%, vector/dense 0%, WikiMoth 100%, on link-only corpora. On direct lookups all three tie at recall@8 = 1.00.] [Same query run five times: WikiMoth returns one distinct result, LLM-based memory varies run to run.]

WikiMoth's value is deterministic, auditable, token-minimal, plain-markdown memory with a real multi-hop capability, not "better retrieval than BM25". Specifically:

  • The −99% is vs dumping the vault (≈5k vs ~482k tokens on a real 356-note vault), not vs BM25: a tuned BM25-RAG also feeds ~5k. The win is against the realistic status quo (paste everything / naive whole-note RAG), and it's deterministic.
  • On a typical real vault, retrieval ≈ BM25. Direct-lookup recall@8 ties at 1.00. The multi-hop / connect-the-dots win (0% → up to 100% where flat search scores zero) shows up on curated, link-heavy corpora; on an average vault, hybrid is never worse than BM25, not strictly better on recall.
  • Determinism is inherent to any static retriever (BM25/dense too); WikiMoth's determinism win is specifically vs LLM-summarised memory (which varies run to run).
  • vs letting the model prune its own context (the agentic arm, real run against Claude Sonnet 4.6, 12 multi-hop questions): the agent reaches the same answers, multi-hop included (12/12). The difference is cost. It takes 4 to 6 paid round-trips and about 10x the billed tokens per question, because it re-sends a growing transcript each step, where WikiMoth answers from one deterministic pass with no model call in the retrieval loop and an auditable note-chain. The multiple is corpus-specific, not a law. Reproduce it: python scripts/run_agentic_benchmark.py.

Pluggable + License

MemoryRAG(retriever=…, compactor=…, reader=…); defaults GraphRetriever(source="wikilinks") / NoOpCompactor / EchoReader. Anything satisfying the small Protocols drops in.

Apache-2.0; see LICENSE. © 2026 Julian Geymonat.

来源:README.md,提交 8823387

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.2.1最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分