Reranking
Improve retrieval precision by re-scoring results with an LLM or Cohere's reranking API.
Why Rerank?
Vector similarity is a good first pass, but it can miss nuance. A document might be semantically close to the query but not actually answer the question. Reranking takes the top retrieval results and re-scores them with a more powerful model that reads both the query and each document, producing a more accurate ranking.
Rerankers implement the Reranker interface:
interface Reranker {
rerank(query: string, results: RetrievalResult[], topN?: number): Promise<RetrievalResult[]>;
}LLM Reranker
Uses any LLM to score each document's relevance to the query on a 0–10 scale. You provide a generateFn that takes a prompt string and returns the LLM's text response.
import { Agent, Cogitator } from '@cogitator-ai/core';
import { LLMReranker } from '@cogitator-ai/rag';
const cog = new Cogitator();
const judge = new Agent({
name: 'reranker',
model: 'openai/gpt-5.5',
instructions: 'You score how relevant documents are to a query.',
});
const reranker = new LLMReranker({
generateFn: async (prompt) => {
const result = await cog.run(judge, { input: prompt, useMemory: false });
return result.output;
},
});
const reranked = await reranker.rerank('How do agents handle tool errors?', retrievalResults, 5);The reranker builds a prompt asking the LLM to score each document and return a JSON array of { index, score } objects. Scores are normalized to 0–1 in the output. Only the first 50 results are put into the prompt; results the LLM does not score (including any beyond 50) get a score of 0. If generateFn throws or the response holds no ranking (for example a reasoning model spent its token budget on reasoning and answered with empty content), the reranker returns the results in retrieval order. By default it only logs a warning. To notice the fallback, pass onError, or set strict to make rerank() throw an LLMRerankError like CohereReranker does:
import { LLMReranker, LLMRerankError } from '@cogitator-ai/rag';
const reranker = new LLMReranker({
generateFn,
onError: (error, { query, response }) => {
metrics.increment('rerank.fallback');
logger.warn({ query, response, cause: error.cause }, error.message);
},
});
const strictReranker = new LLMReranker({ generateFn, strict: true });
try {
await strictReranker.rerank(query, retrievalResults, 5);
} catch (error) {
if (error instanceof LLMRerankError) console.error(error.message, error.response);
}| Option | Type | Default | Description |
|---|---|---|---|
generateFn | (prompt: string) => Promise<string> | - | LLM generation function |
onError | (error: LLMRerankError, context: { query, response? }) => void | - | Called instead of the console warning when reranking falls back. response is the raw answer, if there was one |
strict | boolean | false | Throw an LLMRerankError instead of falling back to the retrieval order |
An LLMRerankError carries the raw model answer in response and, when generateFn threw, the original error in cause.
When to use: When you already have an LLM backend in your stack and want better precision without an external reranking service. Works with any model — local Ollama, OpenAI, Anthropic.
Cohere Reranker
Uses the Cohere Rerank API for high-quality cross-encoder reranking. No LLM prompt engineering needed — Cohere's model is trained specifically for relevance scoring.
import { CohereReranker } from '@cogitator-ai/rag';
const reranker = new CohereReranker({
apiKey: process.env.COHERE_API_KEY!,
model: 'rerank-v3.5',
});
const reranked = await reranker.rerank('How do agents handle tool errors?', retrievalResults, 5);| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Cohere API key |
model | string | rerank-v3.5 | Cohere rerank model name |
Results get Cohere's relevance_score as their score; with topN only the top N are returned. Requests time out after 30 seconds and are not retried — a non-2xx response throws.
When to use: Production deployments where retrieval quality is critical. Cohere's reranking models are fast, accurate, and purpose-built for this task.
Using with RAGPipeline
Pass a reranker to the builder and enable it in the config:
import { RAGPipelineBuilder, TextLoader, CohereReranker } from '@cogitator-ai/rag';
const pipeline = new RAGPipelineBuilder()
.withLoader(new TextLoader())
.withEmbeddingService(embeddings)
.withEmbeddingAdapter(store)
.withReranker(
new CohereReranker({
apiKey: process.env.COHERE_API_KEY!,
})
)
.withConfig({
chunking: { strategy: 'recursive', chunkSize: 512, chunkOverlap: 50 },
reranking: { enabled: true, topN: 5 },
})
.build();
const results = await pipeline.query('What is the agent lifecycle?');The pipeline first retrieves candidates using the configured retriever (retrieval.topK, 10 by default), then reranks them if reranking.enabled is true. The topN parameter controls how many results survive reranking, so set retrieval.topK higher than topN to give the reranker room to reorder.
Comparison
| Reranker | Latency | Quality | Cost | Dependencies |
|---|---|---|---|---|
| LLM | Variable | Good | Per-token LLM cost | Any LLM backend |
| Cohere | Low | Excellent | Per-request | Cohere API key |
Custom Rerankers
Implement the Reranker interface:
import type { Reranker, RetrievalResult } from '@cogitator-ai/rag';
class CrossEncoderReranker implements Reranker {
constructor(private readonly crossEncode: (query: string, document: string) => Promise<number>) {}
async rerank(
query: string,
results: RetrievalResult[],
topN?: number
): Promise<RetrievalResult[]> {
const scored = await Promise.all(
results.map(async (result) => {
const score = await this.crossEncode(query, result.content);
return { ...result, score };
})
);
scored.sort((a, b) => b.score - a.score);
return topN ? scored.slice(0, topN) : scored;
}
}Retrieval Strategies
Retrieve relevant document chunks using similarity search, MMR diversity, hybrid vector+keyword search, or multi-query expansion.
Voice
Add voice and realtime audio capabilities to Cogitator agents with @cogitator-ai/voice — speech-to-text, text-to-speech, voice activity detection, and native speech-to-speech via OpenAI Realtime and Gemini Live APIs.