Naar de inhoud
NLEN
Illustratie: Reflexion-patronen: agents zichzelf laten corrigeren

Reflexion-patronen: agents zichzelf laten corrigeren

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

Wanneer een taalmodel een complexe taak uitvoert, treden er onvermijdelijk redeneerfouten, foute gereedschapsaanroepen of ongeldige gegevensformaten op. In traditionele softwareomgevingen leidt dit tot harde exceptions of afbrekende scripts. In autonome AI-systemen levert een simpele herhaalpoging (retry) vaak weinig op: het model maakt met dezelfde invoerprompt simpelweg dezelfde fout opnieuw. Het Reflexion-patroon lost dit fundamentele probleem op door dynamische zelfreflectie te introduceren. In plaats van gewichten bij te werken via dure fine-tuning, leert het systeem tijdens runtime door fouten verbaal te analyseren en deze inzichten op te slaan in een tijdelijk episodisch geheugen.

In dit artikel wordt stap voor stap uitgelegd hoe Reflexion-architecturen worden opgebouwd, welke componenten strikt noodzakelijk zijn, hoe het geheugen wordt beheerd en hoe valkuilen zoals oneindige reflectielussen worden vermeden. Wie de overstap maakt van losse prompts naar complexe regelsystemen, ontdekt hier hoe zelfcorrectie een agent robuust maakt tegen onvoorspelbare invoer.

De anatomie van het Reflexion-raamwerk

Het Reflexion-patroon splitst de uitvoering van een taak op in drie afzonderlijke rollen: de Actor, de Evaluator en het Self-Reflection model. Deze scheiding voorkomt dat een model zijn eigen werk kritiekloos goedkeurt en zorgt voor een reproduceerbare feedbackcyclus.

De interactie verloopt volgens een vast ritme:

Voor een breder overzicht van hoe loops en toestandsmachines zich verhouden tot statische prompts, lees je het fundament in van prompt- naar graph-engineering.

Waarom gewone prompt-loops falen zonder verbale reflectie

Veel ontwikkelaars bouwen een eenvoudige feedbackloop waarin bij een foutmelding simpelweg de ruwe traceback terug naar het model wordt gestuurd met de mededeling: "Dit faalde, probeer het opnieuw". In de praktijk blijkt dit opmerkelijk ineffectief bij complexe redeneertaken. Zonder expliciete reflectiestap blijft de aandachtsverdeling (attention mechanism) van het model gefixeerd op zijn eerdere redeneerpad.

Het cruciale verschil zit in de verbale representatie van de fout. Door het model te dwingen een tussentijdse analyse te formuleren ("Ik koos API-methode X omdat ik aannam dat parameter Y optioneel was, maar de validatie vereist parameter Y. Bij de volgende poging moet ik eerst parameter Y opvragen"), wordt de context getransformeerd. De gegenereerde reflectie fungeert als een dynamische systeeminformatie die het model helpt om niet opnieuw in dezelfde logische valkuil te stappen.

Een gedetailleerde uitwerking van basale tool-aanroepen en planningsstructuren staat beschreven in promptpatronen voor AI-agents.

Het episodisch reflectiegeheugen beheren

Het episodisch geheugen vormt het werkgeheugen van de Reflexion-agent gedurende één specifieke taak. Omdat elk contextvenster eindig is en onnodige tokens de kosten opdrijven, kan niet elk mislukt tussenresultaat integraal in de prompt blijven staan. Het geheugen moet compact en gefocust blijven.

Er zijn twee gangbare strategieën voor geheugenbeheer in productiesystemen:

Geheugenstrategie Token-impact Complexiteit Geschikt voor
Volledige geschiedenis Zeer hoog (exponentieel) Laag Korte taken (maximaal 2 iteraties)
Schuivend venster (k=2) Laag (lineair begrensd) Laag Standaard codegeneratie en parsing
Geaggregeerde lessen Constant Gemiddeld Complexe multi-step onderzoekstaken

Evaluatormechanismen: Deterministisch versus LLM-as-a-Judge

