Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

Reranking in een RAG-systeem

Hoe je met een cross-encoder reranker de kwaliteit van retrieved chunks verbetert door de initiele vector-zoekresultaten te herordenen.

Het probleem met vectorzoekopdrachten alleen

Vectorzoekopdrachten gebruiken bi-encoders: query en document worden onafhankelijk geembedded en daarna vergeleken. Dat is snel, maar beperkt, want het model ziet de query en het document nooit samen.

Een cross-encoder bekijkt query en document wel samen en kan zo diepere semantische relaties begrijpen. Dat is langzamer, maar nauwkeuriger. De praktische oplossing combineert beide: vectorzoekopdrachten voor snelle filtering (top-20) en een cross-encoder voor precieze herordening (top-5).

info

Twee-fase retrieval

Fase 1 (bi-encoder): haal 20 tot 50 kandidaten op via vectorzoekopdracht.

Fase 2 (cross-encoder): rerank die kandidaten en behoud de top-5 voor de context.

Cohere Rerank API

Het actuele Cohere-rerankmodel is rerank-v3.5 (per juni 2026). Dit is een enkel meertalig model dat de oudere gesplitste v3.0-modellen vervangt en meer dan 100 talen ondersteunt, waaronder Nederlands.

import { CohereClient } from "cohere-ai";

const cohere = new CohereClient({ token: process.env.COHERE_API_KEY });

async function rerankWithCohere(
  query: string,
  documents: { id: string; content: string }[],
  topN = 5
): Promise<{ id: string; content: string; score: number }[]> {
  const response = await cohere.rerank({
    model: "rerank-v3.5",
    query,
    documents: documents.map(d => d.content),
    topN,
    returnDocuments: false,
  });

  return response.results.map(result => ({
    id: documents[result.index].id,
    content: documents[result.index].content,
    score: result.relevanceScore,
  }));
}
lightbulb

Controleer modelnamen voor je deployt

Modelnamen veranderen sneller dan API-vormen. Verifieer de actuele rerank-modelnaam in de Cohere-documentatie voordat je naar productie gaat, en pin de versie expliciet in je configuratie.

Open-source rerankers

Wil je geen externe API, dan draai je een reranker zelf. Onderstaand voorbeeld praat met een lokale reranker-service.

async function rerankWithBGE(
  query: string,
  documents: string[],
  topN = 5
): Promise<{ index: number; score: number }[]> {
  const response = await fetch("http://localhost:8080/rerank", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      query,
      passages: documents,
      top_n: topN,
    }),
  });
  return response.json();
}

Veelgebruikte open-source rerankers:

  • BAAI/bge-reranker-v2-m3: meertalig, sterk op Europese talen.
  • cross-encoder/ms-marco-MiniLM-L-6-v2: Engels, snel en licht.
  • mixedbread-ai/mxbai-rerank-large-v1: hoge kwaliteit, maar een groter en zwaarder model.

LLM-gebaseerde reranking

Voor maximale kwaliteit tegen hogere kosten laat je een LLM elke kandidaat scoren. Dit is flexibel, maar duurder en trager dan een toegewijde reranker.

async function llmRerank(
  query: string,
  documents: { id: string; content: string }[],
  topN = 5
): Promise<{ id: string; content: string; score: number }[]> {
  const scores = await Promise.all(
    documents.map(async (doc) => {
      const response = await anthropic.messages.create({
        model: "claude-haiku-4-5",
        max_tokens: 10,
        messages: [{
          role: "user",
          content: `Geef een relevantiescore van 1-10 voor dit document voor de gegeven vraag.
Geef alleen het getal terug.

Vraag: ${query}
Document: ${doc.content.slice(0, 500)}

Score:`,
        }],
      });
      const scoreText = response.content[0].type === "text" ? response.content[0].text.trim() : "0";
      return { ...doc, score: parseFloat(scoreText) || 0 };
    })
  );

  return scores.sort((a, b) => b.score - a.score).slice(0, topN);
}

Complete reranking pipeline

Hieronder de twee fasen aan elkaar geknoopt: ophalen, rerankenen en het antwoord laten genereren op basis van de top-5.

async function ragWithReranking(question: string): Promise<string> {
  const queryEmbedding = await embedText(question);
  const candidates = await vectorDB.search(queryEmbedding, 20);

  const reranked = await rerankWithCohere(
    question,
    candidates.map(c => ({ id: c.id, content: c.content })),
    5
  );

  const context = reranked
    .map((doc, i) => `[${i + 1}] ${doc.content}`)
    .join("

");

  const response = await anthropic.messages.create({
    model: "claude-opus-4-8",
    max_tokens: 1024,
    system: "Beantwoord de vraag uitsluitend op basis van de verstrekte context. Verwijs naar bronnummers [1], [2] etc.",
    messages: [{ role: "user", content: `Context:
${context}

Vraag: ${question}` }],
  });

  return response.content[0].type === "text" ? response.content[0].text : "";
}

Impact op kwaliteit

Reranking verhoogt vooral de precision aan de top van je resultaten. Onderstaande cijfers zijn indicatief (gemeten op NDCG@5) en variëren sterk per dataset en domein. Meet daarom altijd op je eigen evaluatieset.

Aanpak NDCG@5
Vectorzoekopdracht alleen 0.58
Met Cohere Rerank 0.71
Met BGE Reranker 0.69
Met LLM Rerank 0.74
warning

Reranking is geen vervanging voor goede retrieval

Een reranker kan alleen herordenen wat fase 1 ophaalt. Staat het juiste document niet bij de top-20 kandidaten, dan haalt reranking het ook niet boven. Investeer eerst in goede chunking en embeddings.

Is reranking de moeite waard voor kleine kennisbanken?

Bij minder dan 1000 documenten is de meerwaarde vaak beperkt, omdat de vectorzoekopdracht meestal al nauwkeurig genoeg is. Voeg reranking toe zodra je concrete kwaliteitsproblemen meet op je evaluatieset.

Hoeveel kandidaten geef ik mee aan de reranker?

Doorgaans 10 tot 50. Te weinig kandidaten geeft de reranker weinig om uit te kiezen, terwijl te veel kandidaten de latentie verhogen zonder noemenswaardige kwaliteitswinst.

Kan ik de reranker fine-tunen op mijn domein?

Ja, maar dat vereist geannoteerde trainingsdata met query-document-relevantieparen. Voor domeinspecifieke toepassingen zoals medisch of juridisch is die investering vaak de moeite waard.

Wat is de typische latentie van een cross-encoder?

Indicatief: Cohere Rerank rond de 200 tot 500 ms voor 20 documenten, een lokale BGE-reranker rond de 50 tot 200 ms, en LLM-reranking 2 tot 5 seconden. Kies op basis van je latentievereisten en meet in je eigen omgeving.

Welk rerankmodel kies ik als ik Nederlands ondersteun?

Kies een expliciet meertalig model. Cohere rerank-v3.5 en BAAI/bge-reranker-v2-m3 presteren goed op Nederlands; Engels-only modellen zoals ms-marco-MiniLM zijn daarvoor minder geschikt.