Aller au contenu

Patterns d'Architecture RAG Agentique : Construire des Systèmes de Récupération Autonomes

· 13 min read · default
ragai-agentslangchainllamaindexarchitectureaidevops

Introduction

La Génération Augmentée par Récupération a commencé comme une idée simple : au lieu de se fier uniquement à la mémoire paramétrique d'un modèle de langage, récupérer des documents pertinents d'une base de connaissances externe et les inclure dans le contexte du prompt. Ce pattern de RAG naïf, consistant en embed-récupérer-générer, a fonctionné étonnamment bien pour des réponses simples à des questions sur des collections de documents structurés. Mais quand les équipes ont poussé le RAG en production pour des cas d'usage plus complexes, ses limitations sont devenues douloureusement évidentes. Les requêtes nécessitant un raisonnement à travers plusieurs documents, les questions ambiguës nécessitant des clarifications et les bases de connaissances avec des types de contenu hétérogènes ont tous exposé la fragilité du pipeline récupérer-puis-générer.

Le RAG agentique représente un changement fondamental dans la façon dont nous architecturons les systèmes de récupération. Au lieu d'un pipeline fixe où chaque étape alimente linéairement la suivante, le RAG agentique donne au modèle de langage la capacité de planifier sa stratégie de récupération, d'évaluer la qualité des résultats récupérés, de reformuler les requêtes quand la récupération initiale échoue, et de décider quand il a suffisamment d'informations pour générer une réponse finale. Le modèle devient un participant actif du processus de récupération plutôt qu'un consommateur passif du contexte récupéré.

Ce guide couvre les patterns d'architecture, les frameworks d'implémentation, les méthodes d'évaluation et les considérations de production pour construire des systèmes RAG agentiques. Nous nous appuyons sur des déploiements réels dans des bases de connaissances d'entreprise, des systèmes de support client et des plateformes de documentation technique où ces patterns ont été validés à grande échelle.

Du RAG Naïf au RAG Agentique : Ce Qui a Changé

Le RAG naïf suit un pipeline déterministe en trois étapes : la requête de l'utilisateur est vectorisée, une recherche de similarité vectorielle récupère les top-k fragments de documents les plus similaires, et ces fragments sont concaténés dans un prompt que le LLM utilise pour générer une réponse. Ce pipeline a trois faiblesses fondamentales que le RAG agentique corrige.

Premièrement, le RAG naïf suppose que la requête de l'utilisateur est déjà bien formulée pour la récupération. En pratique, les requêtes des utilisateurs sont souvent vagues, multifacettes ou utilisent une terminologie qui ne correspond pas au vocabulaire des documents indexés. Un utilisateur demandant des informations sur les échecs de déploiement pourrait avoir besoin de documents sur la gestion des erreurs, la configuration de l'infrastructure et les pipelines CI/CD, mais une seule recherche vectorielle peut ne faire remonter qu'une seule de ces facettes.

Deuxièmement, le RAG naïf n'a pas de porte de qualité. Si les documents récupérés sont non pertinents, obsolètes ou insuffisants, le pipeline continue quand même et le LLM génère une réponse à partir d'un contexte médiocre. Il n'y a aucun mécanisme pour que le système reconnaisse un échec de récupération et réessaie.

Troisièmement, le RAG naïf traite toutes les requêtes de manière identique. Une question de recherche factuelle, une question analytique complexe et une question nécessitant une synthèse de multiples sources passent toutes par le même pipeline récupérer-générer. Le RAG agentique introduit une logique conditionnelle qui sélectionne différentes stratégies de récupération et de génération en fonction des caractéristiques de la requête.

La transition du RAG naïf au RAG agentique implique l'ajout de trois capacités : la planification de requêtes (décomposer les requêtes complexes en sous-requêtes), l'évaluation de la récupération (évaluer si le contexte récupéré est suffisant) et le raffinement itératif (reformuler les requêtes et re-récupérer quand les résultats sont insuffisants). Ces capacités transforment un pipeline statique en un système dynamique et autocorrectif.

Patterns d'Architecture Fondamentaux

Les systèmes RAG agentiques sont construits à partir d'un petit nombre de patterns composables. Comprendre ces patterns vous permet de concevoir des systèmes adaptés à votre cas d'usage spécifique.

