Where we left off
Same wiring as naive_rag Lesson 24, applied to this course's hybrid ingest()/ask() from Lesson 23 instead of a dense-only pair. If you've done that lesson, nothing here is new: lifespan builds the index once at startup, app.state holds it for every request, one GET /ask route calls ask() unchanged. This lesson is deliberately a short recipe, not a re-teach, the plumbing is identical to naive_rag Lesson 24, only ingest()/ask()'s internals (hybrid instead of dense-only) differ.
The one real difference
@asynccontextmanagerasync def lifespan(app: FastAPI): app.state.hybrid = ingest(NOTES_DIR) yieldapp.state.hybrid holds a HybridState (Lesson 23), both indexes, instead of a single chromadb.Collection. Everything downstream, the route handler, ask() itself, is otherwise the same shape as naive_rag's service.
Checkpoint
- Same
lifespan/app.state/TestClientpattern asnaive_ragLesson 24, wrapping a hybridask()instead of a dense-only one. - The route handler never needed to know retrieval underneath is hybrid, it just calls
ask(), exactly the payoff Lesson 23's function boundary was built for.
If anything here still feels unclear, ask before moving to Lesson 25.