Exercices IA & Data Science Agent RAG complet avec LangGraph et FAISS
🎉

Bravo!

Avancé 🧠 Fondamentaux 50 XP 0 personnes ont réussi

Agent RAG complet avec LangGraph et FAISS

On va maintenant tout assembler pour construire un vrai agent RAG avec LangGraph et FAISS. L'agent suit un workflow intelligent :

1. Il analyse la question pour déterminer si elle necessite une recherche
2. Si oui, il cherche dans l'index FAISS
3. Il evalue si les documents trouves sont pertinents (distance < seuil)
4. Si pertinents, il génère une réponse ; sinon, il repond qu'il n'a pas trouve

Écris une fonction build_full_rag_agent(documents, vectors, threshold=1.0) qui :

- prend une liste de textes, leurs vecteurs, et un seuil de distance
- construit un index FAISS avec ces données
- crée un graphe LangGraph avec le State suivant :
- query : str
- query_vector : list
- needs_search : bool
- documents : list
- is_relevant : bool
- answer : str

Noeuds :
- "analyser" : met needs_search a True si la query contient un "?" (question), False sinon
- "chercher" : cherche les 2 plus proches vecteurs dans FAISS, stocke les résultats dans documents (liste de dicts avec "text" et "distance")
- "evaluer" : met is_relevant a True si la plus petite distance est inferieure au threshold
- "repondre_avec_contexte" : met answer a "Réponse: " + les textes des documents joints par " | "
- "repondre_sans_contexte" : met answer a "Desole, je n'ai pas trouve de réponse pertinente."
- "repondre_direct" : met answer a "Ce n'est pas une question."

Routing :
- Apres "analyser" : si needs_search est True -> "chercher", sinon -> "repondre_direct"
- Apres "evaluer" : si is_relevant est True -> "repondre_avec_contexte", sinon -> "repondre_sans_contexte"

Enchainement : analyser -> [chercher -> evaluer -> [repondre_avec/sans]] ou [repondre_direct]

Renvoie le graphe compile.

Tests (1/1)

Tests
docs = ['Python est un langage', 'Django est un framework', 'FastAPI est rapide']
vecs = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
app = build_full_rag_agent(docs, vecs, threshold=1.0)

# Question avec résultat pertinent (vecteur proche)
r = app.invoke({
    'query': 'Qu\'est-ce que Python?',
    'query_vector': [0.9, 0.1, 0],
    'needs_search': False,
    'documents': [],
    'is_relevant': False,
    'answer': '',
})
assert r['needs_search'] == True, 'La query contient un ?, needs_search doit etre True'
assert 'Python est un langage' in r['answer'], f'La réponse doit mentionner Python, got: {r["answer"]}'
assert r['answer'].startswith('Réponse: '), 'La réponse doit commencer par Réponse:'

# Pas une question
r2 = app.invoke({
    'query': 'Bonjour',
    'query_vector': [0.5, 0.5, 0],
    'needs_search': False,
    'documents': [],
    'is_relevant': False,
    'answer': '',
})
assert r2['answer'] == "Ce n'est pas une question.", f'Got: {r2["answer"]}'

# Question mais seuil tres bas (pas pertinent)
app2 = build_full_rag_agent(docs, vecs, threshold=0.001)
r3 = app2.invoke({
    'query': 'C\'est quoi?',
    'query_vector': [0.5, 0.5, 0.5],
    'needs_search': False,
    'documents': [],
    'is_relevant': False,
    'answer': '',
})
assert 'pas trouve' in r3['answer'], f'Doit repondre pas trouve, got: {r3["answer"]}'

Indices (3 disponibles)

solution.py