Naar de inhoud
NLEN
Illustratie: Reranking-APIs in een Multi-Stage RAG-Pipeline

Reranking-APIs integreren in een multi-stage RAG-pipeline

Door Ivo Donker — samengesteld met AI-ondersteuning (Claude & Gemini)

In een standaard Retrieval-Augmented Generation (RAG) architectuur vormt vector-zoekfunctionaliteit vaak de eerste verdedigingslinie. Door een zoekvraag om te zetten in een embeddingvector en de dichtstbijzijnde documentfragmenten op te halen via cosinus-overeenkomst, probeert het systeem relevante context aan een taalmodel te leveren. Wie dieper wil duiken in de fundamenten van vectoren en semantische afstandsmaten, kan het basisconcept van semantisch zoeken bouwen met een embeddings-API raadplegen om te begrijpen hoe bi-encoders vectoren genereren. In productieomgevingen blijkt deze eentrapsbenadering echter structureel ontoereikend voor complexe zoekvragen. Bi-encoders comprimeren een compleet tekstfragment tot een enkele statische vector, waardoor subtiele relaties, ontkenningen en exacte woordcombinaties verloren gaan.

Een multi-stage retrieval-architectuur lost dit op door het zoekproces op te splitsen in twee fasen: een snelle, brede eerste selectie (hoge recall) gevolgd door een nauwkeurige herordening met een cross-encoder (hoge precisie). Dit artikel behandelt hoe gespecialiseerde reranking-APIs in zo'n keten worden geïntegreerd, hoe je omgaat met payloadgroottes en latency-budgetten, hoe score-drempels worden gekalibreerd en welke foutpatronen optreden wanneer een externe reranker traag wordt of faalt.

Het fundamentele verschil: bi-encoders versus cross-encoders

Om te begrijpen waarom een reranking-stap noodzakelijk is, moeten we kijken naar de wiskundige interactie tussen query en document. Bij een bi-encoder worden de zoekvraag $q$ en elk kandidaatdocument $d_i$ onafhankelijk van elkaar door een neuraal netwerk gestuurd. Dit levert twee vectoren op: $\vec{u} = f(q)$ en $\vec{v}_i = g(d_i)$. De relevantiescore is simpelweg het inwendig product of de cosinusovereenkomst $\cos(\vec{u}, \vec{v}_i)$. Omdat de documentvectoren vooraf berekend en geïndexeerd zijn in een vectordatabase, kost het vergelijken van duizenden vectoren slechts enkele milliseconden. Het nadeel is dat de aandachtsmechanismen (self-attention) in het taalmodel nooit gelijktijdig naar de query én het specifieke document kijken.

Een cross-encoder werkt fundamenteel anders. Hier worden de zoekvraag en het document samengevoegd tot één gezamenlijke invoersequentie, gescheiden door een scheidingsteken: [CLS] query [SEP] document [EOS]. Het model berekent volledige cross-attention over alle tokens tegelijkertijd. Elk woord in de zoekvraag kan direct interacteren met elk woord in het document. Dit model produceert direct een continue relevantiescore tussen 0 en 1, zonder tussenkomst van vectoren.

De berekening van cross-attention is rekenkundig gezien ordes van grootte zwaarder ($O(N^2)$ complexiteit op token-lengte). Het is onmogelijk om een miljoen documenten realtime door een cross-encoder te halen. Door de bi-encoder in te zetten voor het filteren van een miljoen naar bijvoorbeeld 50 kandidaten, en de cross-encoder alleen los te laten op die 50 fragmenten, combineer je de schaalbaarheid van vector-indexen met de precisie van volledige aandachtsmodellen.

De architectuur van een multi-stage retrieval-keten

Een robuuste productiepijplijn bestaat typisch uit drie achtereenvolgende stappen vóórdat de uiteindelijke prompt naar het generatieve taalmodel wordt gestuurd: candidate retrieval, document preparation en reranking scoring.

