RAG Formation · Lesson · Foundations & measurement
Trace one question through the real code
Follow one request from the API boundary to retrieval, context assembly, generation, citation, and evaluation.
$ lesson --status
▸ course RAG Formation
▸ lesson 02 / 16
▸ phase Foundations & measurement
▸ status Complete
● build → measure → learnStandalone lesson
Learn the full lesson and test your understanding here.
The 16-lesson RAG Formation curriculum is complete. This page contains the complete lesson content, local reference material, and instant browser exercises.
Today’s tangible win
By the end of this lesson, you'll be able to point at the exact file and function behind every stage of a single `/api/v1/qa` request.
Watch the walkthrough
1 · The question we're tracing
Question:
“Quel est le délai de clôture d'un plan d'action pour une non-conformité majeure ?”
Expected source: `PR-QA-MRD-009`
Expected fact: `45 jours`
We're not going to read every file top to bottom. Instead, we'll follow the request from the outside in, and only stop to look at the functions it actually touches.
2 · The call chain
Client
↓ `POST /api/v1/qa`
`routes/qa.py` → `ask_question(request)`
↓ delegates
`qa_service.py` → `answer_question_internal(request)`
↓ runs four retrieval paths
`search_qdrant()` · `search_meilisearch()` · `query_lightrag_context()`
↓ assembles evidence (Which function decides which source items survived?)
`utils.build_context()`
↓ derives citations (Which function converts surviving items into citations?)
`utils.sources_from_items()`
↓ builds prompt and generates
`utils.build_prompt()` → `completion.generate()`
↓ returns
`models.AnswerResponse`
Keep the request-path cheat sheet open in another tab while you work through this.
3 · What each stage actually does
| Stage | Current implementation | Why it matters |
|---|---|---|
| Route | routes/qa.py validates the incoming request with Pydantic, then hands off. | Routes stay thin on purpose; the real logic lives elsewhere. |
| Orchestrate | qa_service.py checks the services are up, cleans up the question, reads backend config, and kicks off retrieval. | It's the coordinator for the whole request, and the best place to start an end-to-end trace. |
| Retrieve | Qdrant hands back semantic chunks and summaries, Meilisearch returns keyword matches, and LightRAG supplies graph context. | If evidence is missing, check retrieval before you blame the prompt. |
| Assemble | build_context() splits the budget fairly across channels and drops whole items rather than truncating them. | Whatever doesn't survive this stage, the model never gets to see. |
| Cite | sources_from_items() only looks at the items that actually made it into the prompt. | A citation should never point to evidence the model never saw. |
| Generate | build_prompt() instructs the model to stick to the given context and refuse anything unsupported; the completion service then writes the answer. | Even evidence that's right there can still get skipped, muddled, or contradicted. |
4 · Reconstruct the call chain — from memory
Close the call-chain diagram in section 2. Without scrolling back to it, fill in this table for the question we're tracing — recall beats re-reading for making this stick:
| Stage | Function |
|---|---|
| Route | __________ |
| Orchestrate | __________ |
| Retrieve (three functions) | __________ |
| Assemble (decides what survives) | __________ |
| Cite (never cites dropped evidence) | __________ |
| Generate | __________ |
Check yourself against section 2. Anything you couldn't recall is exactly what to re-read before moving on — that gap is the point of doing it this way round.
5 · Verify against the running system
The call chain lives in api/, which isn't part of this
free course — but the running API is, via the demo stack. Ask
it the traced question directly and confirm your trace against the
real response, with demo/setup_demo.py up running:
curl -s -X POST http://localhost:8000/api/v1/qa \
-H "Content-Type: application/json" \
-d "{\"question\": \"Quel est le délai de clôture d'un plan d'action pour une non-conformité majeure ?\", \"workspace\": \"meridian_demo\"}"
In the JSON response: sources should include
PR-QA-MRD-009, and answer should state
45 jours. If either is missing, you've just found a real
failure to diagnose with the trace you built in section 4 — was it a
retrieval miss or a generation miss?
6 · Check your reconstruction
So — which function actually does the final context assembly?
Why does `sources_from_items(context_items)` matter so much?
If `/api/v1/search` doesn't turn up `PR-QA-MRD-009`, which family of functions should you dig into first?
Mission connection
For an auditor or QA professional, an answer is only as trustworthy as the path you can explain behind it. That's exactly why a simple call map pays off when something breaks: it tells you where to look first — the index, the context budget, the citation logic, or the model's output.
Next action
Fill in all six blanks in the data-flow table. Then answer this in a single sentence:
Why might a source show up in the raw retrieval results but never make it into the final API citations?
Ask me follow-up questions about anything — any function, any stage — that's still fuzzy. Once you've filled in the six blanks and answered the final question, Lesson 2 is done — move on to whichever lesson you want next.