Onze insights
26/8/2025

Een intelligent agentic RAG-systeem bouwen met LangGraph

Hanne De Kesel
Door Hanne De Kesel
·
August 26, 2025

Traditionele RAG-systemen (Retrieval-Augmented Generation) volgen een eenvoudig patroon: ze halen documenten op en genereren daarna een antwoord. Maar wat als je systeem zelf kan nadenken over wanneer het moet zoeken, de kwaliteit van opgehaalde informatie kan beoordelen en queries zelfs kan herformuleren als de resultaten niet goed genoeg zijn?

Maak kennis met agentic RAG: een slimmere aanpak waarbij autonome agents doorheen het hele retrieval- en generatieproces beslissingen nemen. Zie het als de overstap van een eenvoudige tool naar een intelligente assistent die over zijn eigen prestaties kan redeneren en zich daaraan aanpast.

In deze tutorial bouwen we samen stap voor stap een geavanceerd agentic RAG-systeem met LangGraph. Dit systeem superviseert zichzelf, beoordeelt zijn eigen output en verbetert zijn antwoorden iteratief, in plaats van blindweg op te halen en te genereren.

Wat maakt dit RAG-systeem “agentic”?

Het grote verschil tussen traditionele RAG en agentic RAG zit in het vermogen om beslissingen te nemen. Ons systeem toont echte autonomie via een aantal kerncapaciteiten:

Intelligente query routing: een supervisor agent analyseert elke query en beslist of hij retrieval inzet of rechtstreeks antwoordt vanuit zijn bestaande kennis. Eenvoudige vragen zoals “Wat is Python?” vragen geen zoektocht in documenten, terwijl complexe technische queries baat hebben bij grondige retrieval.

Zelfcorrigerende queries: levert de eerste retrieval geen relevante documenten op, dan geeft het systeem niet op. Het herformuleert de query met andere terminologie en een andere invalshoek, en probeert tot drie keer toe betere resultaten te vinden.

Validatie in meerdere stappen: het systeem hanteert een kwaliteitscontrole in twee lagen. Eerst controleert het of de opgehaalde documenten echt relevant zijn voor de query. Daarna beoordeelt het of het gegenereerde antwoord zowel gebaseerd is op de opgehaalde informatie als nuttig is voor de gebruiker.

Graceful degradation: is de zekerheid laag of de informatie ontoereikend, dan communiceert het systeem expliciet zijn onzekerheid, in plaats van mogelijk misleidende antwoorden te geven.

Architectuuroverzicht: de intelligente beslissingsflow

In tegenstelling tot lineaire RAG-pipelines volgt onze agentic RAG een geavanceerd beslissingsproces dat zich in elke fase kan aanpassen op basis van kwaliteitsbeoordelingen:

SuperviserenDocumenten ophalenDocumenten beoordelenGenererenAntwoord beoordelenAfronden

Elke node in deze workflow heeft specifieke verantwoordelijkheden en kan de flow omleiden op basis van kwaliteitsbeoordelingen. Blijken de documenten bij de beoordeling bijvoorbeeld weinig relevant, dan herformuleert het systeem automatisch de query, in plaats van verder te gaan met ontoereikende informatie.

Het systeem stap voor stap bouwen

Tijd om dit intelligente systeem te bouwen. We beginnen bij de basis en voegen gaandeweg de geavanceerde redeneercapaciteiten toe.

Vereisten

Controleer voor je begint of je het volgende hebt:

  • Python 3.13+ geïnstalleerd
  • Een AWS-account met geconfigureerde toegang tot Bedrock
  • Basiskennis van de concepten van LangChain
  • Vertrouwdheid met graph-gebaseerde workflows (handig, maar niet vereist)

Stap 1: een moderne ontwikkelomgeving opzetten

We gebruiken uv, een snelle package manager voor Python die steeds populairder wordt dankzij zijn snelheid en betrouwbaarheid. Zie het als een efficiënter alternatief voor pip en virtuele omgevingen samen.

Installeer eerst uv op je systeem:

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

Maak nu je projectstructuur aan:

mkdir agentic-rag-tutorial
cd agentic-rag-tutorial
uv init