Routage

Le pattern agentique le plus simple route les requêtes vers différents backends de récupération en fonction de la classification de la requête. Un agent routeur analyse la requête entrante et la dirige vers la source de connaissances la plus appropriée :

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

router_prompt = ChatPromptTemplate.from_messages([
    ("system", """Classify the user query into one of these categories:
    - technical_docs: Questions about API usage, configuration, or code
    - policy: Questions about company policies, procedures, or compliance
    - support: Questions about troubleshooting or known issues
    - general: General questions that don't fit other categories
    
    Respond with ONLY the category name."""),
    ("human", "{query}")
])

router_chain = router_prompt | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser()

Le routage est précieux quand votre base de connaissances couvre plusieurs domaines avec différentes stratégies d'indexation. La documentation technique pourrait être indexée avec un découpage conscient du code et des embeddings, tandis que les documents de politique utilisent un découpage sémantique avec des filtres de métadonnées.

Décomposition de Requêtes

Les questions complexes nécessitent souvent des informations provenant de multiples fragments de documents qui n'apparaîtraient pas ensemble dans une seule récupération. La décomposition de requêtes divise une requête complexe en sous-requêtes indépendantes, récupère pour chacune et synthétise les résultats :

decomposition_prompt = ChatPromptTemplate.from_messages([
    ("system", """Break the following complex question into 2-4 simpler 
    sub-questions that, when answered together, provide a complete answer 
    to the original question. Return as a JSON array of strings."""),
    ("human", "{query}")
])

# Example: "How does our API rate limiting compare to competitors 
# and what are customers saying about it?"
# Decomposes to:
# ["What are our current API rate limiting policies?",
#  "What rate limiting do our main competitors use?",
#  "What customer feedback have we received about rate limiting?"]

Autocorrection

L'autocorrection est le pattern qui distingue le plus le RAG agentique du RAG naïf. Après la récupération, une étape de notation évalue si les documents récupérés sont pertinents et suffisants. S'ils ne le sont pas, le système reformule la requête et réessaie :

grading_prompt = ChatPromptTemplate.from_messages([
    ("system", """You are a retrieval quality grader. Given a user question 
    and a set of retrieved documents, determine:
    1. Are the documents relevant to the question? (yes/no)
    2. Do the documents contain sufficient information to answer? (yes/no)
    3. If insufficient, suggest a reformulated query.
    
    Respond as JSON with keys: relevant, sufficient, reformulated_query"""),
    ("human", "Question: {query}\n\nDocuments: {documents}")
])

Ce pattern permet typiquement 2-3 tentatives de récupération avant de recourir à un message d'échec élégant. Les requêtes reformulées passent souvent de la recherche par similarité sémantique à la recherche par mots-clés, élargissent le périmètre ou ciblent des champs de métadonnées spécifiques.

LangGraph pour l'Orchestration d'Agents avec État

LangGraph est devenu le framework standard pour construire des systèmes RAG agentiques car il fournit une gestion d'état explicite, un routage conditionnel et un support de cycles que les abstractions plus simples basées sur les chaînes de LangChain ne peuvent pas exprimer.

Un système RAG agentique basé sur LangGraph est défini comme un graphe d'états où les nœuds représentent les étapes de traitement et les arêtes représentent les transitions entre les étapes. Les arêtes conditionnelles permettent au graphe de se ramifier en fonction des résultats intermédiaires :

from langgraph.graph import StateGraph, END
from typing import TypedDict, List, Annotated
from operator import add

class AgentState(TypedDict):
    query: str
    sub_queries: List[str]
    documents: List[dict]
    generation: str
    retry_count: int
    retrieval_grade: str

def route_query(state: AgentState) -> AgentState:
    """Classify and route the incoming query."""
    query = state["query"]
    classification = router_chain.invoke({"query": query})
    state["route"] = classification
    return state

def retrieve(state: AgentState) -> AgentState:
    """Retrieve documents based on query and route."""
    query = state["query"]
    docs = retriever.invoke(query)
    state["documents"] = docs
    return state

