How it's built · v0 · predicted, not yet measured

A RAG comparison I can actually grade

Seven retrieval patterns, one corpus that happens to be my own bookshelf, six fixed questions with reference answers written before any pipeline runs, the same generation model everywhere. This page is the lab notebook: what I decided, what I turned down, and what replaces prediction with measurement as each version ships.

I build the way I learn: make the thing, then find out where it breaks, and publish the finding-out. Version 0 is the design and the predicted traces. The measurements start at v1. Every claim on the comparison page that is currently a prediction is labelled as one, and gets a date here when it becomes a measurement.

7patterns, from the same seven-diagram picture everyone shares
3files in the corpus: a Goodreads export, Kindle highlights, a list of recommendations
6fixed questions, one per failure mode, graded by hand
0measured runs so far. That number is the roadmap.

1 · The problemSeven diagrams and no way to tell them apart

The seven-pattern RAG picture is everywhere in 2026, and it is right that these are different designs. What it does not give you is a way to choose. Every pattern is shown on its own demo, so any comparison is really a comparison of demos: different data, different questions, different models, different definitions of "worked".

The question I wanted to answer is the one a team asks before they build: which of these do we actually need, and how would we know? Answering that means holding everything still except the pattern.

2 · Why books, and why mineYou can't evaluate retrieval in a domain where you can't tell right from plausible

The corpus is my reading life: what I read and when, what I rated it, the lines I highlighted, who told me to read it. Not because the world needs another chat-with-your-highlights app, but because evaluation is the hard part of RAG and I can only grade answers in a domain I know cold. When a pipeline says I read a book in 2023 that I read in 2021, or quotes a line back from the wrong book, or recommends something already on my read shelf, I know in one second. In an enterprise-docs demo I would have had to guess, or pay a model to guess for me.

The second reason is that the data has, naturally, the properties that separate the seven patterns. I did not have to invent them; I had to make sure the export kept them.

propertywhere it comes fromwhich pattern it separates
Numbers in a ledger, not in textdates read, ratings, page counts in the Goodreads exportpatterns with a tool from patterns without
Sources that disagreemy review, my own highlights from the same book, and the crowd's average ratingnaive from reranked
Knowledge in relationshipsa highlight in one book naming the author of another; who recommended whatvector from graph
Answers only in imagesphotographed pages and coverstext from multimodal
Questions needing several sourcesa reading list built from shelf, page counts, recommendations and abandoned bookssingle-path from multi-agent

The earlier draft of this site used a handstand-coaching notebook I would have had to write from scratch. Real, exported data won on every count: it took an afternoon instead of a week, it cannot be accused of being built to flatter a pattern, and its failures are legible to anyone who has ever kept a reading list.

3 · The trade-offsDecisions, the alternative, and what each one costs

decisionchoseinstead ofwhy
CorpusMy own exports, curatedA fabricated corpus, or scraped reviewsI know every fact in it, and it is real. Cost: it is small, and small corpora flatter naive RAG; the question set compensates.
Full textHighlights onlyWhole booksA short quotation with attribution can be published; a book cannot. Highlights are also the more personal signal.
RecommendersInitials, from a hand-written CSVFull names, or nothingThe people edges make the graph interesting. The people themselves stay off a public page.
The ledgerKept as rows in SQLite, queried by a toolEmbedding every row as textEmbedding a ledger is how a system invents a book. Keeping it as rows is what makes the Q2 column an honest test of "do you have a tool or not".
Question set6 fixed questions, one per failure mode50 random questionsEach question is a diagnostic: it should turn one pattern from wrong to right. Random questions average that out. More questions come at v5, once the pipelines run.
v0 as predictionsPredicted traces, labelled as suchWaiting for live pipelinesThe design thinking is the product; the numbers are the proof. Shipping the thinking first, honestly labelled, beats shipping nothing. Cost: a reader who skips the label will think these are results.
Graph constructionLLM extraction with a fixed schema (Book, Author, Person, Theme; WROTE, RECOMMENDED, ABOUT, MENTIONS, ARGUES_WITH, READ_AFTER)A hand-built graphA hand-built graph hides the real failure mode of Graph RAG, which is the extractor missing an edge. I want that failure to be possible, visible, and published as JSON.
RerankerA local cross-encoderAn LLM reranking callCheaper per query, and a second LLM in the loop makes it harder to attribute a fix to retrieval rather than to more model.
Generation modelOne model, one prompt template, every patternThe "natural" model for each patternChange one thing at a time. If the model varies, the comparison is of models.
GradingHand labels against a written reference, plus an automatic invented-book countAn LLM judge6 questions × 7 patterns × 5 runs is 210 answers. I can read 210 answers. A judge needs its own validation set, which at this size costs more than reading.
HostingStatic pages now; a small Python service for the pipelinesA full app from day oneStatic pages deploy in seconds and cannot go down. The service only needs to exist for the eval runs and, at v5, the demo box.

4 · The corpusThree files, a few photos, and a graph extracted from them

