Small contracts. Composable search systems.
The runner does not know how every model works. It knows the contracts. Concrete implementations register themselves, and the same pipeline can be measured across modes.
The data flow.
- Corpus: load validated documents, queries, and qrels from the cache.
- Embed: encode documents and queries through an
Embedder. - Retrieve: run one or more
Retrieverimplementations such as BM25 or FAISS. - Fuse: combine ranked lists through a
FusionStrategy, usually reciprocal rank fusion. - Rerank: optionally apply a
Rerankerto the candidate pool. - Evaluate: score relevance and latency, then persist a reproducibility-rich CSV row.
Five public contracts.
| Contract | Implementations | Responsibility |
|---|---|---|
Embedder | Sentence, E5 | Encode documents and queries with a declared dimension. |
Retriever | BM25, FAISS, DiskBBQ | Index documents, search, expose tuning parameters, report size. |
FusionStrategy | RRF, Weighted | Turn ranked result lists into one ranked result list. |
Reranker | Encoder, BGE, MonoT5 | Score candidate documents for one query. |
BenchmarkPipeline | BM25, vector, hybrid modes | Wire components together and emit measured result rows. |
Use the same contracts in your code.
The public API is intentionally direct. There are no compatibility aliases or forwarding layers hiding the component boundaries.
from trivium.retrieval.bm25 import Bm25
from trivium.retrieval.faiss import Faiss
from trivium.fusion.rrf import Rrf
bm25 = Bm25()
bm25.add_documents(documents)
vector = Faiss.Flat()
vector.add_documents(documents, vectors=embeddings)
lexical = bm25.search(queries, k=100)
dense = vector.search(query_vectors, k=100)
fused = Rrf(k=60).fuse([lexical, dense], top_k=50)