Het commando uv init maakt een basisstructuur voor een Python-project aan, met de juiste configuratiebestanden, vergelijkbaar met hoe moderne JavaScript-tools werken.

Stap 2: dependencies installeren

Een van de sterktes van uv is dat het dependencies efficiënt beheert. We installeren onze packages in logische groepen, zodat je begrijpt wat elk onderdeel doet:

# Core workflow and AI components
uv add langgraph langchain-core langchain-community langchain-text-splitters
# AWS integration for production-ready LLMs
uv add langchain-aws boto3
# Vector database and document processing
uv add langchain-chroma chromadb pypdf
# Development tools
uv add --dev langgraph-cli[inmem]

Wat elke dependency doet:

  • LangGraph: orkestreert onze multi-agent workflow
  • LangChain-componenten: leveren de infrastructuur voor AI en documentverwerking
  • AWS/Bedrock: geeft ons toegang tot taalmodellen van productiekwaliteit
  • ChromaDB: zorgt voor vectoropslag en similarity search
  • PyPDF: verwerkt PDF-documenten om kennis in te laden

Stap 3: de projectstructuur ontwerpen

Een goed georganiseerde projectstructuur is cruciaal voor de onderhoudbaarheid. Zo organiseren we onze code:

agentic-rag-tutorial/
├── models/ # Data schemas and state definitions
├── nodes/ # Individual agent nodes with specific responsibilities
├── utils/ # Shared utilities (AWS, vector store setup)
├── resources/ # Knowledge base documents
├── main.py # Workflow orchestration
└── configuration files

Deze structuur scheidt verantwoordelijkheden duidelijk: elke node voert één specifieke taak uit, utilities zijn herbruikbaar en het hoofdbestand focust puur op het verbinden van de componenten.

Stap 4: het schema voor de dataflow definiëren

In elk multi-agentsysteem is het cruciaal om vast te leggen hoe data tussen componenten stroomt. Ons state-schema fungeert als het “geheugen” dat agents doorheen het hele gesprek delen.

Maak models/state.py aan:

from pydantic import BaseModel
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing import List, Optional, Annotated, Sequence

class InputAgentState(BaseModel):
"""What the system receives from users"""
messages: Annotated[Sequence[BaseMessage], add_messages] = []

class AgentState(InputAgentState):
"""Complete state that flows between all agents"""
original_user_query: Optional[str] = None
documents: Optional[str] = None
generated_answer: Optional[str] = None
rephrased_queries: List[str] = [] # Tracks iteration attempts

Waarom deze structuur belangrijk is: De AgentState werkt als een gedeeld notitieboek waarin elke agent kan lezen en schrijven. De lijst rephrased_queries is bijzonder belangrijk: ze voorkomt oneindige lussen door bij te houden hoe vaak we al geprobeerd hebben de query te verbeteren.

Stap 5: productieklare AI-modellen configureren

AWS Bedrock biedt AI-modellen van enterpriseniveau met ingebouwde beveiliging en compliance. We zetten onze AI-infrastructuur op in utils/aws_bedrock.py:

from langchain_aws import ChatBedrockConverse, BedrockEmbeddings, BedrockRerank
from langchain_core.rate_limiters import InMemoryRateLimiter

# Configuration for EU region (adjust based on your needs)
aws_region = "eu-central-1"
model_id_chat = "eu.anthropic.claude-sonnet-4-20250514-v1:0"

# Claude 4 Sonnet with rate limiting for production use
chat_claude_4_sonnet = ChatBedrockConverse(
model=model_id_chat,
region_name=aws_region,
temperature=0, # Deterministic outputs
max_tokens=4096,
rate_limiter=InMemoryRateLimiter(
requests_per_second=5,
max_bucket_size=2 # Prevents burst requests
),
)

# Embedding model for semantic search
embeddings = BedrockEmbeddings(
model_id="cohere.embed-multilingual-v3",
region_name=aws_region,
)

# Reranker improves retrieval quality
compressor = BedrockRerank(
model_arn="arn:aws:bedrock:eu-central-1::foundation-model/cohere.rerank-v3-5:0",
region_name=aws_region,
top_n=10,
)