def grade_documents(state: AgentState) -> AgentState:
    """Grade retrieved documents for relevance and sufficiency."""
    grade = grading_chain.invoke({
        "query": state["query"],
        "documents": state["documents"]
    })
    state["retrieval_grade"] = grade["sufficient"]
    state["retry_count"] = state.get("retry_count", 0) + 1
    if not grade["sufficient"] and grade.get("reformulated_query"):
        state["query"] = grade["reformulated_query"]
    return state

def generate(state: AgentState) -> AgentState:
    """Generate final answer from retrieved context."""
    answer = generation_chain.invoke({
        "query": state["query"],
        "documents": state["documents"]
    })
    state["generation"] = answer
    return state

def should_retry(state: AgentState) -> str:
    """Decide whether to retry retrieval or proceed to generation."""
    if state["retrieval_grade"] == "yes":
        return "generate"
    if state["retry_count"] >= 3:
        return "generate"  # Give up and generate with what we have
    return "retrieve"

# Build the graph
workflow = StateGraph(AgentState)
workflow.add_node("route", route_query)
workflow.add_node("retrieve", retrieve)
workflow.add_node("grade", grade_documents)
workflow.add_node("generate", generate)

workflow.set_entry_point("route")
workflow.add_edge("route", "retrieve")
workflow.add_edge("retrieve", "grade")
workflow.add_conditional_edges("grade", should_retry, {
    "retrieve": "retrieve",
    "generate": "generate"
})
workflow.add_edge("generate", END)

app = workflow.compile()

La structure du graphe rend le flux de contrôle explicite et débuggable. Vous pouvez visualiser le graphe, tracer les exécutions à travers chaque nœud et comprendre exactement pourquoi le système a pris un chemin particulier pour une requête donnée. Cette transparence est critique pour les systèmes de production où vous devez diagnostiquer les pannes et expliquer le comportement.

LangGraph supporte également des patterns plus avancés comme la récupération parallèle à travers plusieurs index, des points de contrôle humain-dans-la-boucle où l'agent se met en pause pour la confirmation de l'utilisateur, et un état persistant qui survit entre les conversations.

Stratégies de Récupération

La couche de récupération dans un système RAG agentique est typiquement plus sophistiquée qu'une simple recherche dans un vector store. Les systèmes de production combinent plusieurs stratégies de récupération.

Recherche Hybride

La recherche hybride combine la récupération vectorielle dense avec la récupération éparse par mots-clés, vous donnant la compréhension sémantique des embeddings avec la précision du matching de mots-clés BM25 :

from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
from langchain_chroma import Chroma

vectorstore = Chroma(
    collection_name="documents",
    embedding_function=embedding_model,
)
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})

bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 10

hybrid_retriever = EnsembleRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    weights=[0.6, 0.4],
)

Re-ranking

Après la récupération initiale, un re-ranker à encodeur croisé note chaque document par rapport à la requête avec une précision bien supérieure à la similarité d'encodeur bi-directionnel. C'est coûteux en calcul mais améliore dramatiquement la précision :

from langchain.retrievers import ContextualCompressionRetriever
from langchain_cohere import CohereRerank

reranker = CohereRerank(model="rerank-v3.5", top_n=5)
compression_retriever = ContextualCompressionRetriever(
    base_compressor=reranker,
    base_retriever=hybrid_retriever,
)

Récupération Multi-Index

Les bases de connaissances de production couvrent souvent plusieurs index avec différents schémas, modèles d'embeddings et types de documents. Un système agentique peut interroger plusieurs index en parallèle et fusionner les résultats :

async def multi_index_retrieve(query: str, indexes: list) -> list:
    """Retrieve from multiple indexes in parallel."""
    import asyncio
    
    async def retrieve_from_index(index, query):
        return await index.aretrieve(query)
    
    tasks = [retrieve_from_index(idx, query) for idx in indexes]
    results = await asyncio.gather(*tasks)
    
    # Flatten and deduplicate
    all_docs = []
    seen_ids = set()
    for result_set in results:
        for doc in result_set:
            if doc.metadata["id"] not in seen_ids:
                all_docs.append(doc)
                seen_ids.add(doc.metadata["id"])
    
    return all_docs

Évaluation avec RAGAS et DeepEval

