Chapter 10 · the search loop, given a home
Two things break at scale:
A vector store fixes both.
class VectorStore:
def add(self, docs):
vecs = self._embed([d["text"] for d in docs])
for d, v in zip(docs, vecs):
self._items.append({**d, "vec": v})
def search(self, query, k=3, where=None):
qv = self._embed([query])[0]
pool = [it for it in self._items
if not where or matches(it, where)]
return top_k_by_cosine(qv, pool, k)embed function → same class for OpenAI + Gemini"Like this query, but only billing, only this customer, only since March."
Still a linear scan. A real database (pgvector, Qdrant, Pinecone) adds:
Same add / search interface.
billing: narrows, then rankOne shared store.py; only the corpus + query differ.
The embedding model is part of your schema.
Change it → every stored vector is meaningless → re-embed the whole corpus. Version it.
A vector store is add() + search() with vectors kept beside the text. You now have RAG's retrieval half. Next: hand the passages to a model and answer from them, with citations.