De belangrijkste ontwerpkeuzes toegelicht:

  • Temperature=0: zorgt voor consistente, deterministische antwoorden
  • Rate limiting: voorkomt problemen met API-quota in productie
  • Reranking: neemt de top 10 van opgehaalde documenten en rangschikt ze opnieuw op relevantie, wat de kwaliteit van de antwoorden aanzienlijk verbetert

Stap 6: een intelligente pipeline voor documentverwerking bouwen

In de vector store slaan we onze kennisbank op en halen we er informatie uit op. Deze implementatie bevat slimme chunking van documenten en compressie voor optimale retrieval.

Maak utils/vector_store.py aan:

from langchain_chroma import Chroma
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.retrievers.contextual_compression import ContextualCompressionRetriever

# Optimized chunking strategy
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=2048, # Large enough for context
chunk_overlap=200, # Prevents information loss at boundaries
length_function=len,
)

def load_pdf(file_path: str):
"""Load and intelligently chunk PDF documents"""
loader = PyPDFLoader(file_path)
pages = list(loader.lazy_load())

# Convert pages to optimally-sized chunks
documents = text_splitter.create_documents(
[page.page_content for page in pages]
)
return documents

def load_vector_store(file_path: str):
"""Create or load existing vector database with compression"""
vector_store = Chroma(
embedding_function=embeddings,
persist_directory=".chroma_db", # Persistent storage
collection_name="pdf_documents",
)

# Only process PDF if database is empty (efficiency optimization)
if vector_store._collection.count() == 0:
documents = load_pdf(file_path)
vector_store.add_documents(documents)

# Return compressed retriever for better results
return ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vector_store.as_retriever(search_kwargs={"k": 10}),
)

Waarom compressie belangrijk is: De contextual compression retriever haalt eerst meer documenten op dan nodig en gebruikt daarna een rerankingmodel om enkel de meest relevante te selecteren. Die aanpak in twee stappen verbetert de kwaliteit van de antwoorden aanzienlijk.

Stap 7: het brein van het systeem bouwen, de supervisor agent

De supervisor is de meest cruciale component. Hij beslist of een query documentretrieval nodig heeft of rechtstreeks beantwoord kan worden. Die intelligentie voorkomt onnodige API-calls en verkort de responstijden.

Maak nodes/supervise.py aan:

from langchain_core.prompts import ChatPromptTemplate
from nodes.retrieve_documents import retriever_tool

# The supervisor's decision-making prompt
prompt_template = ChatPromptTemplate([
("system", """
You are an intelligent routing agent with two options:

1. **Use the search tool** for queries requiring specific technical details:
- Step-by-step procedures
- Specific product information
- Domain-specific knowledge

2. **Respond directly** for general questions:
- Basic definitions
- Simple explanations
- Common knowledge topics
"""),
("human", "QUESTION: {user_query}")
])

def _supervise(state: AgentState) -> dict:
"""Make intelligent routing decisions"""
# Extract current query (original or rephrased)
query = state.rephrased_queries[-1] if state.rephrased_queries else state.messages[-1].content

# Create tool-enabled model
tool_model = chat_claude_4_sonnet.bind_tools([retriever_tool])

response = (prompt_template | tool_model).invoke({"user_query": query})

return {"original_user_query": query, "messages": [response]}

De intelligentie achter de routing: De supervisor gebruikt function calling om te beslissen of hij de retrieval tool aanroept. De tools_condition van LangGraph stuurt automatisch door naar de juiste volgende stap, afhankelijk van of er tools werden aangeroepen.

Stap 8: het systeem voor kennisretrieval bouwen

Ons retrievalsysteem gaat verder dan eenvoudige keyword matching. Het combineert semantisch zoeken met reranking om de meest relevante informatie te vinden.

Maak nodes/retrieve_documents.py aan:

from langchain.tools.retriever import create_retriever_tool
from langgraph.prebuilt import ToolNode

# Load knowledge base (replace with your document path)
vector_store = load_vector_store("resources/your_document.pdf")

# Create a tool that the supervisor can use
retriever_tool = create_retriever_tool(
vector_store,
"knowledge_base_search",
"Search comprehensive knowledge base for detailed technical information" # Describe the available knowledge to help the Supervisor decide whether retrieval is necessary.
)

