Embeddings and Vectorize
Query, filter, and evaluate retrieval
Embed the query, apply tenant metadata before top-k selection, fetch authorized source text, and measure whether the results are useful.
8 minute lesson
Querying reverses the ingest path. Generate a query embedding with the same model, then call the Vectorize binding:
const { data } = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: 'how do I cancel a streaming response?',
})
const matches = await env.VECTORS.query(data[0], {
topK: 5,
filter: { tenant: 'acme' },
returnMetadata: 'indexed',
})
The same model requirement is absolute. A query embedded with a different model than the documents returns results that look plausible and mean nothing, because the two vector spaces are unrelated.
Filter before ranking
Use namespaces or indexed metadata to prevent cross-tenant retrieval before choosing the nearest results. The filter above narrows the candidate set first, then the nearest neighbors are selected inside it.
The order is a security property, not an optimization. Query first and filter the matches afterward and you built the leak: tenant B’s document was the nearest neighbor, got selected, then got dropped — and on a different day it survives into the prompt. Filtering after top-k also silently shrinks results, returning two matches when you asked for five.
Return only the metadata you need and load the source body from its authoritative store. The index holds pointers; D1 or R2 holds the text. That read is also where authorization happens — check the requesting user may see the document before its content enters a prompt.
Measure retrieval alone
Similarity is not correctness. A cosine score of 0.82 tells you the vectors are close, not that the chunk answers the question.
Build a small labeled question set and measure whether the expected chunks appear before adding generation:
const cases = [
{ question: 'how do I cancel a streaming response?', expect: 'lesson:cloudflare-ai/streaming#2' },
{ question: 'which metric does the index use?', expect: 'lesson:cloudflare-ai/indexes#1' },
]
Run each question, check whether the expected ID lands in the top five, and record the hit rate. Retrieval failures are invisible after generation is added — the model fills gaps with fluent guesses, and the answer looks fine until someone checks it.
Test ten questions, record retrieval hits and misses, then change chunking or filters based on evidence. One variable at a time: re-chunk, re-run the ten, compare. That loop is the whole discipline of retrieval work.
Lesson completed