sourcewhat is usedwhat is never used
Goodreads exporttitle, author, ISBN, pages, publication year, date read, date added, my rating, average rating, shelves, my review, read countprivate notes, and any title I remove before ingest
Kindle highlightsthe highlight text, its book, location, and date; my notes on itbookmarks; clippings under 20 characters
Recommendationstitle, who (initials), where, when. Written by hand.the mapping from initials to people, which stays on my machine
Photoscovers from Open Library, a few pages I photograph, one shelf
Extracted graphedges from the files (who wrote what, who recommended what, what I read after what) and edges a model extracts from highlights and reviews (themes; which books mention or argue with which)nothing; the whole graph is committed as JSON

The highlights are the semantic index and the ledger is a table. That split is the single most important design decision on this site: it is what makes the "numbers" question an honest test rather than a trick.

Exact counts (books, highlights, match rate between the two exports) are published here at v1, after ingest. The curated exports themselves stay out of the repo; the photos, the graph JSON, the question set and every result CSV go in.

5 · The question setSix questions, each built to break one pattern and be fixed by the next

questiontestsexpected to separate
Q1Where did I read the line about …?a quotation in the indexnothing. A control, unless a same-author highlight from another book outranks it
Q2What did I read in 2024, rated five stars, under 300 pages?a filter over the ledgerpatterns with a tool (right) from patterns without (invented titles)
Q3Why did I give [book] two stars when everyone else loved it?my review vs my highlights vs the crowdnaive (wrong) from reranked (right)
Q4Which of my books argue with each other?a relationship between two booksvector (partial) from graph and hybrid (right)
Q5[photo] Which book is this, and what did I highlight near it?an answer that starts from pixelstext-only (partial) from multimodal (right)
Q6A two-week holiday list from my to-read shelf, under 400 pages, one from a friend, nothing like the last three I abandonedcomposing four sources into a deliverableevery single-path pattern (partial) from multi-agent (right)

Reference answers are written before I trace a single pipeline and are shown on the comparison page next to each question. The bracketed titles get fixed once the export is inspected. A second reader grades a sample before v5; the agreement rate gets published as the ceiling, as I did for fixmybanana.

6 · How every version is judgedRead every answer first, then count

fixed question set6 questions, referenced run 5× per patternsame model, same prompt read every answerbefore any metric publish the gridbad cells included same questions, same grid, every version

What gets recorded per answer

measurehowwhy it matters
Verdictcorrect / partial / wrong against the reference, by handthe headline cell in the grid
Invented booksany title in the answer that is not in the library, counted by codethe failure a reader can feel; the check that catches what I miss
Retrieval hitdid the chunk or row holding the reference answer reach the model?separates "retrieval failed" from "the model ignored it"
Consistencysame verdict on all 5 runs?a pattern that is right 3 times in 5 is not right
Model callscounted by the codestructural cost, independent of vendor pricing
Latency and $wall clock and token usage per runthe columns that decide what ships

The eval calls the same function the site's demo box will call, so what is measured is what a visitor gets. The question set does not change between versions, so the only thing that moves the grid is the change I made.

7 · VersionsWhat exists, what is measured, and when

v0Sep 2026

Predicted traces. Corpus chosen and its rules written, six question shapes fixed, seven pipelines traced by prediction on all six, the scorecard and decision guide published. No code has run. Every score on the comparison page is a design argument, not a measurement.

v1next

Ingest, naive, reranked, and the ledger tool, live. Parse both exports, build the tables, embed the highlights, add the cross-encoder and the SQL tool. Fix the real titles in the questions. Run 5× each. The first two rows of the grid become measurements, and Q2 gets its first real answer through the tool so the number exists early.

v2

Graph and hybrid. LLM extraction over highlights and reviews with the fixed schema, then traversal. Publish the extracted graph so a missed edge is visible. The Q4 column becomes real.

v3

Multimodal. Multimodal embeddings for the photos, vision-language generation. The Q5 column becomes real.

v4

Router and multi-agent. A small routing model over the five retrievers; then a planner, sub-agents and a synthesizer. The Q6 column, and the cost column, become real.

v5

The numbers. Full grid with latency and cost per cell, consistency across runs, invented-book counts, a second reader's agreement rate, ten more questions drawn from how people actually ask, and a live "ask the librarian" box with the trace panel. The "predicted" label comes off the comparison page.

8 · The stackSmall on purpose

partchoicenote
These pagesPlain HTML, inline CSS, no build stepCloudflare Pages, deploys on push. Same as my other sites.
Pipelines (v1+)Python service, five retrievers, seven patterns behind one interface: answer(question, pattern, image) → {answer, context, route, calls, ms, usage}the eval imports the same function the demo box calls
LedgerSQLite, queried by the SQL toola few hundred rows; never embedded
EmbeddingsVoyage text embeddings; the multimodal model for v3; vectors stored in the same SQLite fileone file, no vector database to run
RerankerA local cross-encoderno API call, so its cost shows up as milliseconds, not dollars
GraphNetworkX in memory, extracted by Claude with a fixed schema, committed as JSONa graph database would be a fourth thing to run for a graph with under a thousand nodes
GenerationClaude Opus 5 with adaptive thinking, one prompt template for every patternthe router uses Claude Haiku 4.5: it is a classifier and should cost like one
Hosting the serviceRailway, Cloudflare in frontthe setup I already run for fixmybanana
Built by Shirley · v0, September 2026 · updated when a version ships · ← Seven RAGs, one shelf · Source