# ToolNode automatically handles tool execution
_retrieve_documents = ToolNode([retriever_tool])

Waarom deze aanpak werkt: Door retrieval als tool te verpakken, laten we het supervisormodel zelf beslissen wanneer het die inzet. De ToolNode neemt alle complexiteit van het uitvoeren van tools en het formatteren van resultaten voor zijn rekening.

Stap 9: kwaliteitscontrole implementeren (relevantie van documenten beoordelen)

Niet alle opgehaalde documenten zijn bruikbaar. Ons beoordelingssysteem zorgt ervoor dat we enkel met relevante informatie verdergaan, wat hallucinaties voorkomt en de kwaliteit van de antwoorden verbetert.

Maak nodes/grade_documents.py aan:

from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate

class GradingResult(BaseModel):
documents_relevant: bool = Field(
description="Are the documents relevant to answering the question?"
)

grade_prompt = ChatPromptTemplate([
("system", """
Evaluate document relevance using these criteria:

RELEVANT if documents contain:
- Direct answers or related information
- Key concepts mentioned in the question
- Useful background context

NOT RELEVANT only if:
- Completely unrelated topic
- No useful information for the question
"""),
("human", "QUESTION: {query}\nDOCUMENTS: {documents}")
])

def _grade_documents(state: AgentState):
"""Assess whether retrieved documents can help answer the question"""
model = chat_claude_4_sonnet.with_structured_output(GradingResult)

result = (grade_prompt | model).invoke({
"query": state.original_user_query,
"documents": state.messages[-1].content
})

return {"documents": state.messages[-1].content}

De filosofie achter de beoordeling: Bij twijfel houden we een document liever bij. Beter potentieel nuttige documenten behouden dan informatie weggooien die van pas kan komen. De structured output zorgt voor een consistente evaluatie.

Stap 10: intelligente antwoordgeneratie

De generatienode maakt antwoorden die zowel accuraat als behulpzaam zijn. Hij is ontworpen om conversationeel te klinken en tegelijk gebaseerd te blijven op de opgehaalde informatie.

Maak nodes/generate.py aan:

from pydantic import BaseModel, Field

class GenerateResponse(BaseModel):
generated_answer: str = Field(description="Generated answer based on retrieved information")

generate_prompt = ChatPromptTemplate([
("system", """
Create helpful, accurate answers using the retrieved information.

Guidelines:
- Be clear and actionable
- Don't mention "retrieved information" or system processes
- Say "I don't know" if information is insufficient
- Focus on being genuinely helpful
"""),
("human", "QUESTION: {query}\nINFORMATION: {documents}")
])

def _generate(state: AgentState):
"""Generate grounded, helpful answers"""
model = chat_claude_4_sonnet.with_structured_output(GenerateResponse)

response = (generate_prompt | model).invoke({
"query": state.original_user_query,
"documents": state.documents,
})

return {"generated_answer": response.generated_answer, "rephrased_queries": []}

Ontwerpprincipe: De generatie is erop gericht om op een natuurlijke manier behulpzaam te zijn, eerder dan als systeemoutput te klinken. Zo voelt de interactie meer aan als een gesprek en wekt ze meer vertrouwen.

Stap 11: antwoorden valideren in meerdere stappen (kwaliteit en waarheid garanderen)

Ons validatiesysteem voert twee cruciale controles uit: nagaan of antwoorden gebaseerd zijn op de opgehaalde feiten (om hallucinaties te voorkomen) en bevestigen dat ze echt een antwoord bieden op de vraag van de gebruiker (om bruikbaarheid te garanderen).

Maak nodes/grade_answer.py aan:

from enum import Enum
from pydantic import BaseModel, Field

class GradingOutcome(str, Enum):
USEFUL = "useful" # Good answer, proceed
NOT_USEFUL = "not useful" # Try rephrasing query
NOT_SUPPORTED = "not supported" # Express uncertainty

class GradingResult(BaseModel):
grading: bool = Field(description="Positive or negative assessment")

