ContextOS

Hybrid Retrieval

Combining dense semantic search and exact-keyword lexical matching.

ContextOS implements Hybrid Retrieval out of the box because single-strategy retrieval is almost never sufficient for production applications. While vector embeddings are excellent at capturing semantic meaning, they struggle significantly with out-of-vocabulary terms, specific names, UUIDs, or exact error codes.

The Dense Strategy (pgvector)

For semantic search, ContextOS uses the SemanticRetriever. When an item is added to the system, it is automatically passed through an EmbeddingService (either a local model like BGE or an external API like OpenAI).

The embeddings are stored in PostgreSQL using the pgvector extension. At retrieval time, the user's query is embedded, and ContextOS performs a cosine-similarity search using HNSW indexes to find the top K nearest neighbors.

Configurable: You can tune how many dense candidates are retrieved via the dense_candidate_k setting in your configuration.

The Lexical Strategy (BM25)

To complement semantic search, ContextOS includes a Bm25Retriever. This relies on the BM25 (Best Matching 25) algorithm, which ranks documents based on term frequency and inverse document frequency.

If a user asks "Why did task 27a44e4c fail?", the dense model might not understand the UUID, but BM25 will instantly surface the exact log containing that ID.

Reciprocal Rank Fusion (RRF)

Retrieving candidates from two entirely different systems presents a major problem: how do you combine their scores? A cosine distance score of 0.82 cannot be mathematically compared to a BM25 score of 15.4.

ContextOS solves this by using Reciprocal Rank Fusion (RRF). Instead of comparing absolute scores, RRF compares their relative rankings across the two lists.

The formula implemented in ContextOS is:

RRF(d) = 1 / (k + rank_dense(d)) + 1 / (k + rank_bm25(d))

Where k is a smoothing constant (default: 60). By ignoring raw scores and focusing on ranks, ContextOS seamlessly merges the candidate lists into a single, unified pool ready for Reranking.

Disabling BM25

If your use case does not require exact keyword matching and you wish to minimize database complexity, you can disable BM25 entirely by setting:

enable_bm25=False