Laisser Gemini choisir la source : FAISS, Tavily et Direct Answers dans LangGraph
Créez un petit flux de travail LangGraph dans lequel Gemini dirige chaque question vers une base de connaissances FAISS, une recherche web Tavily ou une réponse directe, en utilisant un routage de sortie structurée.
La plupart des prototypes de systèmes de questions-réponses font passer chaque requête par un pipeline fixe, bien que les questions diffèrent quant à leurs besoins : certaines dépendent de documents internes à une entreprise, d’autres de faits qui changent quotidiennement, et d’autres encore ne nécessitent que des connaissances générales. Cette présentation met en place un flux de travail Python compact dans lequel un modèle de langage examine d’abord chaque question avant de la diriger vers l’endroit approprié : un stockage vectoriel FAISS pour les connaissances internes, Tavily pour des résultats web en temps réel, ou directement vers Gemini. À la fin, vous comprendrez comment les états, les nœuds et les arêtes conditionnelles s’assemblent dans LangGraph, pourquoi une sortie structurée rend un routeur d’LLM fiable, et où ce design de taille pédagogique nécessite des améliorations avant d’être utilisé par des utilisateurs réels. Si vous souhaitez un catalogue plus complet de formes graphiques, consultez l’article sur le routage, le fan-out.
Les modèles de critique et d’approbation dans LangGraph complètent cette mise en pratique concrète.Trois questions, trois sources différentes
Imaginons trois utilisateurs. Le premier demande des informations sur la politique de congés de l’entreprise ; seuls les documents internes peuvent y répondre. Le deuxième souhaite connaître la météo d’aujourd’hui dans l’Uttarakhand ; aucun document interne ne contient cette information, donc le système doit rechercher sur Internet. Le troisième demande ce qu’est RAG ; le modèle en connaît déjà la réponse, et chercher des informations supplémentaires ne ferait qu’augmenter le temps de réponse.
L’architecture cible place donc un routeur alimenté par Gemini devant trois branches qui convergent toutes vers une seule étape de génération de réponse :
User Question
|
v
+----------------+
| AI Router |
| (Gemini) |
+----------------+
/ | \
/ | \
v v v
FAISS Tavily Gemini
Internal DB Web Direct
\ | /
\ | /
v v v
+----------------+
| Generate Answer|
| Gemini |
+----------------+
|
v
Answer
Le routeur constitue le cœur de cette conception. La solution rapide qui semble attractive est la correspondance par mots-clés, comme le montre le pseudocode ci-dessous, où des mots spécifiques dans la question sélectionnent une branche :
if "weather" in question:
use_tavily()
elif "leave" in question:
use_faiss()
else:
use_gemini()
Ici, cette décision est déléguée à Gemini. Comme le modèle interprète la question plutôt que de la scanner pour y détecter des mots déclencheurs, ce flux de travail est qualifié d’agentif.
IA générative, agents et flux de travail agentifs
Ces termes sont souvent utilisés de manière interchangeable, mais ils décrivent différents niveaux d’implication du modèle.
IA générative simple
L’application la plus basique envoie une instruction à un modèle et renvoie tout ce que celui-ci produit :
User Question
|
v
LLM
|
v
Answer
Il peut expliquer le RAG à partir des données d’entraînement, mais il ne sait rien de votre politique de congés.
Agents qui appellent des outils
User Question
|
v
LLM
|
+------> Database
|
+------> Web Search
|
+------> API
|
v
Answer
Une question météorologique, par exemple, peut être répondue en interrogeant un service météo plutôt que de faire en sorte que le modèle invente une prévision plausible.
Flux de travail agents
Un flux de travail agents permet au modèle d’influencer le parcours suivi lors de l’exécution en temps réel. Dans ce projet, le flux se présente comme suit :
Question
|
v
Router
|
+----> FAISS
|
+----> Tavily
|
+----> Gemini
Le développeur définit les trois itinéraires possibles ; le modèle ne fait que choisir parmi eux pour chaque question reçue. Il n’a pas un contrôle total sur l’application, mais seulement un menu d’actions autorisées. « Flux de travail de routage agents » est le nom le plus précis pour cette organisation.
Mise en place du projet
Vous avez besoin de Python 3.10 ou ultérieur, d’une clé API pour Google Gemini et d’une clé API pour Tavily. Installez les bibliothèques en une seule fois :
pip install -U langchain langchain-google-genai langchain-community langgraph faiss-cpu tavily-python python-dotenv pydantic
Conservez les deux clés dans un fichier .env plutôt que dans le code source :
GOOGLE_API_KEY=your_google_api_key
TAVILY_API_KEY=your_tavily_api_key
Les importations comprennent des outils d’aide à la typographie, Pydantic pour les schémas de routage, des wrappers de documents et de FAISS de LangChain, les classes de chat et d’embedding de Gemini, les primitives graphiques de LangGraph ainsi que le client Tavily :
import os
from typing import List, Literal
from typing_extensions import TypedDict
from dotenv import load_dotenv
from pydantic import BaseModel
from langchain_core.documents import Document
from langchain_community.vectorstores import FAISS
from langchain_google_genai import (
ChatGoogleGenerativeAI,
GoogleGenerativeAIEmbeddings,
)
from langgraph.graph import StateGraph, START, END
from tavily import TavilyClient
La étape suivante, présentée sous forme d’extrait en une ligne, consiste simplement à charger les variables d’environnement :
Load the environment variables:
Avec override=True, les valeurs provenant de .env prennent le pas sur les variables déjà définies dans votre shell :
load_dotenv(override=True)
Configuration de Gemini pour le routage, les réponses et les embeddings
Le modèle de chat remplit deux fonctions. Il choisit d’abord quelle source doit gérer une question, puis il compose la réponse finale à partir du contexte collecté. La configuration ci-dessous active deux tentatives automatiques et laisse les limites de tokens et de délai non définies :
model = ChatGoogleGenerativeAI(
model="gemini-3.6-flash",
max_tokens=None,
timeout=None,
max_retries=2,
api_key=os.getenv("GOOGLE_API_KEY"),
)
Les identifiants des modèles changent fréquemment, il est donc nécessaire de vérifier le nom indiqué dans l’extrait par rapport à la liste actuelle des modèles Gemini avant de le lancer.
Les embeddings constituent une question distincte gérée par un modèle séparé. Gemini Embedding convertit chaque document en un vecteur numérique :
embeddings = GoogleGenerativeAIEmbeddings(
model="models/gemini-embedding-001",
google_api_key=os.getenv("GOOGLE_API_KEY"),
)
Ce sont les vecteurs qui rendent possible la recherche sémantique : les textes ayant un sens similaire se trouvent proches l’un de l’autre dans l’espace vectoriel, ce qui permet à une question de récupérer des passages pertinents même lorsqu’elle ne partage que peu de mots exacts avec eux.
Une petite base de connaissances interne dans FAISS
Afin de maintenir l’attention sur le flux de travail, la base de connaissances ne contient que trois documents courts : une politique de remboursement sur 30 jours, un droit à 20 jours de congé payé avec une exigence de préavis de sept jours, ainsi que les heures d’assistance en semaine. Dans un système réel, ces textes proviendraient de manuels, de PDF, de pages Notion, de tickets d’assistance, de documents internes ou de lignes de base de données.
documents = [
Document(
page_content="""
Our company provides a 30-day refund policy.
Customers can request a refund within 30 days of purchase.
"""
),
Document(
page_content="""
Employees receive 20 paid vacation days per year.
Vacation requests must be submitted at least 7 days in advance.
"""
),
Document(
page_content="""
The company provides technical support from Monday to Friday,
9 AM to 6 PM IST.
"""
),
]
La création du magasin et son encapsulation en tant que récupérateur nécessite deux appels. En définissant k à 3, on demande les trois documents les plus proches ; avec ce corpus simplifié, cela signifie que chaque document est retourné classé selon sa similarité :
vector_store = FAISS.from_documents(
documents,
embeddings,
)
retriever = vector_store.as_retriever(
search_kwargs={"k": 3}
)
FAISS ne comprend pas le langage ; c’est un index pour la recherche de voisins les plus proches. Le modèle d’incorporation transforme la question reçue en un vecteur, et FAISS renvoie les documents stockés dont les vecteurs sont les plus proches de celui-ci.
Ajout d’un client Tavily pour des informations en temps réel
La recherche sur le Web ne nécessite qu’une instance client créée à partir de la deuxième clé API :
tavily_client = TavilyClient(
api_key=os.getenv("TAVILY_API_KEY")
)
Conception de l’état partagé
L’état est l’idée centrale de LangGraph : un dictionnaire typé qui circule à travers le graphe, que chaque nœud lit et auquel il contribue. Ce flux de travail nécessite la question, tous les documents récupérés, les résultats de Tavily, la source choisie ainsi que la réponse finale :
class AgentState(TypedDict):
question: str
documents: List[Document]
tavily_response: str
source: str
answer: str
Conceptuellement, cela fonctionne comme un enregistrement partagé que chaque étape peut consulter :
AgentState
|
+-----------+-----------+
| | |
question documents source
|
tavily_response
|
answer
Un nœud reçoit l’état actuel, utilise les champs qui l’intéressent et renvoie des mises à jour, que LangGraph fusionne avant de transmettre l’état au nœud suivant.
Le nœud de récupération FAISS
Ce nœud prend la question provenant de l’état, la passe par le moteur de recherche et stocke les documents correspondants :
def retrieve_from_faiss(state : AgentState) -> AgentState:
question = state['question']
""" Fetch the details from the FAISS vector database
"""
result = retriever.invoke(question)
return {**state, "documents": result}
Il s’exécute uniquement lorsque le routeur a choisi la base de connaissances interne. Une question comme celle ci-dessous constitue un déclencheur typique :
"What is our company leave policy?"
Pour cette entrée, le récupérateur doit afficher le document relatif aux vacances :
Employees receive 20 paid vacation days per year.
Vacation requests must be submitted at least 7 days in advance.
Le nœud de recherche Tavily
Le nœud de recherche web envoie la question à Tavily avec search_depth réglé sur advanced, puis extrait le champ content de chaque résultat :
def search_with_tavily(state: AgentState) -> AgentState:
question = state['question']
""" Using the Tavily to search the web and
fetch the latest information about user query
"""
response = tavilyClient.search(
query=question,
search_depth='advanced'
)
contents = [result["content"] for result in response["results"]]
return {**state, "tavilyResponse":contents}
Ces extraits deviennent le contexte que Gemini lira lors de la rédaction de la réponse. Notez que le nœud renvoie une liste de chaînes de caractères ; si vous préférez un seul bloc de texte, concaténez les éléments avant de les stocker afin que le champ corresponde au type str déclaré dans l’état.
Rendre le routeur fiable grâce à une sortie structurée
Un routeur naïf demande au modèle de répondre par l’un de trois mots simples :
Return only:
faiss
tavily
gemini
Les modèles de langage ne respectent pas toujours les instructions de formatage. Vous pourriez obtenir une phrase complète en retour :
I would choose tavily.
Ou une formulation légèrement différente :
The best option is: tavily
Chacune de ces réponses perturbe un graphe qui nécessite une chaîne de caractères exacte pour choisir une arête. Une sortie structurée comble cette lacune. Décrivez d’abord les décisions autorisées sous forme de modèle Pydantic dont le seul champ est restreint à trois valeurs littérales :
class RouteDecision(BaseModel):
source: Literal["faiss", "tavily", "gemini"]
Ensuite, dérivez un modèle de routage qui doit retourner une instance de ce schéma. La méthode json_schema demande au fournisseur de limiter la génération au schéma plutôt que de se fier uniquement à la formulation de l’instruction :
router_model = model.with_structured_output(
RouteDecision,
method="json_schema",
)
La sortie est validée par rapport au type Literal, de sorte qu’une valeur invalide apparaît comme une erreur au lieu de diriger le graphe vers une destination non définie.
Rédaction du nœud de décision
L’invite de routage décrit chaque option : faiss pour les questions relatives à la politique de l’entreprise et aux autres connaissances internes, tavily pour tout ce qui nécessite des informations actuelles ou en ligne, et gemini pour les connaissances générales qui n’exigent ni l’un ni l’autre :
def decide_source(state:AgentState)-> AgentState:
question = state["question"]
prompt = f"""
Decide the best source for answering this question.
Choose exactly one:
faiss:
Use when the question can be answered using our internal
knowledge base.related to company policy and all
tavily:
Use when the question requires current, recent, or web-based
information.
gemini:
Use when the question is general knowledge and does not
require our internal documents or current web information.
Question:
{question}
Return only one word:
faiss, tavily, or gemini
"""
response = model.invoke(prompt)
# return response
return {**state,"source":response.text}
Regardez attentivement les dernières lignes. Telles quelles, le nœud appelle encore directement model et stocke response.text, ce qui correspond exactement à l’approche fragile basée sur du texte libre décrite ci-dessus. Pour bénéficier du schéma, appelez plutôt router_model.invoke(prompt) et stockez l’attribut source du résultat. Avec ce changement, le routage renvoie un objet typé comme celui-ci plutôt que du texte arbitraire :
RouteDecision(source="faiss")
Indiquer à LangGraph où aller ensuite
Les arêtes conditionnelles nécessitent une fonction qui indique quel branch suivre. Celle-ci ne prend aucune décision par elle-même ; elle lit le choix déjà enregistré dans l’état par le routeur et le renvoie à LangGraph :
def route_source(
state: AgentState,
) -> Literal["faiss", "tavily", "gemini"]:
return state["source"]
Un nœud de réponse pour chaque itinéraire
Tous les trois branches aboutissent à l’étape de génération identique. Elle examine source et crée en conséquence une requête : des documents récupérés combinés en contexte pour FAISS, des extraits de recherche comme matériel de référence pour Tavily, ou la question brute pour des réponses directes :
# Generate the answer for the user
def generateAnswer(state:AgentState) -> AgentState:
source = state["source"]
question = state['question']
documents = state['documents']
tavilyResponse = state['tavilyResponse']
if source == "faiss":
context = "\n\n".join([doc.page_content for doc in documents])
prompt = f"""Based on the following context answer the question below
Context:
{context}
Question:
{question}
"""
elif source == "tavily":
prompt = f""" Based on the following search result , use this as an reference and provdie the
answer to the below question
Context:
{tavilyResponse}
Question:
{question}
"""
else:
prompt = f" Answer the following question : {question}"
response = model.invoke(prompt)
answer = response.content
return {**state, "answer":answer}
Le même modèle Gemini génère chaque réponse. La seule chose qui varie est le contexte placé devant lui, ce qui constitue l’idée fondamentale de RAG : des entrées de meilleure qualité, et non un modèle différent, produisent des réponses fondées.
Assurer la cohérence des noms lors de l’assemblage des extraits
Ces extraits combinent deux styles de nommage, et les incohérences provoqueront des erreurs si vous les collez ensemble sans modification. L’état déclare tavily_response tandis que les nœuds lisent et écrivent tavilyResponse ; le client est créé sous le nom de tavily_client mais est appelé tavilyClient ; et la fonction de réponse est définie comme generateAnswer mais enregistrée sous le nom de generate_answer. Choisissez une convention et appliquez-la partout avant d’exécuter le graphe.
Montage du graphe
À ce stade, les éléments consistent en un nœud de décision ainsi que trois possibilités de continuation :
decide_source
|
+----> faiss
|
+----> tavily
|
+----> gemini
Il y a une subtilité ici. Le chemin gemini n’est pas du tout une étape de récupération ; il signifie « sauter la récupération et répondre directement ». Ainsi, au lieu de créer un nœud vide pour ce cas, cette branche peut pointer directement vers le nœud de génération partagé.
Commencez par créer un graphe représentant le type d’état :
workflow = StateGraph(AgentState)
Enregistrez les quatre nœuds :
workflow.add_node("decide", decide_source)
workflow.add_node("faiss", retrieve_from_faiss)
workflow.add_node("tavily", search_with_tavily)
workflow.add_node("generate", generate_answer)
Faites du nœud de décision le point d’entrée :
workflow.add_edge(START, "decide")
Connectez les arêtes conditionnelles. La mise en correspondance transforme chaque valeur que la fonction de routage peut retourner en un nom de nœud, c’est là que gemini est envoyé directement vers generate :
workflow.add_conditional_edges(
"decide",
route_source,
{
"faiss": "faiss",
"tavily": "tavily",
"gemini": "generate",
},
)
Les branches de récupération et de recherche doivent ensuite conduire à la génération de la réponse :
workflow.add_edge("faiss", "generate")
workflow.add_edge("tavily", "generate")
La génération est l’étape finale avant la fin du graphe :
workflow.add_edge("generate", END)
La compilation transforme cette définition en une application exécutable :
app = workflow.compile()
Le graphe final
Le flux complet, du début à la fin :
START
|
v
+--------------+
| decide |
| source |
+--------------+
/ | \
/ | \
v v v
+------+ +--------+ +---------+
|FAISS | | Tavily | | Generate|
| | | | | directly|
+------+ +--------+ +---------+
\ | /
\ | /
v v v
+----------------+
| generate |
| answer |
+----------------+
|
v
END
Gardez à l’esprit le principe fondamental : le graphe définit l’ensemble des chemins possibles, et le modèle en choisit un à l’exécution. C’est cette combinaison qui rend le flux de travail agent sans en faire quelque chose d’imprévisible.
Essayer les trois approches
def ask_question(question: str):
initial_state = {
"question":question,
"documents":[],
"tavilyResponse":"",
"source":""
}
result = app.invoke(initial_state)
return result
Une question qui nécessite le web
La question météorologique doit être envoyée à Tavily :
result = ask_question(
"What is the current weather in Uttarakhand?"
)
Affichez à la fois la source choisie et la réponse générée :
print("Source:", result["source"])
print("Answer:", result["answer"])
Le chemin attendu :
Source: tavily
Les conditions actuelles n’existent que sur le web.
Une question de connaissance générale
Ensuite, posez une question sur la génération augmentée par récupération d’informations :
result = ask_question(
"What is Retrieval Augmented Generation?"
)
Affichez le résultat de la même manière :
print("Source:", result["source"])
print("Answer:", result["answer"])
Le routeur devrait omettre complètement la récupération d’informations :
Source: gemini
Le modèle peut expliquer le concept par lui-même.
Une question concernant la politique interne
Finalement, la question relative à la politique de congés :
result = ask_question(
"What is our company leave policy?"
)
Et les mêmes instructions print :
print("Source:", result["source"])
print("Answer:", result["answer"])
Cette fois, la base de connaissances interne devrait l’emporter :
Source: faiss
Le routage des LLM est probabiliste, il faut donc considérer ces résultats comme attendus et non comme des garanties.
Pourquoi le routage sémantique bat les règles basées sur des mots-clés
Voici à nouveau l’alternative codée en dur :
if "weather" in question:
use_tavily()
elif "leave" in question:
use_faiss()
else:
use_gemini()
De telles règles s’usent rapidement. Prenons l’exemple d’un utilisateur qui demande si le bureau est ouvert le samedi. Rien dans cette phrase ne mentionne de politique, pourtant la réponse se trouve très probablement dans la documentation interne. Un routeur basé sur un modèle peut déduire l’intention ; une liste de mots-clés ne le peut pas, à moins que quelqu’un n’anticipe toutes les formulations possibles.
Cette approche s’échelle également mieux. Lorsque de nouveaux backends apparaissent, comme ceux ci-dessous, on étend le schéma et la instruction plutôt que de créer un enchevêtrement de conditions :
SQL Database
Internal API
CRM
Customer Support System
Documentation
Web Search
Le compromis consiste en une appel supplémentaire au modèle, avec ses coûts et sa latence, avant même que le travail réel ne commence.
S’agit-il vraiment d’un agent IA ?
Il est plus exact de le qualifier de petit flux de travail agent que d’agent autonome. Les actions disponibles sont définies à l’avance :
FAISS
Tavily
Direct Gemini
Le modèle ne peut pas décider de son propre chef d’effectuer quelque chose comme suit, car ces capacités ne lui ont jamais été conférées :
delete a database
send an email
call an arbitrary API
L’architecture ressemble à une chaîne de responsabilités :
Developer defines possible actions
|
v
LLM chooses action
|
v
LangGraph executes
|
v
Result
Cette contrainte est en fait un avantage : des choix limités permettent de garder le comportement prévisible et auditable en production.
Le rôle de chaque composant
LangGraph : le moteur de flux de travail
LangGraph contrôle la structure de l’application : l’état, les nœuds, les arêtes, le routage conditionnel ainsi que l’ordre d’exécution. En théorie, chaque étape suit le même schéma :
State
|
v
Node
|
v
Updated State
|
v
Conditional Edge
|
+----> Node A
|
+----> Node B
|
+----> Node C
Un graphe composé de petites étapes est plus facile à tester et à observer qu’une fonction complexe et étendue.
FAISS : la couche de récupération
FAISS gère la partie RAG du système. De manière simplifiée :
Company Documents
|
v
Embeddings
|
v
FAISS
|
v
Similar Documents
|
v
Gemini
|
v
Answer
Une mise en production réelle ajoute un pipeline d’ingestion devant ce système :
Documents
|
v
Load
|
v
Split into chunks
|
v
Generate embeddings
|
v
Store vectors
|
v
Retrieve relevant chunks
|
v
Generate answer
L’exemple omet le chargement et la segmentation des données pour se concentrer sur le flux de travail ; les guides réels nécessitent ces deux étapes.
Tavily : recherche web en temps réel
Tavily traite les informations qui évoluent au fil du temps :
User Question
|
v
Router
|
v
Tavily
|
v
Search Results
|
v
Gemini
|
v
Answer
Les cas typiques incluent la météo en temps réel, les actualités urgentes, les nouvelles sorties de produits, la documentation à jour, les événements récents ainsi que des données de marché en direct. En environnement de production, il faut également déterminer comment les résultats de recherche seront cités, filtrés, validés et présentés, car le contenu web n’est ni garanti précis ni sûr à transmettre à un modèle sans vérification.
Le schéma à retenir
Les composants sont interchangeables. L’idée fondamentale réside dans le routage : associer chaque question à la capacité la plus adaptée pour y répondre.
Internal Knowledge
|
+------ FAISS
Current Information
|
+------ Tavily
General Knowledge
|
+------ Gemini
Ce choix de capacité par requête réapparaît dans presque toutes les applications agentes sérieuses.
Où aller ensuite
Ajouter plus d’outils
Le routeur pourrait choisir parmi bien d’autres backends :
SQL Database
REST APIs
CRM
Email
Calendar
Internal Documentation
Améliorer la récupération
Les trois documents présentés ne constituent qu’une démonstration, et non une base de connaissances. Un véritable système RAG nécessite des chargeurs de documents, un découpage en blocs, des métadonnées, de meilleures stratégies de recherche, un réclassement des résultats, des citations des sources ainsi qu’un contrôle d’accès, afin que les utilisateurs ne récupèrent que ce à quoi ils ont droit.
Vérifier les décisions de routage
Une étape de validation entre le routeur et les outils peut rejeter des choix peu plausibles et permettre un retour en arrière sécurisé :
Router
|
v
Validator
|
+---- valid ----> Tool
|
+---- invalid --> Fallback
Cela devient d’autant plus important à mesure que le nombre d’outils augmente.
Gérer les échecs
Une sortie structurée garantit un itinéraire valide, mais pas nécessairement une appel à outil réussi. Une API de recherche peut expirer ou ne rien retourner :
Router
|
v
Tavily
|
X
Search failed
|
v
Fallback
Les systèmes de production nécessitent des tentatives répétées ainsi qu’un mécanisme de secours, comme une réponse directe accompagnée d’une mise en garde claire. L’article sur la conception de graphes d’agents résilients avec tentatives répétées et mécanismes de secours aborde ce sujet plus en détail.
Rendre cela observable
Lorsqu’une réponse est incorrecte, il faut savoir à quel stade l’opération a échoué :
Wrong route?
|
v
Bad retrieval?
|
v
Bad search results?
|
v
Bad generation?
En enregistrant la source sélectionnée ainsi que les résultats intermédiaires, il devient possible de répondre à cette question.
Introduire des boucles
Le graphique actuel ne prend qu’une seule décision :
Question
|
v
Router
|
v
Tool
|
v
Answer
Un agent plus performant évalue ce qu’il a trouvé et décide s’il doit agir à nouveau :
Question
|
v
Reason
|
v
Tool
|
v
Evaluate Result
|
+---- Need more information?
| |
| v
| Tool
| |
+------------+
|
v
Final Answer
C’est ici que les systèmes agents acquièrent une véritable puissance : le modèle détermine si les informations disponibles sont suffisantes ou s’une autre action est nécessaire. C’est également ici qu’il faut impérativement des limites d’itération, afin qu’une boucle ne puisse pas s’exécuter indéfiniment.
Points clés
Tout le système répond à une seule question : comment une application d’IA doit-elle décider de l’origine d’une réponse ? Le flux de travail final se présente comme suit :
User Question
|
v
Gemini
Router
|
+----------+----------+
| | |
v v v
FAISS Tavily Gemini
Internal DB Web Direct
| | |
+----------+----------+
|
v
Gemini
Final Answer
- Définissez les capacités et les limites dans le graphe ; laissez le modèle choisir parmi elles en temps de exécution.
- Utilisez des sorties structurées pour les décisions de routage, et assurez-vous que le nœud de décision appelle réellement le modèle structuré.
- Gardez un seul nœud de génération de réponse et modifiez uniquement le contexte que vous lui fournissez.
- Considérez le routage comme un composant testable : enregistrez les décisions et vérifiez-les à l’aide de questions représentatives.
À partir de là, cette même base s’étend naturellement aux bases de données SQL, aux API, à la mémoire, à l’approbation humaine, aux nœuds d’évaluation, aux tentatives de réessai et aux configurations multi-agents. Le graphe se développe, mais le principe reste inchangé : donnez au modèle des capacités utiles, définez comment elles peuvent être utilisées, et laissez-le choisir celle qui convient à la tâche.
Lectures complémentaires
- Intégrer un agent LangChain dans FastAPI : Outils, recherche manuelle, streaming — Créez un assistant intégré à l’application avec FastAPI et LangChain : un manuel PDF stocké dans ChromaDB exposé en tant qu’outil, un contexte par utilisateur, une historique sauvegardée et des réponses en streaming.
- Concevoir une mémoire agente en quatre niveaux avec LangGraph et Amazon Bedrock — Apprenez à fournir aux agents LLM une mémoire épisodique, sémantique et procédurale fonctionnelle sur Bedrock et LangGraph, ainsi qu’à les protéger contre le poisonage, les fuites de PII et les interférences entre utilisateurs.