# First validation: Hallucination detection
hallucination_prompt = ChatPromptTemplate([
("system", """
Check if the answer is supported by the retrieved facts.

GROUNDED: Answer directly supported by documents OR appropriately says "I don't know"
NOT GROUNDED: Answer contains unsupported claims or fabricated details
"""),
("human", "FACTS: {documents}\nANSWER: {answer}")
])

# Second validation: Usefulness assessment
usefulness_prompt = ChatPromptTemplate([
("system", """
Evaluate if the answer actually helps the user.

ADDRESSES: Directly responds to the question with sufficient detail
DOES NOT ADDRESS: Off-topic, too vague, or irrelevant
"""),
("human", "QUESTION: {query}\nANSWER: {answer}")
])

def _grade_answer(state: AgentState):
"""Two-stage validation: truth and usefulness"""
model = chat_claude_4_sonnet.with_structured_output(GradingResult)

# Stage 1: Truth check
hallucination_check = (hallucination_prompt | model).invoke({
"documents": state.documents,
"answer": state.generated_answer,
})

if hallucination_check.grading:
# Stage 2: Usefulness check
usefulness_check = (usefulness_prompt | model).invoke({
"query": state.original_user_query,
"answer": state.generated_answer,
})
return GradingOutcome.USEFUL if usefulness_check.grading else GradingOutcome.NOT_USEFUL
else:
return GradingOutcome.NOT_SUPPORTED

Waarom validatie in twee stappen belangrijk is: Een antwoord kan feitelijk correct zijn maar totaal nutteloos, of net behulpzaam maar met verzonnen details. Ons systeem vangt beide problemen op en reageert gepast.

Stap 12: slimme herformulering van queries (leren uit mislukkingen)

Als retrieval faalt, geeft het systeem niet op. Het analyseert waarom de query mogelijk niet werkte en probeert alternatieve benaderingen.

Maak nodes/rephrase_query.py aan:

class RephraseResponse(BaseModel):
rephrased_user_query: str = Field(description="Improved query for better search results")

rephrase_prompt = ChatPromptTemplate([
("system", """
Analyze why the previous search failed and create a better query.

Improvement strategies:
- Use more specific technical terminology
- Include relevant synonyms and related concepts
- Try different angles on the same question
- Make the query more searchable while preserving intent
"""),
("human", """
ORIGINAL: {query}
PREVIOUS ATTEMPTS: {previous_attempts}
WHAT WE FOUND: {documents}
LAST RESPONSE: {answer}

Create a new search approach for this question.
""")
])

def _rephrase_query(state: AgentState):
"""Intelligently rephrase queries based on previous failures"""
model = chat_claude_4_sonnet.with_structured_output(RephraseResponse)

response = (rephrase_prompt | model).invoke({
"query": state.original_user_query,
"previous_attempts": state.rephrased_queries,
"documents": state.documents,
"answer": state.generated_answer,
})

# Add new attempt to history
updated_attempts = state.rephrased_queries + [response.rephrased_user_query]
return {"rephrased_queries": updated_attempts}

Het leermechanisme: Door context mee te geven over eerdere pogingen en resultaten, kan het systeem uit zijn fouten leren en echt andere benaderingen proberen in plaats van kleine variaties.

Stap 13: elegant omgaan met onzekerheid

Heeft het systeem weinig vertrouwen in zijn antwoord, dan is eerlijkheid beter dan het risico om te misleiden. Door onzekerheid uit te spreken, behoudt het systeem het vertrouwen van de gebruiker.

Maak nodes/express_uncertainty.py en nodes/wrap_up.py aan:

# express_uncertainty.py
from langchain_core.messages import AIMessage

def _express_uncertainty(state: AgentState) -> AgentState:
"""Honestly communicate uncertainty when confidence is low"""
uncertainty_message = """I'm not entirely confident about this answer.
The available information doesn't provide enough context for complete accuracy."""

full_response = f"{uncertainty_message}\n\n{state.generated_answer}"
return {"messages": [AIMessage(content=full_response)]}

# wrap_up.py
def _wrap_up(state: AgentState) -> AgentState:
"""Provide confident, final response"""
return {"messages": [AIMessage(content=state.generated_answer)]}

Vertrouwen door transparantie: Gebruikers waarderen eerlijkheid over beperkingen. Deze aanpak bouwt vertrouwen op en laat gebruikers onderbouwde beslissingen nemen over hoe ze de informatie gebruiken.