Fase Methode Invoer Uitvoer Typische latency
Stage 1: Ophalen Hybride zoekactie (BM25 + Vector) Ruwe zoekvraag Top 50–100 chunks 10 – 35 ms
Stage 2: Reranking Cross-Encoder API (b.v. Cohere, Jina, BGE) Zoekvraag + Stage 1 chunks Top 3–5 geordende chunks 60 – 250 ms
Stage 3: Generatie Generatief LLM Prompt met Top chunks Gegenereerd antwoord 500 – 3000 ms

Bij het dimensioneren van Stage 1 is het belangrijk om voldoende marge te nemen. Als het juiste documentfragment niet in de initiële selectie van 50 documenten zit, kan de reranker het immers nooit naar boven halen (recall-plafond). Het sturen van te veel fragmenten naar de reranker verhoogt daarentegen de netwerklatency en de API-kosten evenredig. Om de operationele doorvoer en volumekosten van de eerste fase onder controle te houden, biedt het overzicht over embeddings-API's in productie: limieten, kosten en doorvoer praktische handvatten voor het dimensioneren van batchgroottes en rate limits.

Payloads structureren en de API-aanroep uitvoeren

Reranking-APIs verwachten doorgaans een JSON-payload met de zoekvraag en een array van documenten of tekststrings. Een veelvoorkomende fout in integraties is het meesturen van overbodige metadata naar de reranker. De reranker beoordeelt tekstuele relevantie; velden zoals interne database-IDs, timestamps, tenant-identificatoren en JSON-attributen vervuilen het contextvenster van het cross-encoder-model en drijven het tokenverbruik op.

Hieronder staat een implementatievoorbeeld in TypeScript/Node.js dat laat zien hoe je kandidaatdocumenten stript, valideert en met een expliciete timeout en foutafhandeling naar een reranking-endpoint stuurt:

interface CandidateDocument {
  id: string;
  text: string;
  metadata: Record<string, unknown>;
}

interface RankedResult {
  id: string;
  text: string;
  score: number;
  originalIndex: number;
}

