The LLM-wiki pattern
The LLM-wiki pattern
An LLM-wiki (a pattern popularised by Andrej Karpathy) is a knowledge base that an LLM builds and maintains on your behalf, rather than one you fill in by hand. You feed it raw material; the agent compiles that material into a cross-linked set of pages, keeps a catalog and a ledger, and answers questions against what it has compiled — filing the good answers back so the wiki gets smarter over time.
Lettuce is built to be a substrate for exactly this. The wiki is just a folder of Markdown notes, and every operation an agent needs is exposed as an MCP tool. This page explains the three layers, then maps the INGEST / QUERY / LINT operations onto Lettuce’s tools.
The three layers
A well-run LLM-wiki is really three layers stacked on top of each other:
1. sources/ — the immutable raw layer
Everything that came from outside the wiki lands in sources/: ingested articles, pasted transcripts, downloaded papers. These files are treated as append-only and never rewritten — and this is enforced, not just convention: through the agent API an agent may create a new source or append to one, but overwriting, patching, deleting, or moving an existing file under an immutable prefix is rejected. The default prefix is sources/; a vault can change it with an immutable: list in index.md frontmatter (or opt out with immutable: []), and get_vault_info reports the active prefixes. They’re the evidence the wiki is built from, so you can always trace a claim back to where it came from. Structural files like sources/** are exempt from the content lint rules — they aren’t expected to be tidy, cross-linked notes.
2. The wiki — the LLM-owned compiled layer
This is the wiki proper: the entity, concept, and summary pages the agent writes by reading the sources and synthesising them. A page about a person, a project, a technical concept, or a recurring question. These pages are dense, cross-linked with [[wikilinks]], and meant to be re-read and revised. This is the layer the agent owns and continuously improves.
3. The schema — CLAUDE.md / AGENTS.md
The rules of the wiki — its conventions, its tag taxonomy, how pages should be named and structured — live in CLAUDE.md (the maintainer guide that lettuce wiki init scaffolds) and/or an AGENTS.md brief. Lettuce surfaces these to every connected agent through the get_vault_info tool, which returns the two files concatenated as a single brief. So the first thing an agent learns when it connects is how this particular wiki wants to be maintained.
CLAUDE.md and AGENTS.md play the same “schema” role — the names just match whichever agent tool you use. get_vault_info surfaces both. Only the root brief is surfaced; there is no per-folder AGENTS.md.
Two structural files tie the layers together:
index.mdis the curated catalog — the human-readable home page that links out to the wiki’s pages. When you publish the vault,index.mdbecomes the site’s front page.log.mdis the append-only, greppable ledger. Every ingest and every filed page leaves a dated, parseable line (## [YYYY-MM-DD] ingest | <title>), so you can reconstruct what was added and when with plaingrep.
Scaffolding a wiki
lettuce wiki init [dir] [--force] [--agents]
lettuce wiki init scaffolds exactly four files into the directory:
CLAUDE.md— the maintainer guide (the schema).index.md— the catalog / home page.log.md— the append-only ledger.sources/README.md— explains the raw layer.
With --agents it also writes a tailored AGENTS.md (vault-etiquette brief generated from whatever the directory currently contains). --force overwrites files that already exist. It does not write any .lettuce/*.yml config.
wiki only takes init. The actual work commands — ingest, search, lint, slides, agents — are top-level lettuce subcommands, not subcommands of wiki.
The operations
INGEST — turn raw material into compiled pages
Ingest is a two-step move: get the raw source in, then compile it.
Capture the source.
lettuce ingest <url | file | dir>takes a URL, a local file, or a directory (markdown, text, HTML, PDF, docx, odt, rtf, epub, org, rst, or latex), converts it to clean Markdown — readability for web pages, PDFKit text extraction for PDFs,pandocfor the other formats — downloads the article’s images, writessources/<slug>.md, and appends a dated entry tolog.mdif one exists.Compile + cross-link. The agent reads the new source and writes the durable pages. For an entirely new page,
file_pageis the workhorse: in a single call it writes the page, links it underindex.md’s## Pagesheading (configurable viaindex_section), and appends a datedlog.mdentry — so the catalog and ledger stay correct automatically. To extend an existing page, the agent usespatch_section(append / prepend / replace under a heading or block) orwrite_noteinappendmode, keeping the cross-references right.
So file_page is what makes the wiki compound: every new page is born already cataloged and logged.
When the agent runs under a propose-only key (its writes queue for review instead of committing — see Suggestions mode/01 Overview), a multi-page ingest can be filed as one reviewable unit with propose_batch: the user accepts or rejects all the new pages together instead of one at a time.
QUERY — answer questions, and bank the good answers
Querying is search → read → synthesise, with one extra step that makes the wiki grow:
searchruns full-text search (FTS5/BM25) over note titles and bodies and returns{path, title, snippet, score}—scoreis the BM25 rank, where lower is better. From the shell the same index is available aslettuce search <vault> <query…> [--limit N], which supports"quoted phrases", uppercaseOR/NOT, and a leading-to exclude a term.semantic_searchcomplements it with vector recall — find pages by meaning, not just keyword: it embeds the query and ranks by cosine similarity, with ahybridmode that fuses the two via reciprocal-rank fusion. Build the vector index once withlettuce embed <vault>(any OpenAI-compatible embeddings endpoint, including a local one like Ollama — no key, nothing leaves the machine). Use keyword search for exact names, semantic for “pages about X”.answercloses the loop in one call: it retrieves the most relevant material and asks the model for a grounded, cited answer (from the shell,lettuce answer <vault> <question…>). With embeddings configured it retrieves at passage granularity — ranking individual section-sized chunks and feeding the model just the matching passages (grouped back by source page for citation) rather than whole notes, so the context is tighter and more on-topic. Build the chunk index withlettuce embed.
read_note(andread_outline/link_graphto gather neighbours) pulls the matching pages.The agent synthesises an answer.
File the answer back. When an answer is durable — a question worth keeping the answer to — the agent files it as its own page with
file_page. The next query finds it directly instead of re-deriving it. This is the compounding loop: good answers become pages, pages become future search hits.
LINT — keep the wiki healthy
Two kinds of lint, one deterministic and one LLM-assisted:
lint(andlettuce lint [vault] [--suggest]from the shell) is a fast, offline, deterministic structural check:unresolved-link,orphan-note(no links in or out),no-inbound(has outbound links but nothing points to it — the pattern’s “orphan” page),tag-typo,orphan-tag,untagged-note, plus schema rules (unknown-tag/missing-required-tag/deprecated-tag) whenindex.mddeclares alint:block. Structural files (index.md,CLAUDE.md,AGENTS.md,log.md,sources/**) are exempt from the content rules.lint_semanticis the LLM-assisted health check: it surfaces contradictions between pages, stale claims, important concepts that lack their own page, data gaps, open questions the wiki can’t yet answer, and suggested sources worth ingesting next. The last two are forward-looking — they feed the compounding loop by telling the agent what to write or read next. It’s advisory — it returns findings and applies nothing. It requires an LLM provider to be configured (Settings → AI providers, or an env key); otherwise it returns a clear error.
A companion read tool, suggest_tags, proposes tags for the untagged / missing-required notes that lint flags (also via lettuce lint --suggest). Like lint_semantic, it’s propose-then-confirm and needs a provider.
Putting it together
A day in the life of an LLM-wiki:
lettuce ingest https://…drops a cleaned article intosources/and logs it.The agent reads it, then calls
file_pageto write a concept page — cataloged inindex.md, logged inlog.md, cross-linked to related pages.Later you ask a question. The agent
searches,read_notes the hits, answers — andfile_pages the answer so it’s there next time.Periodically,
lintcatches broken links and orphans;lint_semanticflags a contradiction between two pages, which the agent reconciles and records inlog.md.
The result is a vault that gets denser and more useful the more you feed it — and stays inspectable, because everything is plain Markdown, the catalog is one file, and the ledger is greppable.
See also
Core concepts/04 Wikilinks, backlinks, and the graph — the cross-linking that holds the wiki together
Agent reference/01 Overview — what agents can do, and the trust model
CLI cookbook/07 lettuce wiki —
wiki init,ingest,search,lint,slides