De betrouwbaarheid van een Reflexion-loop staat of valt met de kwaliteit van de Evaluator. Wanneer de Evaluator onterecht een foutieve oplossing goedkeurt (false positive) of een correcte oplossing afkeurt (false negative), raakt de agent ontregeld.

In robuuste architecturen wordt altijd de voorkeur gegeven aan deterministische evaluatoren waar dat mogelijk is. Denk aan JSON Schema-validatie, TypeScript-compilatietests of sandbox-uitvoeringen met Pytest. Deterministische evaluatoren leveren ondubbelzinnige feedback op en verbruiken geen extra tokens voor de beoordelingsstap.

Wanneer de kwaliteit van vrije tekst, samenvattingen of creatieve argumentatie moet worden beoordeeld, is een deterministische check ontoereikend. In die gevallen wordt een secundair taalmodel ingezet als LLM-as-a-Judge. De prompt voor deze evaluator moet strikte rubrieken bevatten met binaire scoringscriteria. Vermijd vage instructies zoals "Beoordeel of dit antwoord goed is"; definieer in plaats daarvan expliciete controlepunten (bijvoorbeeld: "Bevat het antwoord alle drie de verplichte bronnen? Is het formaat strikt Markdown? Bevat het geen hallucinaties ten opzichte van de meegegeven context?").

Voor specifieke patronen rondom directe schema-reparaties zonder volledige geheugenbuffer raadpleeg je de gids over herstelprompts bij gefaalde validatie van LLM-output.

Implementatie: Een complete Reflexion-loop in Python

Hieronder staat een concreet, modulair Python-voorbeeld waarin een agent een datatransformatie-opdracht uitvoert. De loop combineert een Actor, een deterministische validator en een Reflector die bij fouten direct feedback genereert en opslaat.

import json
from typing import List, Dict, Any, Optional

class ReflexionEngine:
  def __init__(self, llm_client, max_trials: int = 3):
    self.llm = llm_client
    self.max_trials = max_trials
    self.reflections: List[str] = []

  def execute_task(self, instruction: str) -> Optional[Dict[str, Any]]:
    for trial in range(1, self.max_trials + 1):
      # 1. Actor genereert output met historische reflecties
      actor_prompt = self._build_actor_prompt(instruction)
      candidate_output = self.llm.generate(actor_prompt)

      # 2. Evaluator valideert het resultaat
      is_valid, error_log = self._evaluate(candidate_output)
      if is_valid:
        return json.loads(candidate_output)

      # 3. Reflector analyseert de fout bij mislukking
      if trial < self.max_trials:
        reflection = self._reflect(instruction, candidate_output, error_log)
        self.reflections.append(f"Poging {trial} fout: {reflection}")

    return None

  def _build_actor_prompt(self, instruction: str) -> str:
    memory_context = "\n".join(self.reflections)
    return f"""Taak: {instruction}

Lessen uit eerdere mislukte pogingen:
{memory_context if memory_context else "Geen eerdere pogingen."}

Genereer uitsluitend geldige JSON die voldoet aan de taakeisen."""

  def _evaluate(self, raw_output: str) -> (bool, str):
    try:
      data = json.loads(raw_output)
      if "id" not in data or "status" not in data:
        return False, "Sleutels 'id' en 'status' zijn verplicht."
      if not isinstance(data["id"], int):
        return False, "Veld 'id' moet een integer zijn."
      return True, ""
    except json.JSONDecodeError as err:
      return False, f"Ongeldige JSON syntaxis: {str(err)}"

  def _reflect(self, task: str, failed_output: str, error: str) -> str:
    reflect_prompt = f"""Je bent een reflectiemodel. Analyseer de fout.
Taak: {task}
Gefaalde output: {failed_output}
Foutmelding: {error}

Geef een bondige analyse van maximaal twee zinnen: wat ging er mis
en welke specifieke wijziging lost dit op in de volgende poging?"""
    return self.llm.generate(reflect_prompt)

Valkuilen: Reflectie-drift, vicieuze cirkels en hallucinatie

