A production RAG pipeline for semantic code search: field notes from JetBrains
In the second part of its Air Context series, JetBrains explains how it stores millions of code embeddings and retrieves the right ones in milliseconds. The post covers the pitfalls of filtered approximate search, incremental indexing that keeps up with Git, and why delegating retrieval to a read-only sub-agent saves context.
Key points
- ANN indexes such as HNSW trade recall for speed; a hybrid pass with exact search over a small candidate set restores precision.
- Filters fight ANN graphs: pre-filtering keeps 100% recall relative to the filter but loses the index and falls back to exact search.
- pgvector's iterative-scan mode softens weak post-filtering at the cost of unpredictable latency.
- Incremental indexing is built on a Merkle tree mirroring the directory layout plus deduplication by chunk content hash.
- HNSW builds run asynchronously after a snapshot is fully stored, at most one at a time per shard, so queries hit a stable database.
- Retrieval is delegated to a capped, read-only sub-agent that can run on a cheaper model, keeping the main agent's context small.
The second post in JetBrains' Air Context series moves past chunking and vectorization to the storage and retrieval layer. Approximate nearest-neighbor indexes alone are not enough: because permission and repository filters can disconnect an HNSW graph, the team weighs pre-filtering, metadata bundled into the index (as Qdrant and turbopuffer do) and post-filtering, where pgvector's iterative-scan mode trades latency for recall. Their answer is a hybrid: a cheap ANN pass narrows the corpus, then an exact comparison over a small candidate set handles precision.
On the indexing side, directory snapshots are clustered into a Merkle tree and embeddings deduplicated by chunk hash, so only changed clusters are reindexed; heavy HNSW builds run asynchronously after a snapshot is stored, one build per shard. Retrieval results are reranked, and a read-only sub-agent handles search so the main agent's context stays small.
Real-world use
- Building an internal code search tool or MCP server where filtered vector search has to respect repo and folder scope
- Enforcing per-tenant and per-path permissions in a multi-tenant developer platform without breaking the vector index
- Keeping a search index fresh on a large monorepo with constant commits instead of reindexing everything
- Handing code discovery to a dedicated sub-agent in Claude Code or similar to cut token spend and keep the main context clean
Why it matters
Agent quality depends heavily on pulling the right context, and these notes show which retrieval trade-offs actually bite at production scale.