Where we left off
This is a short recipe lesson, not a new-concepts lesson. naive_rag Lesson 24 already taught FastAPI's lifespan hook, app.state, and TestClient, in depth, this lesson reuses that exact pattern almost verbatim, the only thing that changed is which ingest()/ask() pair gets wired in. If any of lifespan, app.state, or TestClient feel unfamiliar, go read naive_rag Lesson 24's README first, this one won't re-teach them.
What actually changed from naive_rag Lesson 24
@asynccontextmanagerasync def lifespan(app: FastAPI): app.state.corrective_state = ingest(NOTES_DIR) yieldOne line: ingest() now returns Lesson 23's CorrectiveState (collection + grader) instead of a bare chromadb.Collection, and it's stored on app.state.corrective_state instead of app.state.collection. Everything else about the lifespan hook is identical.
@app.get("/ask", response_model=AskResponse)def ask_endpoint(q: str, k: int = 2) -> AskResponse: return AskResponse(answer=ask(q, app.state.corrective_state, k))Also identical in shape to naive_rag Lesson 24's one route, ask() itself does more work now (grading, filtering, rewriting), but the route calling it doesn't need to know that, it's still one function call.
Checkpoint
- This lesson intentionally doesn't re-teach FastAPI, see
naive_ragLesson 24 for that depth. - The only real change:
app.statenow holds aCorrectiveState(Lesson 23), not a bare collection, andask()internally grades and corrects before generating, invisibly to the route handler. - Lesson 16's grader-circularity demo, not this lesson, is where this course's real depth lives, this lesson exists to show the pipeline is service-shaped, nothing more.
If anything here still feels unclear, ask before moving to Lesson 25.