Hoewel Reflexion de nauwkeurigheid van agents aanzienlijk kan verhogen, brengt het ook specifieke faalmechanismen met zich mee die actief beheerd moeten worden:

Om deze problemen te beteugelen, worden drie mitigerende maatregelen toegepast:

  1. Temperatuurverlaging: Zet de temperatuur van de Reflector lager (bijvoorbeeld tussen 0.0 en 0.2) dan die van de Actor. Dit dwingt analytische, deterministische analyses af.
  2. Harde faallimiet: Stel het maximaal aantal pogingen strikt in op 3 tot 5. Meer iteraties leveren zelden alsnog een doorbraak op en leiden tot tokenverspilling.
  3. Backtracking en deterministische contextinjectie: Geef de Reflector altijd de exacte foutmeldingen van het onderliggende systeem mee, zodat hij niet hoeft te raden naar de oorzaak.

Wie dieper wil ingaan op vastlopende cycli en oneindige aanroepen, vindt praktische diagnostiek in het artikel over agentic loops debuggen.

Kosten, latency en prestatie-afwegingen in productie

Het introduceren van een Reflexion-loop brengt substantiële kosten en latency met zich mee. Elke extra poging vereist minimaal twee LLM-aanroepen (één voor de Reflector en één voor de nieuwe poging van de Actor). Bij een taak die pas na drie iteraties slaagt, kan het totale tokenverbruik verviervoudigen ten opzichte van een single-shot aanroep.

Architectuurpatroon Gemiddelde latency Relatieve kosten Succespercentage complexe taken
Single-shot prompting Laag (1x) 1x (basis) Matig tot laag
Chain-of-Thought (statisch) Laag tot gemiddeld (1.5x) 1.5x Gemiddeld
Naïeve retry-loop Hoog (3x - 5x) 3x - 5x Matig
Reflexion (Actor + Reflector) Hoog (3x - 6x) 3.5x - 7x Hoog

De afweging is daarom helder: Reflexion is zelden geschikt voor latency-gevoelige, interactieve toepassingen zoals realtime chatapplicaties voor consumenten. Het patroon blinkt echter uit in asynchrone achtergrondprocessen, zoals geautomatiseerde coderefactoring, complexe ETL-extractiepijplijnen en rapportgeneratie waarbij een foutieve uitkomst handmatige menselijke interventie zou vereisen.

Reflexion-agents testen en monitoren

Omdat een agent met een Reflexion-patroon over meerdere iteraties redeneert, is traditionele input/output-monitoring ontoereikend. Het volstaat niet om alleen te controleren of de uiteindelijke uitkomst klopt; het traject ernaartoe moet inzichtelijk zijn.

Belangrijke metrieken om bij te houden tijdens het testen van reflectielussen:

Om systematisch te meten of de introductie van reflectiestappen je slagingspercentage verhoogt zonder de kosten onevenredig op te drijven, bekijk je de meetmethoden in het overzicht van agent-evaluatie en trajectanalyse.

Praktische vuistregels voor implementatie

Bij het ontwerpen van zelfcorrigerende agent-architecturen bieden de volgende richtlijnen houvast:

  1. Houd reflecties beknopt: Instrueer het reflectiemodel om in maximaal twee zinnen de kernfout en de oplossing te formuleren. Lange essays vervuilen de context en leiden tot verlies van focus bij de Actor.
  2. Isoleer de evaluatie: Laat de Actor nooit zichzelf beoordelen. Gebruik deterministische code of een strikt geprompt evaluator-model.
  3. Verwijder ruwe foutlogs na reflectie: Zodra de Reflector een nuttige synthese heeft gemaakt van een traceback, hoeft de honderd regels tellende foutmelding niet langer in het contextvenster te blijven staan. De beknopte reflectie is voldoende.
  4. Stel harde grenzen aan iteraties: Beperk het aantal pogingen tot maximaal drie voor standaardtaken en maximaal vijf voor zware programmeertaken.

Door deze ontwerpprincipes consistent toe te passen, transformeert een kwetsbare reeks LLM-aanroepen in een veerkrachtig, zelflerend systeem dat zelfstandig herstelt van onvoorziene fouten.