Building an LLM wiki
Building an LLM wiki
This is the end-to-end pattern: a small, curated vault that an agent keeps growing for you. You drop in raw material, the agent reads it, writes a clean summary page, threads it into the existing pages, and files it so the catalog and the ledger stay current. Do that a few dozen times and you have an interlinked knowledge base instead of a folder of clippings.
The shape comes from Karpathy’s “LLM wiki”: a curated index.md catalog, a raw sources/
layer the agent reads but never edits — the write API enforces this; new sources and appends are
allowed, overwriting or deleting an existing one is rejected — and an append-only log.md ledger.
1. Scaffold the wiki
lettuce wiki init research --agents
wiki init writes exactly four files, then --agents adds a fifth tailored brief:
Scaffolded LLM wiki in /…/research:
CLAUDE.md
index.md
log.md
sources/README.md
AGENTS.md
CLAUDE.md— the maintainer guide (the rules the agent follows when curating).index.md— the curated catalog and published home page. New pages get linked under its## Pagesheading.log.md— the append-only ingest ledger; entries keep a## [YYYY-MM-DD] <op> | <title>prefix so the log stays greppable.sources/README.md— explains the raw layer.AGENTS.md— the vault-etiquette brief thatget_vault_infosurfaces to a connected agent (alongsideCLAUDE.md).
wiki init only accepts init. There is no wiki ingest / wiki lint / wiki slides —
ingest, lint, search, slides, and agents are all top-level lettuce commands.
2. Connect an agent
Point your MCP client at the vault — see Getting started/03 Your first agent connection for the one-minute pairing. In short:
lettuce serve research
The banner prints the API key. The agent’s get_vault_info returns the combined brief
(AGENTS.md + CLAUDE.md), so it knows the curation rules before it touches anything — and the
active immutable prefixes (sources/ by default), so it knows what it may not overwrite.
To let the agent find pages by meaning (not just keyword) via semantic_search, build a vector
index once: lettuce embed research. It uses any OpenAI-compatible embeddings endpoint — set
LETTUCE_EMBED_BASE_URL to a local server like Ollama for a no-key, nothing-leaves-the-machine
setup. Re-run lettuce embed after big edits to refresh.
3. Ingest a source
Ways material lands in sources/:
# From the web — pandoc + readability, downloads images, appends a log.md entry:
lettuce ingest https://example.com/some-paper
# From a local file (docx / epub / odt / rtf / html / markdown / …):
lettuce ingest ~/Downloads/whitepaper.docx
# A whole folder at once (batch):
lettuce ingest ~/research-dump/
# …or just drop a note into sources/ by hand:
# research/sources/interview-notes.md
(PDFs work directly — Lettuce extracts the text layer with PDFKit, so no pandoc needed for them. A scanned/image-only PDF with no text layer errors with a clear “OCR it first”.)
ingest accepts a URL, a local file, or a whole directory and writes sources/<slug>.md for each.
4. The compounding pass
This is the part that makes it a wiki rather than a dumping ground. Ask the agent to digest the new source into the existing pages:
@claude A new source landed at
sources/some-paper.md. Digest it:
read_notethe source.Write a tight summary page (claims, evidence, your read).
For each entity or concept it touches,
patch_sectionthat page to add what’s new.
file_pagethe summary so it shows up in the catalog and the log.
A typical sequence of tool calls:
| Step | Tool | What happens |
|---|---|---|
| Read the raw input | read_note("sources/some-paper.md") |
Pulls the ingested text. |
| Find the pages it touches | search("retrieval augmentation") — or semantic_search for meaning-based recall |
Returns {path, title, snippet, score} (lower score = better). |
| Pull the neighborhood | link_graph(path, depth: 2) |
The bidirectional subgraph to wire links into. |
| Update 3–5 concept pages | patch_section(path, target_type: "heading", target: "Findings", op: "append", content: …) |
Threads the new claim into each existing page. |
| File the summary | file_page(path, content, summary, title) |
Writes the page and links it under index.md’s ## Pages and appends a dated log.md entry — in one call. |
The key move is file_page: it does the three-way bookkeeping (page + catalog + ledger) so the
agent never forgets to update the index. Use patch_section to update an existing page; use
file_page only for a brand-new one.
apply_edits is the surgical alternative to patch_section — give it an array of
{find, replace} edits and they apply atomically; if any find doesn’t match exactly once,
the whole call rolls back.
So one ingest fans out into: 1 new summary page, 3–5 patched concept pages, 1 catalog link, and 1 log entry. Repeat across sources and the graph densifies on its own.
5. File a question’s answer back as a page
A query worth keeping shouldn’t evaporate in chat. When you ask the agent something that the wiki should know, have it file the answer:
@claude What does the corpus say about eval contamination? Synthesize across the relevant pages, then
file_pagethe answer asEval contamination.mdwith a one-line summary.
The agent searches, reads the hits, writes a synthesis, and file_pages it — now the next
person (or agent) finds it in the catalog instead of re-asking.
6. A periodic health pass
Run two passes on a schedule (weekly is plenty for a slow-growing wiki):
# Deterministic, offline — broken links, orphans, tag issues:
lettuce lint research
# Add LLM tag suggestions for untagged / missing-required notes:
lettuce lint research --suggest
lint flags unresolved-link, orphan-note, no-inbound (the pattern’s “orphan” — has
outbound links but nothing points back), tag-typo, orphan-tag, and untagged-note. The
structural files (index.md, CLAUDE.md, AGENTS.md, log.md, sources/**) are exempt from
the content rules.
Then have the connected agent run the semantic health-check, which is LLM-backed (it needs a provider configured):
@claude Run
lint_semanticover the wiki. Reconcile any contradictions or stale claims you find by patching the relevant pages, andsuggest_tagsfor anything untagged. Record the fixes with alog.mdentry.
lint_semantic surfaces contradictions, stale claims, missing-concept pages, and data gaps;
suggest_tags proposes tags for untagged or missing-required notes. Both return a clear error if
no LLM provider is configured.
Running it hands-off
Want this on autopilot? Put the connected agent behind a propose-only key so its writes land as
reviewable proposals instead of committing directly — see Suggestions mode/01 Overview. Since
one ingest fans out into several pages, have the agent file them with propose_batch so the
whole set queues as a single batch you accept or reject together (Accept all / Reject all in the
proposals inspector) rather than page by page. Pair it with a
schedule to run the digest + health pass on a cadence, and you
have a wiki that maintains itself while you review the diffs.