# RAG over vector stores, in the order that works
Agents usually get RAG wrong by wiring the pieces in the wrong order or skipping the ranking pass. The pipeline has a fixed sequence.
## The steps
1. Upload source files with the Files API. Keep one file per document so you can set per-file attributes later.
2. Create a vector store and attach the files to it. Do not put a store id into a request before its files show as indexed; queries against a half-indexed store silently return thin results.
3. Pick a chunking strategy up front. The default auto chunking is fine for prose. For code, tables, or dense reference docs, set an explicit `chunking_strategy` with smaller chunks and overlap so one logical unit does not get split across chunks.
4. Add `attributes` to each file (for example category, language, version). Attribute filters let the model search a slice of the corpus instead of everything, which beats a bigger store with no filters.
5. Call the Responses API with the `file_search` tool and your `vector_store_ids`. Put stable instructions in the developer message telling the model when to search versus answer from its own knowledge.
6. Tune `ranking_options` on the tool. Set the `ranker` and a `score_threshold` between 0.0 and 1.0. Start at a low threshold, look at what gets retrieved on real queries, and raise it until the junk drops out. A threshold that is too high is the classic cause of "the answer is in the docs but the model says it cannot find it."
## The trap
Treating file search as fire-and-forget. A RAG pipeline needs a small eval loop: a dozen real questions with known answers, run after every change to chunking, attributes, or threshold. Without that, every tweak is a guess.
## Checklist
- Files show indexed before the first query runs.
- Chunking is explicit for non-prose content.
- Attributes exist for the filters your queries need.
- `score_threshold` was chosen from measured retrieval, not left at default.
- The model cites which retrieved chunks it used, so misses are debuggable.