Build your own RAG pipeline
Assemble, generate, cite·25 min

Generate via cassette

0 / 1
# grade this stage (from your course directory)
npx theinference test --stage cassette-generate --watch

Now generate — without ever calling a live LLM. The prompt's sha256 hash keys into cassette.json, which returns the recorded answer for that exact prompt (and throws on anything else). This is the determinism boundary: same prompt → same answer, forever, offline.

What to build

Your task

Implement generate + parseCitations

Implement generate(prompt, cassette): hash the prompt with the provided promptHash, look it up, return the recorded answer (throw on a miss). Implement parseCitations(answer, usedChunkIds): parse [doc i] markers and map each to its chunk id via usedChunkIds[i-1]. The generate <q> <k> <budget> command assembles, prompts, generates, and prints {answer, citations}.

src/cassette.ts / src/assemble.ts
export function generate(prompt: string, cassette: Cassette): string {
  const entry = cassette[promptHash(prompt)];
  if (!entry) throw new Error("cassette miss");
  return entry.answer;
}
export function parseCitations(answer: string, usedChunkIds: string[]): string[] {
  // match /\[doc (\d+)\]/g; idx = n-1; if in range -> usedChunkIds[idx]
}
How the grader runs it
./your_program.sh generate 0 5 200
# -> { answer: "1887 [doc 1].", citations: ["eiffel-tower#p1::0-31"] }
How it's graded

process mode, stdout_json_match. The answer text and the parsed citation ids must match the reference exactly. A cassette miss throws (non-zero exit) — which fails the step — proving you assembled the exact prompt the cassette was recorded against.