L'évaluation des systèmes RAG agentiques nécessite de mesurer plusieurs dimensions de qualité. RAGAS et DeepEval sont les deux frameworks d'évaluation les plus largement adoptés, chacun avec des forces distinctes.

RAGAS fournit un ensemble de métriques sans référence qui évaluent la qualité du RAG sans nécessiter de réponses de vérité terrain pour chaque requête de test :

from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
)
from datasets import Dataset

eval_dataset = Dataset.from_dict({
    "question": questions,
    "answer": generated_answers,
    "contexts": retrieved_contexts,
    "ground_truth": reference_answers,
})

results = evaluate(
    dataset=eval_dataset,
    metrics=[
        faithfulness,
        answer_relevancy,
        context_precision,
        context_recall,
    ],
)

print(results)

La fidélité mesure si la réponse générée est soutenue par le contexte récupéré, détectant les hallucinations. La pertinence de la réponse mesure si la réponse aborde la question. La précision du contexte mesure si les documents récupérés sont pertinents, et le rappel du contexte mesure si la récupération a capturé toutes les informations nécessaires.

DeepEval étend l'évaluation avec des métriques supplémentaires particulièrement pertinentes pour les systèmes agentiques :

from deepeval import evaluate
from deepeval.metrics import (
    FaithfulnessMetric,
    AnswerRelevancyMetric,
    ContextualRelevancyMetric,
    HallucinationMetric,
)
from deepeval.test_case import LLMTestCase

test_case = LLMTestCase(
    input="How do I configure rate limiting?",
    actual_output=generated_answer,
    expected_output=reference_answer,
    retrieval_context=retrieved_chunks,
)

metrics = [
    FaithfulnessMetric(threshold=0.8),
    AnswerRelevancyMetric(threshold=0.7),
    ContextualRelevancyMetric(threshold=0.7),
    HallucinationMetric(threshold=0.5),
]

evaluate(test_cases=[test_case], metrics=metrics)

Pour le RAG agentique spécifiquement, vous devriez aussi mesurer l'efficacité de récupération : combien de rounds de récupération le système nécessite en moyenne, quel pourcentage de requêtes nécessite une reformulation, et comment la qualité de la réponse se dégrade à travers les tentatives de reprise. Ces métriques sont spécifiques à la boucle agentique et ne sont pas couvertes par les frameworks standards d'évaluation RAG.

Patterns de Production

Passer le RAG agentique du prototype à la production nécessite d'aborder des préoccupations de fiabilité, de sécurité et d'exploitation qui n'émergent pas pendant le développement.

Garde-fous

Tout système RAG de production a besoin de garde-fous qui empêchent le LLM de générer des réponses nuisibles, hors sujet ou sans fondement factuel :

from guardrails import Guard
from guardrails.hub import ToxicLanguage, CompetitorCheck

guard = Guard().use_many(
    ToxicLanguage(on_fail="exception"),
    CompetitorCheck(
        competitors=["competitor_a", "competitor_b"],
        on_fail="fix"
    ),
)

raw_response = generation_chain.invoke({"query": query, "documents": docs})
validated_response = guard.validate(raw_response)

Chaînes de Repli

Quand la boucle agentique ne parvient pas à trouver un contexte suffisant après le maximum de tentatives, le système a besoin d'une dégradation élégante plutôt que de générer une réponse peu fiable :

def generate_with_fallback(state: AgentState) -> AgentState:
    if state["retrieval_grade"] != "yes" and state["retry_count"] >= 3:
        state["generation"] = (
            "I wasn't able to find sufficient information in our knowledge base "
            "to fully answer your question. Here's what I found:\n\n"
            f"{partial_answer_from_context(state['documents'])}\n\n"
            "For a complete answer, I'd recommend contacting the support team."
        )
        state["confidence"] = "low"
    else:
        state["generation"] = generation_chain.invoke({
            "query": state["query"],
            "documents": state["documents"]
        })
        state["confidence"] = "high"
    return state

Humain-dans-la-Boucle

Pour les applications à enjeux élevés, LangGraph supporte des points d'interruption où l'agent met l'exécution en pause et attend l'approbation humaine :

from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()