async function rerankCandidates(
  query: string,
  candidates: CandidateDocument[],
  apiKey: string,
  topN: number = 5,
  timeoutMs: number = 400
): Promise<RankedResult[]> {
  if (!candidates.length) return [];

  // Strip metadata en stuur alleen noodzakelijke tekst door
  const documents = candidates.map(c => c.text);
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const response = await fetch("https://api.rerank-provider.example/v1/rerank", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${apiKey}`,
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        model: "rerank-v3.5-general",
        query: query,
        documents: documents,
        top_n: topN,
        return_documents: false
      }),
      signal: controller.signal
    });

    if (!response.ok) {
      throw new Error(`Reranker HTTP ${response.status}: ${response.statusText}`);
    }

    const data = await response.json();
    
    // Koppel de gerangschikte indices terug aan de originele objecten
    return data.results.map((r: { index: number; relevance_score: number }) => ({
      id: candidates[r.index].id,
      text: candidates[r.index].text,
      score: r.relevance_score,
      originalIndex: r.index
    }));
  } catch (err: unknown) {
    if (err instanceof Error && err.name === "AbortError") {
      // Faalmodus: Latency-overschrijding
      console.warn(`Reranker timeout na ${timeoutMs}ms. Val terug op Stage 1 ranking.`);
    } else {
      console.error("Reranking API fout:", err);
    }
    // Fallback: retourneer de top N uit Stage 1 met neutrale score
    return candidates.slice(0, topN).map((c, i) => ({
      id: c.id,
      text: c.text,
      score: 0.0,
      originalIndex: i
    }));
  } finally {
    clearTimeout(timer);
  }
}

Let op de vlag return_documents: false in de payload. Door de provider te instrueren alleen de index en de relevantiescore terug te sturen, bespaar je substantiële netwerkbandbreedte op het responspad. De oorspronkelijke tekst staat immers al in het geheugen van de aanroepende applicatie.

Score-drempels en dynamische context-filtering

Een belangrijk voordeel van rerankers boven ruwe cosinus-scores is de schaalbaarheid van de relevantiescore. Cosinus-afstanden van bi-encoders zijn berucht om hun smalle dynamische bereik: een score van 0.78 kan bij het ene model uitstekend zijn en bij het andere model pure ruis betekenen. Cross-encoders produceren daarentegen een betrouwbaardere sigmoid-kansverdeling.

Dit maakt het mogelijk om met dynamische score-drempels (cutoffs) te werken in plaats van een vast aantal fragmenten ($k$) mee te sturen naar het taalmodel. Als een zoekopdracht leidt tot 50 kandidaten waarvan er slechts één echt antwoord geeft op de vraag, zal de reranker voor document 1 een score van bijvoorbeeld 0.94 teruggeven en voor document 2 tot en met 50 scores onder de 0.20.

Wanneer je blindelings een vaste $top\_k = 5$ in je prompt injecteert, vervuilt de context met vier irrelevante brokstukken tekst. Dit verhoogt het risico op hallucinaties en drijft de tokenkosten van het generatieve model onnodig op. Een dynamisch filteralgoritme hanteert twee regels:

Hierdoor ontvangt het generatiemodel bij specifieke vragen slechts één haarscherp tekstfragment, terwijl brede overzichtsvragen automatisch vier of vijf relevante contextstukken meekrijgen.

Faalgedrag en circuit breakers bij netwerkuitval

Elke externe API-aanroep introduceert een nieuw faalpunt in de architectuur. Reranking bevindt zich direct op het kritieke pad van de gebruikerservaring: zolang de reranker niet antwoordt, kan de prompt niet worden samengesteld en kan het generatieve model niet beginnen met streamen.

De belangrijkste faalmodus van reranking-APIs is niet een harde 500-fout, maar tail-latency (hangende verbindingen door overbelasting bij de provider). Als een applicatie een strikte SLA van maximaal 1000 ms totale responstijd heeft, mag de reranker daar maximaal 250 tot 300 ms van consumeren. Om te voorkomen dat trage API-calls de hele keten blokkeren, is een deadline-budget en een circuit breaker vereist. Hoe je strikte tijdslimieten en deadline-overdracht configureert over opeenvolgende microservices, wordt behandeld in het artikel over timeouts, annulering en deadline-budgetten bij LLM-calls.

Wanneer de circuit breaker opengaat door herhaalde timeouts, moet de pipeline direct terugvallen op graceful degradation. In het geval van RAG betekent dit dat de applicatie de top 5 fragmenten rechtstreeks uit de vector-zoekactie (Stage 1) gebruikt. De kwaliteit van het gegenereerde antwoord kan hierdoor tijdelijk fractioneel dalen, maar de gebruiker behoudt een werkende interface met acceptabele responstijden.

Doorvoer, batching en routering via een gateway

In applicaties met hoge gelijktijdigheid ontstaat al snel een knelpunt rond de rate limits van de reranking-provider. Waar embeddings relatief goedkoop en massaal parallel te berekenen zijn, hanteren de meeste reranking-diensten strengere limieten op het aantal verwerkte documenten per minuut (DPM) en queries per seconde (QPS).

Om te voorkomen dat afzonderlijke microservices direct tegen provider-limieten aanlopen, kan al het retrieval- en reranking-verkeer worden gecentraliseerd achter een gateway. Een overzicht van architecturen voor het beheren van doorvoer, budgettering en throttling bij retrieval-processen is te vinden in de gids over retrieval-verkeer door de gateway: embeddings-calls routeren, limiteren en budgetteren.

Door reranking-aanroepen via een gateway te sturen, profiteer je van drie operationele voordelen:

Privacy, dataminimalisatie en compliance

Bij het doorsturen van documentfragmenten naar een externe reranker ontstaat een compliance-aspect dat vaak over het hoofd wordt gezien. In een standaard RAG-opstelling worden chunks vaak lokaal opgeslagen in een eigen vectordatabase. Zodra je Stage 2 activeert, verzend je potentieel honderden vertrouwelijke tekstfragmenten per zoekactie naar de API van een externe partij.

Omdat rerankers de volledige platte tekst nodig hebben om hun cross-attention te berekenen, kunnen gevoelige persoonsgegevens (PII), medische dossiers of bedrijfsgeheimen in de logs van de reranking-leverancier belanden. Bij het selecteren van een SaaS-reranker moet daarom expliciet worden gecontroleerd of de verwerker een Zero Data Retention (ZDR) overeenkomst biedt en waar de inferentieservers geografisch gestationeerd zijn. Meer informatie over het selecteren van verwerkers en het waarborgen van data-integriteit is te vinden in het overzicht over AI-modellen en privacy: keuzes voor AVG-compliance.

Als datasoevereiniteit een harde eis is, kan worden gekozen voor een zelfgehoste cross-encoder met behulp van frameworks zoals Text Embeddings Inference (TEI) of vLLM op eigen infrastructuur. Dit elimineert externe dataoverdracht, maar verschuift de operationele verantwoordelijkheid voor GPU-capaciteit en latency-management naar het eigen team.

Wat kost de mitigatie: afwegingen in productie

Het toevoegen van een reranking-laag is geen kosteloze optimalisatie. Wie een multi-stage pipeline ontwerpt, moet de trade-offs tussen accuraatheid, vertraging en infrastructuurkosten nauwkeurig tegen elkaar afwegen.

Dimensie Eentraps RAG (Alleen Vector) Tweetraps RAG (Vector + Reranker API)
Precisie (NDCG@10) Gemiddeld; gevoelig voor irrelevante zoekresultaten Hoog; cross-encoder vangt nuances en contextuele match
End-to-end Latency Laag (ongeveer 15–40 ms retrieval overhead) Middel tot hoog (extra 80–250 ms netwerk- en inferentietijd)
API-kosten Alleen embedding-generatie en vectordatabase Extra kosten per gerangschikt document (bijv. per 1.000 chunks)
Foutgevoeligheid Eén afhankelijkheid (vectordatabase) Twee afhankelijkheden (vectordb + externe rerank-service)

In de praktijk blijkt de investering in een reranker zich dubbel en dwars terug te betalen voor systemen waar foutieve antwoorden grote zakelijke risico's dragen, zoals bij juridische zoeksystemen, technische handleidingen en interne kennisbanken. Door het aantal context-tokens dat uiteindelijk naar het generatieve model wordt gestuurd drastisch te verlagen van bijvoorbeeld 15 naar 3 hoogwaardige fragmenten, bespaar je bovendien op de invoerkosten van het uiteindelijke taalmodel. Dit compenseert in veel architecturen een aanzienlijk deel van de reranker-kosten.

Conclusie en implementatie-stappenplan

Het integreren van een reranking-API transformeert een kwetsbare vector-zoekopstelling in een betrouwbare, multi-stage informatiestroom. De sleutel tot een succesvolle implementatie ligt niet alleen in het kiezen van het krachtigste cross-encoder model, maar vooral in de operationele randvoorwaarden: strakke timeouts, robuuste circuit breakers, dynamische score-filtering en gecentraliseerd beheer van de doorvoer.

Begin bij het bouwen altijd met een baseline-evaluatie van de eerste fase. Zorg dat de recall van de bi-encoder hoog genoeg is om relevante documenten binnen de top 50 te vangen, voeg vervolgens de reranker toe met een strikte timeout van maximaal 350 ms, en implementeer dynamische score-drempels om te voorkomen dat ruis de generatieve context binnendringt.