Vector & full-text fused for agentic retrieval

Keyword and vector retrieval fail in opposite directions: one misses the paraphrase, the other misses the exact id. Infino powers hybrid search that runs both over the same rows and fuses the rankings, providing the highest accuracy retrieval.

BM25 vector hybrid_search SQL
cat WHY_HYBRID.MD

Retrieval modes measured by accuracy

Hybrid is consistently one of the most accurate forms of agentic retrieval, and Infino is one of the few engines that runs it natively.

mode accuracy
keyword 40.6
vector 45.0
hybrid 48.4
hybrid + rerankinginfino 50.0

Sources: Azure AI Search, 2023 ↗  ·  BEIR ↗  ·  RRF, SIGIR 2009 ↗

infino explain cost

Why hybrid is expensive, and why it isn't here

Running both retrievers on a search engine or a vector database means keeping the corpus and its vectors resident on hot block storage, in nodes that stay on. Infino reads the same rows from your bucket and meters the query work, so the bill tracks traffic.

engine per month
Infino $9,715
OpenSearch $147,172
Qdrant $191,918
Elasticsearch $230,682

5B documents, 10M queries a month, 1 KB rows, each engine holding the text and a 1024-dimension vector. Model your workload →

cat hybrid.sql

Retrieval results are returned as a table

hybrid_search takes the table, the text column and the words to match, then the embedding column and the query vector. What comes back is an ordinary table: your columns plus a score, so WHERE, ORDER BY, and LIMIT work on it like any other query.

tickets.sql
-- BM25 + vector, fused inside the engine
SELECT   _id, subject, score
FROM     hybrid_search(
           'tickets',                               -- one table, one snapshot
           'body', 'disk full on ingest',           -- keyword side (OR)
           'embedding', :q, 200                     -- vector side, 200 deep each
         )
WHERE    priority IN ('p0', 'p1')
ORDER BY score DESC
LIMIT    20;
  • The keyword index and the embeddings sit inside the same Parquet files.
  • Both searches read one version of the table, so the two sides can never be out of step with each other.
  • The last argument, 200, is how many rows each side returns before they are merged.

Because the result is a table, the rest of the question stays in the same statement. Here the same call feeds a GROUP BY, asking for 5,000 rows a side so the counts cover the whole match set instead of the first page.

hybrid.sql
-- Which services retried a failed request, by team?
SELECT   team,
         count(DISTINCT service) AS services     -- which services
FROM     hybrid_search('events', 'message',
                       'retry failed request',
                       'embedding', :q, 5000)     -- BM25 + vector, fused
GROUP BY team                                    -- by team
ORDER BY services DESC;

The full query surface →

infino explain rrf # rank-based fusion

How two lists become one ranking

The two searches report different things. BM25 gives a relevance score, where a bigger number is a better match. Vector search gives a distance, where a smaller number is a better match. Adding them would be meaningless, so the engine throws both numbers away and uses each row’s position in its list.

  • A row scores 1 / (60 + position) for each list it appears in. First place is worth 1/61, tenth place 1/70. Near the top counts for more, and the gap between positions shrinks as you go down.
  • A row found by both searches collects both scores, so the rows they agree on rise to the top. A row only one search found still keeps its place in the ranking.
  • There is nothing to tune. You do not set a weight between keyword and vector, and the same query returns the same order every time.

The arithmetic, and what sits around retrieval →

cat FAQ.md

Hybrid search questions

Why run both BM25 and vector?

They fail in opposite directions. BM25 finds the token “retry” and misses “backoff.” Vector finds the same idea said differently and misses an exact id. hybrid_search runs both over one snapshot and fuses the rankings.

What is the score column on hybrid_search?

The column contains the reciprocal-rank-fusion score, where higher is better. It is a separate ranking signal from the BM25 score and vector distance. ORDER BY score DESC lists the best blended matches first.

Do I tune a weight between BM25 and vector?

Fusion uses rank positions with a fixed constant of 60. The function exposes a single deterministic blend instead of an alpha or score-normalization step. A quality reranker can provide a second ranking over the returned rows.

Can I see the BM25 score and the vector distance too?

hybrid_search emits the fused score. Run bm25_search and vector_search as separate relations and join them on _id to inspect each raw signal and the disagreement set.

What is a table-valued search function?

hybrid_search (and bm25_search, vector_search) are relations. They take a table name, return that table’s columns plus a score, and belong in FROM. COUNT, WHERE, JOIN, and GROUP BY are ordinary SQL on top. k controls retrieval depth; LIMIT controls the rows displayed.

Where do the embeddings come from?

Infino can embed text on ingest using the model selected for the table. You can also send vectors from your own model. Embed the query with the same model you indexed. Keyword search and SQL run directly on text and columns.

Does hybrid search replace a reranker?

Infino retrieves and fuses candidates. A cross-encoder or other quality model can rerank the returned rows, while query rewrite, chunking, and labeled evaluation remain application concerns.