# Add interrupt before generation for sensitive queries
workflow.add_node("human_review", lambda state: state)
workflow.add_conditional_edges(
    "grade",
    lambda state: "human_review" if state.get("sensitive") else "generate",
    {"human_review": "human_review", "generate": "generate"}
)

app = workflow.compile(
    checkpointer=checkpointer,
    interrupt_before=["human_review"]
)

Observabilité avec LangSmith et Phoenix

L'observabilité est non négociable pour les systèmes agentiques en production. La nature non déterministe du flux de contrôle piloté par LLM signifie que vous ne pouvez pas prédire ni tester chaque chemin d'exécution possible. Vous avez besoin d'un traçage complet pour comprendre ce que fait votre système en production.

LangSmith fournit un traçage de bout en bout pour les applications LangGraph :

import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your-api-key"
os.environ["LANGCHAIN_PROJECT"] = "agentic-rag-production"

# All LangGraph invocations are automatically traced
result = app.invoke({"query": "How do I configure rate limiting?"})

Chaque trace montre le chemin d'exécution complet à travers le graphe : quels nœuds ont été visités, quelles étaient les entrées et sorties du LLM à chaque étape, les résultats de récupération, les décisions de notation et les comptes de tokens. C'est essentiel pour débugger les cas où l'agent prend un chemin inattendu ou génère une réponse médiocre.

Arize Phoenix fournit une alternative open source avec un focus sur le monitoring de la qualité de récupération :

import phoenix as px
from phoenix.trace.langchain import LangChainInstrumentor

px.launch_app()
LangChainInstrumentor().instrument()

# Traces are now collected in Phoenix
result = app.invoke({"query": user_query})

Phoenix est particulièrement précieux pour surveiller la qualité de récupération dans le temps. Il suit la dérive des embeddings, les distributions de pertinence de récupération et peut alerter quand la qualité de récupération se dégrade, ce qui indique souvent que la base de connaissances a dérivé de la distribution d'entraînement du modèle d'embeddings et nécessite une ré-indexation.

Métriques clés à surveiller en production :

# Custom metrics to track
metrics = {
    "avg_retrieval_rounds": [],
    "reformulation_rate": [],
    "fallback_rate": [],
    "latency_p50_ms": [],
    "latency_p99_ms": [],
    "token_cost_per_query": [],
    "faithfulness_score": [],
}

Optimisation des Coûts et de la Latence

Les systèmes RAG agentiques sont intrinsèquement plus coûteux et plus lents que le RAG naïf car ils effectuent de multiples appels LLM par requête. Optimiser les coûts et la latence sans sacrifier la qualité nécessite des décisions architecturales soigneuses.

Utilisez des modèles plus petits pour le routage et la notation. Le routeur et le notateur de documents n'ont pas besoin de la capacité complète de raisonnement d'un modèle de frontière. GPT-4o-mini ou Claude 3.5 Haiku peuvent gérer ces tâches pour une fraction du coût et de la latence :

# Use a small, fast model for routing and grading
routing_model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
grading_model = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# Use a larger model only for final generation
generation_model = ChatOpenAI(model="gpt-4o", temperature=0.1)

Mettez en cache les résultats de récupération de manière agressive. De nombreuses requêtes d'utilisateurs sont des variations de la même question sous-jacente. Un cache sémantique qui fait correspondre les requêtes dans un seuil de similarité peut éliminer les récupérations et appels LLM redondants :

from langchain_community.cache import RedisSemanticCache

set_llm_cache(RedisSemanticCache(
    redis_url="redis://localhost:6379",
    embedding=embedding_model,
    score_threshold=0.95,
))

Définissez des timeouts agressifs à chaque étape pour prévenir une latence incontrôlée. Un seul appel LLM lent ne devrait pas causer le timeout de toute la requête :

from asyncio import wait_for, TimeoutError

async def retrieve_with_timeout(state, timeout_seconds=5):
    try:
        return await wait_for(retrieve(state), timeout=timeout_seconds)
    except TimeoutError:
        state["documents"] = []
        state["retrieval_grade"] = "no"
        return state

Études de Cas Réels et Anti-Patterns