Stap 14: de volledige intelligente workflow orkestreren

Nu brengen we alle componenten samen in een samenhangende workflow die bij elke stap intelligente beslissingen kan nemen.

Maak main.py aan:

from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import tools_condition

# Import all our intelligent agents
from models.state import AgentState, InputAgentState, OutputAgentState
from nodes.supervise import _supervise
# ... (other imports)

def decide_to_generate(state: AgentState) -> str:
"""Decide whether to generate or try rephrasing"""
# Generate if we have good documents OR we've tried 3 times
if (state.documents and len(state.documents) > 0) or len(state.rephrased_queries) >= 3:
return "generate"
return "rephrase_query"

def get_graph() -> StateGraph:
"""Create the intelligent workflow"""
workflow = StateGraph(AgentState, input_schema=InputAgentState, output_schema=OutputAgentState)

# Add all our intelligent agents
workflow.add_node("supervise", _supervise)
workflow.add_node("retrieve_documents", _retrieve_documents)
workflow.add_node("grade_documents", _grade_documents)
workflow.add_node("generate", _generate)
workflow.add_node("rephrase_query", _rephrase_query)
workflow.add_node("express_uncertainty", _express_uncertainty)
workflow.add_node("wrap_up", _wrap_up)

# Define the intelligent decision flow
workflow.add_edge(START, "supervise")

# Supervisor decides: search or respond directly
workflow.add_conditional_edges(
"supervise", tools_condition,
{"tools": "retrieve_documents", END: END}
)

workflow.add_edge("retrieve_documents", "grade_documents")

# Grade documents and decide next step
workflow.add_conditional_edges(
"grade_documents", decide_to_generate,
["generate", "rephrase_query"]
)

workflow.add_edge("rephrase_query", "supervise") # Try again

# Grade answer quality and route accordingly
workflow.add_conditional_edges(
"generate", _grade_answer,
{
GradingOutcome.NOT_SUPPORTED.value: "express_uncertainty",
GradingOutcome.NOT_USEFUL.value: "rephrase_query",
GradingOutcome.USEFUL.value: "wrap_up",
}
)

workflow.add_edge("express_uncertainty", END)
workflow.add_edge("wrap_up", END)

return workflow.compile()

De intelligentie in de flow: Elke conditional edge is een beslismoment waarop het systeem de kwaliteit evalueert en de beste volgende actie kiest. Zo ontstaat een zelfverbeterende lus die via iteratie betere resultaten oplevert.

Stap 15: configuratie en deployment

Maak je configuratiebestanden aan voor een productieklare deployment:

.env.example:

AWS_DEFAULT_REGION="eu-central-1"
LANGSMITH_API_KEY="your_key_here" # Optional: for monitoring
LANGSMITH_TRACING="true"
LANGSMITH_PROJECT="agentic-rag"

langgraph.json:

{
"dependencies": ["."],
"graphs": {"agentic-rag": "./main.py:_graph"},
"env": ".env"
}

Stap 16: je intelligente systeem uitvoeren en testen

Stel je AWS-credentials in (eenmalig):

aws configure

Configureer je omgeving:

cp .env.example .env # Edit .env with your actual values

Voeg je kennisbank toe: plaats je PDF in resources/ en pas het pad aan in retrieve_documents.py

Start LangGraph Studio om interactief te testen:

uv run langgraph dev

De intelligentie begrijpen: hoe beslissingen verlopen

We overlopen hoe ons systeem verschillende soorten queries afhandelt, zodat je ziet hoe die intelligentie werkt:

Eenvoudige query: “Wat is machine learning?”

  1. Supervisor analyseert → herkent algemene kennis
  2. Antwoordt rechtstreeks zonder retrieval → efficiënt en snel

Complexe query: “Hoe configureer ik de timeout-instellingen van AWS Lambda?”

  1. Supervisor analyseert → ziet dat er specifieke technische details nodig zijn
  2. Haalt documenten op → doorzoekt de kennisbank
  3. Beoordeelt documenten → controleert de relevantie voor hij verdergaat
  4. Genereert antwoord → stelt een gedetailleerd, bruikbaar antwoord op
  5. Beoordeelt antwoord → valideert kwaliteit en bruikbaarheid

