September 12, 2026
BUILD A SMALL RAG SYSTEM YOU CAN INSPECT

Lesson 23.1 defined the workflow: retrieve, supply, answer with links. Now you turn that definition into a bounded first build — small enough to test, transparent enough to debug.
Start with ten documents, not ten thousand
Your first corpus should be deliberately small: ten product documents, a handful of public reports, or one internal manual you have permission to use. Small is a feature. You can read the whole corpus yourself, you know what a correct answer looks like, and you can tell when retrieval drifts.
Pick material with a clear owner and audience. A support manual for one product, a benefits policy set for employees, or a folder of dated Sonariq research notes on two companies — each works because the boundary is obvious. If you cannot name who may read a document, you are not ready to retrieve it for them.
Resist the urge to ingest the company drive "so the AI knows everything." A RAG system that answers from everything answers accountably from nothing.
The eight-step build sequence
Build in this order. Each step produces something you can check before moving on.
1. collect and clean sources
2. preserve source URL, owner, date, and permissions
3. chunk and embed
4. store chunk + metadata
5. retrieve with metadata filters
6. send only selected material to the model
7. render answer and sources
8. log question, retrieved chunks, and answer for evaluation
1. Collect and clean. Gather the source files, strip navigation chrome and duplicates, and confirm you have the current version. Keep the original alongside the cleaned text so you can verify later what the model actually received.
2. Preserve provenance and permissions. For every document, record the source URL or file path, owner, publication or revision date, and who may see it. This follows the Part VI rule: every fact keeps its URL, owner, date or period, unit, and retrieval time. Permissions recorded here become filters later — not suggestions.
3. Chunk and embed. Split documents into coherent chunks, as taught in Class 22: a policy section, a manual procedure, one Sonariq research section — not arbitrary 500-character slices. Then create an embedding per chunk for similarity search. Remember the tradeoff: chunks too large bury the answer in noise; chunks too small lose the surrounding meaning.
4. Store chunk plus metadata. Keep the chunk text, its vector, and its metadata together — in Postgres with pgvector, the Postgres extension from Lesson 22.3 that adds a vector column and similarity search, if you already run product data there. A conceptual row holds the document title, chunk position, source URL, owner, date, visibility, and product or version ID alongside the embedding. Ordinary columns do the access and freshness work; the vector column does the similarity work.
5. Retrieve with filters first. When a question arrives, apply metadata filters before ranking by similarity: right product, right version, right date range, right audience. Only then take the top candidates. A benefits answer for contractors should never compete with a full-time policy chunk, no matter how semantically close it is.
6. Send only selected material. Pass the small selected set — three to five chunks — to the model with an instruction to answer only from that context and to say when the material is insufficient. Everything else stays out of the prompt. What the model never receives, it cannot leak.
7. Render answer plus sources. Show a concise answer, the supporting quotation or evidence list, and a link per claim that opens the exact section. Source links are product functionality, not decoration.
8. Log everything. Record the question, the retrieved chunk IDs, the answer, and the sources shown. Without this log there is no evaluation, only vibes.
Keep retrieval visible so failures are diagnosable
In development, always display the retrieved chunks beside the answer. This is the single habit that separates builders from prompt-tinkerers.
When an answer is wrong, the visible chunks tell you which half failed:
| Symptom | Likely cause | Fix |
|---|---|---|
| Chunks are irrelevant | Retrieval failure: bad query, bad chunking, missing filter | Fix chunk policy, metadata, or filters |
| Chunks are right, answer is wrong | Generation failure: model drifted beyond context | Tighten instructions, reduce context, require quotations |
| Chunks are empty | Corpus gap or over-strict filter | Add the missing source or correct the filter |
| Chunks are right but stale | Refresh failure: old version still indexed | Retire the old version, re-ingest the current one |
If you hide retrieval behind a chat bubble too early, every failure looks like "the AI hallucinated" and no failure gets fixed.
Give your coding agent bounded tasks
A coding agent can build this quickly if you bound the assignment. Sequence it the way the system runs:
1. Schema first. Create the chunks table with text, embedding, source URL, owner, date, visibility, and product ID. Load a sample of five chunks by hand and read them back. 2. Read-only retrieval endpoint. Add a query path that takes a question, applies metadata filters, and returns ranked chunks with their source links. No model call yet. Verify with three known questions. 3. Answer rendering. Only now add the model call and the answer template with source cards. Confirm every claim in a test answer maps to a displayed chunk. 4. Chat UI last. A conversational interface is polish on top of a tested pipeline, not a substitute for one.
Each stage has a checkable artifact: a readable table, a retrieval log, a sourced answer, and finally the interface. Never let the agent skip to step four.
Filled example: a RAG-PLAN.md you can copy
# RAG-PLAN — Handbook remote-days assistant (v1)
Corpus (10 docs): Employee Handbook §4.2 remote work (2026-07-01),
Benefits Guide §2 (2026-06-15), IT policy §7 logins (2026-08-15),
plus 7 prior dated versions kept OUT of the index for reference only.
Owner: People Ops (people@). Allowed users: full-time employees
(visibility = 'internal-ft'); contractors excluded by filter.
Chunk policy: one policy section per chunk (3–8 sentences each),
section heading prepended so each chunk reads standalone.
Metadata per chunk: document_title, section, position, source_url
(exact section link), owner, updated_at, visibility, product_id
('handbook'), language ('en').
Test Q1: "How many remote days per month?" → expect Handbook §4.2
chunk "Remote-day allowance (two days/month)" + Benefits §2
"Requesting days" chunk.
Test Q2: "How do I reset my login?" → expect IT §7 "Resetting your
password (steps 1–4)" chunk.
Test Q3: "What changed in August 2026?" → expect changelog chunk
dated 2026-08-15, superseding pre-2026-08-01 chunks.
Failure plan: no relevant chunk → decline with checked-sources list;
stale chunk retrieved → retire old version, re-ingest current.
Refresh: owner reviews monthly; version bump retires prior chunks.
Your plan should be this specific: a builder reading it never invents a source.
The finish line
You are done with version one when a user can ask three known questions and inspect the exact evidence used for each: the chunk text, the source title, the date, and a working link. If a colleague can open the source from the answer and confirm the quotation, the system is grounded. If they cannot, it is a demo.
Practical exercise: plan a small inspectable build
Create RAG-PLAN.md with: the corpus (named documents), the source owner, allowed users, the chunk policy (unit and approximate size), the metadata fields stored per chunk, three test questions with the expected source passages, and a failure and refresh plan (what happens when a document changes or a question has no evidence).
Finish line: a RAG-PLAN.md specific enough that a coding agent could build the schema and retrieval endpoint without inventing a source.
Verify: hand the plan to a fresh AI session and ask which three test questions it would run and which source passage each should retrieve. If it cannot answer from the plan alone, the corpus or metadata section is too vague.
Common failure mode: adding the chat interface before the read-only retrieval endpoint is tested — every failure then looks like "the AI hallucinated" instead of a diagnosable retrieval, generation, or refresh fault. Schema, retrieval log, sourced answer, interface — in that order.
Check your understanding
1. Why is ten documents a better first corpus than ten thousand? 2. Which metadata must be preserved before chunking, and why? 3. Why do filters run before similarity ranking? 4. How do visible chunks tell a retrieval failure from a generation failure?
ARTICLE DISCUSSION
JOIN THE
CONVERSATION.
Got a question, a take, or a better way to do this? Log in and leave a comment.
