Salut la famille tech ! Nina Torres de retour sur agntbox.com. Aujourd’hui, nous allons explorer quelque chose qui fait des vagues dans la communauté AI, surtout pour ceux d’entre nous qui s’essayent à créer des expériences AI plus personnalisées et conscientes du contexte. Je parle de LlamaIndex, et plus précisément, de la façon dont son moteur de requêtes a évolué pour gérer des interactions de données de plus en plus complexes. Oubliez l’idée de simplement lancer un PDF et espérer le meilleur ; nous allons voir comment le faire vraiment réfléchir à travers plusieurs sources de données différentes.
Ma boîte de réception, comme beaucoup d’entre vous, a été inondée de questions sur comment aller au-delà des configurations RAG de base (Retrieval-Augmented Generation). Les gens construisent des applications incroyables, mais ils se heurtent à un mur lorsque leur AI doit répondre à des questions nécessitant de synthétiser des informations provenant d’une base de données, d’un ensemble de documents, et d’une API en direct. Ce n’est plus un simple problème de « trouver ce texte ». C’est un problème de « découvrir quoi demander où, puis combiner les réponses ». Et honnêtement, c’est un problème avec lequel je me bats sur un projet personnel pour un client qui gère un site de e-commerce de niche – imaginez un assistant AI qui doit vérifier l’inventaire des produits (base de données), consulter les avis des utilisateurs (documents) et suggérer des articles connexes en fonction des prix en temps réel (API). Ma configuration initiale de LlamaIndex, bien que convenable pour la partie avis, a complètement échoué sur la synthèse multi-sources.
Aujourd’hui, nous allons donc explorer les capacités avancées du moteur de requêtes de LlamaIndex pour la synthèse de données multi-sources. Il ne s’agit pas seulement d’indexer différents types de données ; il s’agit de construire un moteur de requêtes qui orchestre intelligemment la récupération d’informations à travers ces sources diverses pour répondre à des questions complexes et multifactorielles. Nous parlons de passer d’un simple récupérateur à un système sophistiqué de routage et de planification.
L’Évolution de « Poser une Question » dans LlamaIndex
Vous vous souvenez de la première apparition de LlamaIndex ? C’était génial pour transformer des données non structurées en quelque chose avec lequel un LLM pouvait discuter. Vous chargiez des documents, construisiez un index et posiez des questions. Simple et efficace. Mais la vie n’est pas toujours simple. Nos données ne sont pas toujours dans des fichiers texte bien rangés. Elles sont éparpillées à travers des bases de données SQL, des magasins noSQL, des APIs et une multitude de PDFs.
L’approche initiale consistait souvent à créer des index séparés pour chaque source de données. Vous aviez votre index de documents, votre index SQL, peut-être un outil API. Ensuite, vous décidiez manuellement laquelle interroger en fonction des entrées de l’utilisateur. Cela fonctionne pour des cas simples, mais qu’en est-il d’une question comme : « Quelles sont les évaluations moyennes des produits lancés au cours du dernier trimestre, et y en a-t-il qui ont des tickets de support ouverts ? »
Cette question nécessite :
- Interroger une base de données pour les dates de lancement de produits et les évaluations moyennes.
- Interroger un autre système (peut-être un magasin de documents séparé ou une API) pour les tickets de support ouverts liés à ces produits.
- Synthétiser les deux morceaux d’information pour fournir une réponse cohérente.
C’est là que LlamaIndex a vraiment amélioré son offre, passant de récupérateurs isolés à des moteurs de requête intégrés capables de comprendre et d’agir sur de telles requêtes multifactorielles.
Au-delà de la Récupération Basique : Planification et Routage de Requêtes
La magie opère avec ce que LlamaIndex appelle « planification de requêtes » et « routage ». Au lieu de se contenter de récupérer des morceaux de texte, le moteur de requêtes, souvent alimenté par un LLM lui-même, tente d’abord de comprendre l’intention de l’utilisateur puis détermine la meilleure stratégie pour y répondre. Cela implique :
- Identifier les Sous-Questions : Décomposer une question complexe en questions plus petites et indépendantes.
- Faire Correspondre les Sous-Questions aux Outils/Index : Déterminer quelle source de données spécifique (par exemple, une base de données SQL, un index de documents vectoriels, un point de terminaison API) est le mieux adapté pour répondre à chaque sous-question.
- Exécuter les Requêtes : Lancer ces sous-requêtes contre les outils choisis.
- Synthétiser les Résultats : Prendre les réponses individuelles et les combiner en une réponse unique et complète.
Cela n’est pas qu’un concept théorique ; cela est pratiquement mis en œuvre à travers des éléments tels que le `QueryPipeline` de LlamaIndex, le `RouterQueryEngine`, et la capacité de définir des `Tools` personnalisés.
Configuration pour la Synthèse Multi-Sources : Un Exemple Pratique
Passons en revue une version simplifiée du problème de mon client e-commerce. Imaginez que nous avons trois sources de données :
- Base de Données des Produits : Une base de données SQL avec des identifiants de produits, des noms, des prix et des dates de lancement.
- Documents d’Avis : Une collection d’avis d’utilisateurs (PDFs, fichiers texte) pour chaque produit.
- API d’Inventaire : Une simple API qui renvoie les niveaux de stock actuels pour un identifiant de produit donné.
Notre objectif est de répondre à une question comme : « Parlez-moi de ‘Fancy Widget Pro’ – quel est son prix, que disent les utilisateurs à son sujet, et est-il actuellement en stock ? »
Étape 1 : Préparer Vos Sources de Données et Outils
Tout d’abord, nous devons rendre chaque source de données accessible à LlamaIndex en tant que « outil ».
A. Outil de Base de Données SQL
Nous allons utiliser le `SQLTableRetrieverTool` de LlamaIndex pour cela. Supposons que vous ayez une base de données SQLite simple nommée `products.db` avec une table `products`.
from llama_index.core import SQLDatabase
from sqlalchemy import create_engine, text
from llama_index.core.tools import SQLTableRetrieverTool
# Créer une base de données et une table fictives pour la démonstration
engine = create_engine("sqlite:///products.db")
with engine.connect() as connection:
connection.execute(text("""
CREATE TABLE IF NOT EXISTS products (
product_id TEXT PRIMARY KEY,
name TEXT,
price REAL,
launch_date TEXT
);
"""))
connection.execute(text("""
INSERT OR IGNORE INTO products (product_id, name, price, launch_date) VALUES
('FWP001', 'Fancy Widget Pro', 129.99, '2025-01-15'),
('MGS002', 'Mega Gadget Super', 249.00, '2024-11-01');
"""))
connection.commit()
sql_database = SQLDatabase(engine=engine)
sql_tool = SQLTableRetrieverTool.from_instances(
sql_database=sql_database,
table_names=["products"],
description=(
"Utile pour interroger des informations sur les produits telles que le nom, le prix et la date de lancement. "
"L'entrée doit être une requête SQL pour la table 'products'."
)
)
Les Avis de Nina : Ce `SQLTableRetrieverTool` est un sauveur. Avant lui, j’écrivais des fonctions personnalisées pour interagir avec les bases de données, et j’avais l’impression de réinventer la roue à chaque fois. Cet outil rend les choses beaucoup plus simples, même si vous devez toujours faire attention à l’ingénierie des invites pour vous assurer que le LLM génère de bonnes requêtes SQL.
B. Outil d’Index de Documents d’Avis
Pour les avis, nous allons créer un index vectoriel à partir de quelques documents d’avis fictifs.
import os
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.tools import QueryEngineTool
# Créer un répertoire et des fichiers d'avis fictifs
os.makedirs("reviews", exist_ok=True)
with open("reviews/FWP001_reviews.txt", "w") as f:
f.write("Avis sur Fancy Widget Pro :\n")
f.write("1. 'J'adore absolument le Fancy Widget Pro ! Si rapide et fiable.' - Utilisateur A\n")
f.write("2. 'Un peu cher, mais ça vaut le coup pour la qualité.' - Utilisateur B\n")
f.write("3. 'Améliore considérablement mon flux de travail.' - Utilisateur C\n")
with open("reviews/MGS002_reviews.txt", "w") as f:
f.write("Avis sur Mega Gadget Super :\n")
f.write("1. 'La durée de vie de la batterie est incroyable !' - Utilisateur X\n")
f.write("2. 'Processus de configuration déroutant.' - Utilisateur Y\n")
# Charger les documents et créer un index
documents = SimpleDirectoryReader("reviews").load_data()
review_index = VectorStoreIndex.from_documents(documents)
review_query_engine = review_index.as_query_engine()
review_tool = QueryEngineTool(
query_engine=review_query_engine,
metadata={"name": "review_tool",
"description": "Utile pour répondre aux questions sur les avis des produits à partir des documents de retours des utilisateurs. "
"L'entrée doit être un nom ou un identifiant de produit spécifique."}
)
Les Avis de Nina : Le `QueryEngineTool` est votre outil de choix pour envelopper n’importe quel moteur de requête de LlamaIndex existant dans un outil. C’est incroyablement flexible. Assurez-vous simplement que votre `description` est super claire pour que le LLM sache quand l’utiliser.
C. Outil d’API d’Inventaire
Pour l’API, nous allons simuler une simple fonction Python en tant qu’appel API utilisant `FunctionTool`.
from llama_index.core.tools import FunctionTool
# Simuler une API d'inventaire
def get_stock_level(product_id: str) -> str:
"""
Renvoie le niveau de stock actuel pour un identifiant de produit donné.
Args:
product_id (str) : L'identifiant du produit.
Returns:
str : Une chaîne indiquant le niveau de stock, par exemple, "En Stock", "Stock Faible", "Rupture de Stock".
"""
if product_id == "FWP001":
return "En Stock"
elif product_id == "MGS002":
return "Stock Faible"
else:
return "Rupture de Stock"
inventory_tool = FunctionTool.from_defaults(fn=get_stock_level,
description=(
"Utile pour vérifier le niveau de stock actuel d'un produit. "
"L'entrée doit être un identifiant de produit (par exemple, 'FWP001')."
))
Les Avis de Nina : `FunctionTool` est une pure génie. Vous pouvez envelopper presque n’importe quelle fonction Python et l’exposer à votre LLM. C’est ainsi que vous vous connectez à de réelles APIs, des services internes, ou même exécutez des scripts locaux. C’est un changement significatif pour intégrer des actions externes dans les capacités de votre AI.
Étape 2 : Construire le Moteur de Requêtes Router
Maintenant que nous avons nos outils individuels, nous avons besoin d’une méthode pour que LlamaIndex les choisisse et les utilise intelligemment. C’est là que le `RouterQueryEngine` entre en jeu. Il utilise un LLM pour décider quel outil (ou séquence d’outils) utiliser en fonction de la requête de l’utilisateur.
from llama_index.core.query_engine import RouterQueryEngine
from llama_index.core.selectors import LLMSingleSelector
from llama_index.llms.openai import OpenAI # En supposant que vous avez configuré une clé API OpenAI
# Initialiser LLM (par exemple, OpenAI) pour le routage et la synthèse
llm = OpenAI(model="gpt-3.5-turbo") # Ou gpt-4, selon vos besoins et budget
# Combiner tous les outils
all_tools = [sql_tool, review_tool, inventory_tool]
# Créer le RouterQueryEngine
router_query_engine = RouterQueryEngine(
selector=LLMSingleSelector.from_defaults(llm=llm),
query_engine_tools=all_tools,
verbose=True # Défini sur True pour voir les décisions de routage
)
Point de vue de Nina : Le `LLMSingleSelector` est la méthode par défaut, souvent suffisante, pour indiquer au routeur quel outil utiliser. Pour des scénarios plus complexes, LlamaIndex offre d’autres sélecteurs ou vous pouvez même en créer un sur mesure. Le `verbose=True` est absolument essentiel pendant le développement – il vous montre ce que le LLM est « en train de penser » lorsqu’il essaie de router votre requête, ce qui est précieux pour le débogage.
Étape 3 : Interroger le moteur multi-sources
Posons notre question complexe :
response = router_query_engine.query("Parlez-moi de 'Fancy Widget Pro' – quel est son prix, que disent les utilisateurs à son sujet, et est-il actuellement en stock ?")
print(response)
Lorsque vous exécutez cela, vous verrez la sortie `verbose` montrant le processus de réflexion du LLM :
- Il identifiera que « prix » nécessite le `sql_tool`.
- « Que disent les utilisateurs » nécessite le `review_tool`.
- « Actuellement en stock » nécessite le `inventory_tool`.
Le LLM exécutera ensuite chacun de ces outils, obtiendra leurs réponses respectives, et enfin les synthétisera en une seule réponse cohérente. C’est comme avoir un petit chef d’orchestre pour vos données !
Une sortie typique pourrait ressembler à ceci :
> Sélection de l'outil de moteur de requête : sql_tool
> Sélection de l'outil de moteur de requête : review_tool
> Sélection de l'outil de moteur de requête : inventory_tool
Le Fancy Widget Pro (ID produit FWP001) coûte 129,99 $. Les utilisateurs l'adorent généralement, le décrivant comme "rapide et fiable" et affirmant qu'il a "considérablement amélioré leur flux de travail", bien que certains mentionnent qu'il est "un peu cher". Il est actuellement en stock.
C’est une amélioration considérable par rapport à l’interrogation manuelle de chaque source. Le LLM gère l’orchestration, rendant l’interaction beaucoup plus naturelle et puissante.
Scénarios avancés : Pipelines de requête et routage récursif
Le `RouterQueryEngine` est fantastique pour choisir un outil unique pour répondre à une sous-question. Mais que se passe-t-il si la sortie d’un outil doit être utilisée comme entrée pour un autre ? Ou si vous avez besoin d’une séquence spécifique d’opérations qui ne peut pas être gérée par une simple sélection d’outil unique ?
C’est là que le `QueryPipeline` devient incroyablement puissant. Il vous permet de chaîner ensemble plusieurs composants, y compris des outils, des récupérateurs, des LLM et même d’autres moteurs de requête, de manière à former un graphe acyclique dirigé (DAG).
Imaginez un scénario : « Trouvez tous les produits lancés au cours des 6 derniers mois qui ont un faible stock et des critiques positives. »
- Interroger la base de données SQL pour les produits lancés au cours des 6 derniers mois.
- Pour chaque ID de produit de l’étape 1, vérifier le niveau de stock via l’API. Filtrer pour « Faible Stock ».
- Pour les produits restants, interroger les documents d’évaluation pour le sentiment. Filtrer pour « critiques positives ».
- Synthétiser la liste finale.
C’est un processus conditionnel en plusieurs étapes. Vous pourriez créer un `QueryPipeline` pour cela, où la sortie d’une étape devient l’entrée de la suivante, éventuellement avec un LLM entre les deux pour traiter les résultats intermédiaires ou décider de la prochaine étape.
Bien que nous ne construisions pas d’exemple complet de `QueryPipeline` ici (c’est un article à part entière !), sachez qu’il offre la flexibilité pour une véritable automatisation de flux de travail complexe au sein de votre application LlamaIndex. Vous pouvez même intégrer un `RouterQueryEngine` au sein d’un `QueryPipeline` pour une prise de décision récursive.
Points à retenir pour votre prochain projet IA
- Cartographiez vos sources de données : Avant d’écrire une seule ligne de code, identifiez clairement toutes les sources de données avec lesquelles votre IA doit interagir. Comprenez leur structure (structurée, non structurée, pilotée par API).
- Définissez des outils clairs : Pour chaque source de données, créez un LlamaIndex `Tool` spécifique (par exemple, `SQLTableRetrieverTool`, `QueryEngineTool`, `FunctionTool`). Écrivez des `metadata` clairs et descriptifs pour chaque outil. Cette description est ce que le LLM utilise pour décider quand l’invoquer. Prenez le temps de faire cela ; c’est l’ingénierie des invites pour vos outils !
- Commencez par le `RouterQueryEngine` : Pour les projets multi-sources initiaux, le `RouterQueryEngine` est souvent le point d’entrée le plus simple. Il gère la prise de décision basée sur le LLM pour vous.
- Utilisez `verbose=True` largement : Vraiment, c’est votre meilleur ami. Cela vous donne un aperçu du raisonnement du LLM et vous aide à affiner vos descriptions d’outils lorsque le routeur prend des décisions inattendues.
- Considérez `QueryPipeline` pour les flux de travail : Si vos questions impliquent des étapes séquentielles, une logique conditionnelle, ou où la sortie d’un outil alimente directement un autre, commencez à penser à `QueryPipeline`. C’est plus complexe à mettre en place mais offre un contrôle sans précédent sur les interactions en plusieurs étapes.
- Itérez sur les descriptions des outils : La capacité du LLM à choisir le bon outil dépend énormément de la manière dont vous avez décrit le but de chaque outil et ses entrées/sorties attendues. N’ayez pas peur d’expérimenter avec différentes formulations.
La capacité de LlamaIndex à orchestrer intelligemment des requêtes à travers différentes sources de données représente un grand bond en avant pour la création d’agents véritablement intelligents. Cela nous éloigne d’une logique rigide et codée en dur pour nous rapprocher de systèmes capables de raisonner sur les besoins en information et de récupérer des données dynamiquement depuis les endroits les plus appropriés. L’assistant e-commerce de mon client est désormais beaucoup plus intelligent, capable d’extraire les prix de la base de données, le sentiment des utilisateurs à partir des critiques et les niveaux de stock via l’API, le tout à partir d’une seule requête en langage naturel. C’est vraiment quelque chose à voir !
C’est tout pour aujourd’hui, les amis ! Allez-y et construisez des agents IA incroyablement intelligents et multi-sources. Faites-moi savoir ce que vous construisez dans les commentaires !
Articles connexes
- **TITRE : Les outils CLI que j’adore et pourquoi vous devriez les aimer aussi**
- Comment intégrer les SDK d’IA
- Meilleures pratiques en développement d’agents IA
🕒 Published: