What this is
No new concepts in this lesson. This is the course's capstone: a small, real FastAPI service built entirely out of ideas from Lessons 1 through 24, combined into one thing. If you can read lesson.py and understand why every piece is there, you've mastered this course. If any piece feels unfamiliar, that's a sign to revisit the lesson it came from.
What it does
Ingests every fixture note into a chromadb collection at startup, then serves a GET /ask endpoint that retrieves relevant chunks (dropping anything under a similarity threshold), cites its sources, and admits when it doesn't know something, the same behavior as Lesson 15/18, now running on chromadb instead of a Python list, and reachable over HTTP.
Where each piece came from
@asynccontextmanagerasync def lifespan(app: FastAPI): chroma_client = chromadb.Client() app.state.collection = ingest(NOTES_DIR, chroma_client)Lesson 24 (the service shell) plus Lesson 23 (ingest(), run once at startup).
results = collection.query( query_embeddings=[query_vector], n_results=k, include=["documents", "distances", "metadatas"],)Lesson 21 (chromadb-backed retrieval), extended to also request distances and metadatas, both needed below.
relevant = [ (doc, meta["source"]) for doc, distance, meta in zip(documents[0], distances[0], metadatas[0]) if (1 - distance) >= MIN_SCORE]Lesson 14's threshold (MIN_SCORE), reimplemented against chromadb's distance instead of this course's own cosine similarity. Since chromadb's distance is smaller-is-more-similar (Lesson 20) and this course's threshold was always phrased as similarity, 1 - distance converts back to the same direction before comparing.
if not relevant: return "I don't have any information relevant to that question."Lesson 15 (skip the API call when nothing clears the bar).
context = "\n\n".join(f"[Source: {source}]\n{doc}" for doc, source in relevant)Lesson 15/22 (metadata carried alongside each chunk, here source from chromadb's own metadatas instead of a Python dict field).
Rules:- If the context doesn't contain the answer, say "I don't have information about that."- Every claim in your answer must cite which source it came from...Lesson 15's grounded, citation-requiring prompt, unchanged.
Try this yourself
Without looking anything up:
- Lower
MIN_SCOREand confirm the France question starts returning an (incorrect) attempt at an answer instead of the fallback message. - Add a new fixture
.mdfile and confirm a question about it gets ingested, retrieved, and cited correctly without any other code change. - Run
uvicorn lesson:app --reloadfrom this folder and hitGET /ask?q=...from a browser orcurl, confirm it behaves identically to theTestClientcalls in the script.
This is where Naive RAG, built entirely from scratch, ends up: a small, real, citation-aware service. Lesson 26 is a short, code-free look at where this specific architecture still falls short, and which course in this series picks up each of those threads.