Le pattern d'échec le plus courant dans les déploiements de RAG agentique est la boucle de retry infinie. Sans un plafond strict sur les tentatives de récupération et un comportement de repli clair, l'agent peut cycler à travers des reformulations indéfiniment, brûlant des tokens et de la latence. Définissez toujours des limites de retry explicites et mesurez votre taux de reformulation. Si plus de 20% des requêtes nécessitent une reformulation, le problème se situe probablement dans votre couche de récupération (découpage médiocre, mauvais modèle d'embeddings, index obsolète) plutôt que dans la logique de l'agent.

Un autre anti-pattern fréquent est le sur-routage. Les équipes construisent parfois des graphes de routage complexes avec des dizaines d'index spécialisés alors qu'un seul récupérateur hybride bien conçu serait plus performant. Commencez avec l'architecture la plus simple qui répond à vos exigences et ajoutez de la complexité uniquement quand vous avez des métriques montrant que les approches plus simples sont insuffisantes.

Le bourrage de fenêtre de contexte est un troisième anti-pattern. Récupérer 20 fragments de documents et les entasser tous dans le prompt gaspille des tokens et peut en fait dégrader la qualité de la réponse car le LLM a du mal à identifier l'information pertinente dans un océan de contexte marginalement lié. Le re-ranking vers un petit nombre de fragments de haute qualité (typiquement 3-5) surpasse systématiquement les fenêtres de contexte plus grandes dans les benchmarks d'évaluation.

Enfin, méfiez-vous de la sur-optimisation pilotée par l'évaluation. Les équipes qui optimisent uniquement pour les scores RAGAS peuvent construire des systèmes qui scorent bien sur les benchmarks mais produisent des réponses excessivement prudentes et évasives que les utilisateurs trouvent inutiles. Équilibrez l'évaluation quantitative avec une revue qualitative des conversations réelles d'utilisateurs.

Le Futur : Systèmes RAG Multi-Agents

La prochaine frontière du RAG agentique est les systèmes multi-agents où des agents spécialisés collaborent sur des tâches complexes de récupération et de synthèse. Au lieu d'un seul agent gérant l'ensemble du pipeline de récupération-génération, vous déployez une équipe d'agents avec différentes spécialisations.

Un agent de recherche gère la décomposition des requêtes et la récupération multi-saut à travers de grandes collections de documents. Un agent de vérification des faits vérifie les affirmations générées contre les documents sources. Un agent de synthèse combine les résultats de multiples sous-requêtes en une réponse cohérente et bien structurée. Un agent éditeur revoit la sortie finale pour la clarté, la précision et le ton.

from langgraph.graph import StateGraph

# Multi-agent RAG architecture
multi_agent = StateGraph(MultiAgentState)
multi_agent.add_node("planner", planner_agent)
multi_agent.add_node("researcher", researcher_agent)
multi_agent.add_node("fact_checker", fact_check_agent)
multi_agent.add_node("synthesizer", synthesis_agent)

multi_agent.set_entry_point("planner")
multi_agent.add_edge("planner", "researcher")
multi_agent.add_edge("researcher", "fact_checker")
multi_agent.add_conditional_edges(
    "fact_checker",
    lambda s: "researcher" if s["needs_more_research"] else "synthesizer",
)
multi_agent.add_edge("synthesizer", END)

Cette architecture est plus coûteuse par requête mais gère les questions de recherche complexes avec lesquelles les systèmes à agent unique ont des difficultés. Le défi de conception clé est de définir des interfaces claires entre agents et de s'assurer que la surcharge de la communication inter-agents n'annule pas les bénéfices de la spécialisation.

L'écosystème d'outils converge pour supporter nativement les patterns multi-agents. La composition de sous-graphes de LangGraph, la couche d'orchestration d'agents de LlamaIndex et CrewAI fournissent tous des primitives pour construire des systèmes RAG multi-agents. Au fur et à mesure que ces frameworks mûrissent et que les coûts continuent de baisser, le RAG multi-agents deviendra pratique pour un plus large éventail d'applications de production. Les principes couverts dans ce guide — gestion d'état explicite, évaluation de la qualité de récupération, replis élégants et observabilité complète — restent essentiels que vous déployiez un agent unique ou une équipe d'agents spécialisés.