# Reranking

URL: https://cogitator.app/docs/rag/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:

```typescript
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.

```typescript
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:

```typescript
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](https://docs.cohere.com/reference/rerank) for high-quality cross-encoder reranking. No LLM prompt engineering needed — Cohere's model is trained specifically for relevance scoring.

```typescript
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:

```typescript
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:

```typescript
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;
  }
}
```