Uitdagende query: “Hoe herstel ik de quantum flux capacitor?”

  1. Supervisor → beslist om te zoeken (de query klinkt technisch)
  2. Retrieval → vindt geen relevante documenten
  3. Beoordeling van documenten → stelt vast dat ze niet relevant zijn
  4. Herformulering van de query → probeert alternatieve termen (tot 3 pogingen)
  5. Nog altijd geen goede resultaten → drukt gepaste onzekerheid uit

Kenmerken die dit systeem productieklaar maken

Intelligent beheer van resources: de supervisor voorkomt onnodige API-calls door eenvoudige queries rechtstreeks af te handelen, wat de kosten drukt en de responstijden verkort.

Pipeline voor kwaliteitsbewaking: validatie in meerdere stappen zorgt ervoor dat antwoorden zowel feitelijk onderbouwd als echt nuttig zijn voor gebruikers.

Zelfverbeterende lus: mislukte queries leiden tot een slimme herformulering in plaats van een generiek “Ik weet het niet”.

Eerlijk over onzekerheid: bij lage zekerheid communiceert het systeem expliciet zijn beperkingen, wat het vertrouwen van gebruikers versterkt.

Modulaire architectuur: elke component heeft één verantwoordelijkheid, waardoor het systeem eenvoudig te onderhouden, te testen en uit te breiden is.

Monitoring in productie: de integratie met LangSmith geeft inzicht in de besluitvorming en in prestatiemetrics.

Je agentic RAG-systeem uitbreiden

Deze basis biedt tal van mogelijkheden om verder uit te bouwen:

Intelligentie over meerdere domeinen: voeg routinglogica toe om verschillende kennisdomeinen (technische documentatie, beleidsdocumenten enz.) met gespecialiseerde verwerkingsstrategieën af te handelen.

Geavanceerde kwaliteitsmetrics: implementeer verfijndere evaluatiecriteria, zoals feedbackloops met gebruikers en confidence scores voor antwoorden.

Personalisatielaag: houd gebruikersvoorkeuren bij en stem antwoorden af op het expertiseniveau en de interactiegeschiedenis.

Integratie van tools: voeg rekentools, API-integraties of realtime databronnen toe om meer soorten queries aan te kunnen.

Collaboratieve intelligentie: laat meerdere agents samenwerken, waarbij elke agent zich specialiseert in een ander aspect van de queryverwerking.

Conclusie: de toekomst van intelligente RAG

Een agentic RAG-systeem bouwen met LangGraph betekent een fundamentele verschuiving van reactieve naar intelligente informatiesystemen. In plaats van gewoon op te halen en te genereren, hebben we een systeem gebouwd dat het volgende doet:

  • Denkt na over de beste aanpak voor elke query
  • Evalueert de kwaliteit van zijn eigen werk
  • Past zich aan wanneer een eerste aanpak faalt
  • Communiceert eerlijk over zijn beperkingen

De combinatie van de workfloworkestratie van LangGraph, de productieklare LLM's van AWS Bedrock en een doordacht agentontwerp vormt een robuuste basis voor een nieuwe generatie RAG-toepassingen. Deze systemen doen meer dan vragen beantwoorden. Ze begrijpen context, leren uit mislukkingen en verbeteren hun antwoorden voortdurend.

Nu AI-systemen steeds vaker opduiken in bedrijfstoepassingen, wordt deze agentic aanpak essentieel om betrouwbare, geloofwaardige en echt behulpzame AI-assistenten te bouwen waarop gebruikers kunnen rekenen bij kritieke beslissingen.

De toekomst van RAG ligt bij intelligente systemen die over hun eigen processen kunnen redeneren en zich aanpassen om gebruikers de best mogelijke resultaten te bieden. Deze tutorial legt de basis om zulke systemen te bouwen.

Volledige implementatie en bronnen

Je vindt de volledige implementatie van dit agentic RAG-systeem, inclusief alle code, configuratiebestanden en extra productiefeatures, op GitHub: https://github.com/UnikooBelgium/ws-agentic-rag