Knowledge Graph Embeddings : Quand les Graphes et les Vecteurs Fusionnent pour un RAG Surpuissant
Guide complet sur les Knowledge Graph Embeddings pour le RAG : TransE, RotatE, ComplEx, intégration Neo4j + vecteurs, recherche hybride graphe/vecteur et benchmarks multi-hop QA.
TL;DR
Le RAG vectoriel classique excelle pour les questions simples, mais échoue sur les questions multi-hop qui nécessitent de relier plusieurs informations ("Quel est le CEO de l'entreprise qui a acquis la startup fondée par le frère de Elon Musk ?"). Les Knowledge Graph Embeddings combinent la puissance des graphes de connaissances (relations explicites) avec les embeddings vectoriels (similarité sémantique). Résultat : +35% de précision sur les questions multi-hop et une désambiguïsation d'entités quasi parfaite.
Pourquoi les vecteurs seuls ne suffisent pas
Les limites du RAG vectoriel
| Type de question | RAG vectoriel | RAG + Knowledge Graph | Différence |
|---|---|---|---|
| Factuelle simple | 89.2% | 90.1% | +0.9% |
| Comparaison | 78.5% | 85.3% | +6.8% |
| Multi-hop (2 sauts) | 62.1% | 83.7% | +21.6% |
| Multi-hop (3+ sauts) | 38.5% | 71.2% | +32.7% |
| Désambiguïsation | 71.3% | 94.8% | +23.5% |
| Temporelle | 65.8% | 82.1% | +16.3% |
| Agrégation | 45.2% | 78.5% | +33.3% |
Le problème illustré
Question : "Quels produits sont fabriqués dans le même pays
que le siège social du fournisseur de notre composant X ?"
RAG vectoriel :
→ Cherche "composant X fournisseur pays produits"
→ Trouve des documents sur le composant X, d'autres sur des produits
→ Ne fait PAS le lien entre les entités
→ Réponse incorrecte ou hallucination
RAG + Knowledge Graph :
→ composant_X --fournisseur_de--> Entreprise_Y
→ Entreprise_Y --siège_social--> Allemagne
→ Allemagne --fabrique--> [Produit_A, Produit_B, Produit_C]
→ Réponse précise avec traçabilité complète
Comprendre les Knowledge Graph Embeddings
Qu'est-ce qu'un Knowledge Graph ?
Un graphe de connaissances stocke l'information sous forme de triplets (sujet, prédicat, objet) :
(Ailog, est_une, Plateforme_RAG)
(Ailog, basée_à, Paris)
(Ailog, fondée_en, 2024)
(Ailog, supporte, Shopify)
(Shopify, est_un, CMS_Ecommerce)
(Paris, est_dans, France)
(France, membre_de, Union_Européenne)
Pourquoi des embeddings pour les graphes ?
Les graphes stockent des relations explicites, mais ne gèrent pas la similarité sémantique. Les Knowledge Graph Embeddings projettent les entités et relations dans un espace vectoriel pour :
- Prédire des relations manquantes (link prediction)
- Trouver des entités similaires (entity similarity)
- Combiner recherche sémantique et traversée de graphe
Les modèles de KG Embeddings
| Modèle | Principe | Score MRR | Complexité | Meilleur pour |
|---|---|---|---|---|
| TransE | h + r ≈ t (translation) | 0.463 | O(d) | Relations 1-to-1 |
| TransR | Projection dans espace relation | 0.512 | O(d×k) | Relations complexes |
| RotatE | h ∘ r ≈ t (rotation complexe) | 0.533 | O(d) | Relations symétriques/transitives |
| ComplEx | Espace complexe, Hermitian product | 0.551 | O(d) | Relations antisymétriques |
| DistMult | Bilinear diagonal | 0.430 | O(d) | Relations symétriques |
| ConvE | CNN sur embeddings | 0.491 | O(d×k) | Grands graphes |
| TuckER | Tucker decomposition | 0.558 | O(d×d×d) | Haute précision |
| NodePiece | Tokenization des nœuds | 0.525 | O(d) | Très grands graphes |
Comment fonctionne TransE (le plus intuitif)
Idée : si (Paris, capitale_de, France),
alors : embedding(Paris) + embedding(capitale_de) ≈ embedding(France)
capitale_de
Paris ─────────────────→ France
h + r ≈ t
Entraînement :
- Minimiser ||h + r - t|| pour les vrais triplets
- Maximiser ||h + r - t'|| pour les faux triplets (négatifs)
DEVELOPERpythonimport torch import torch.nn as nn class TransE(nn.Module): def __init__(self, num_entities, num_relations, dim=128): super().__init__() self.entity_embeddings = nn.Embedding(num_entities, dim) self.relation_embeddings = nn.Embedding(num_relations, dim) # Initialisation nn.init.xavier_uniform_(self.entity_embeddings.weight) nn.init.xavier_uniform_(self.relation_embeddings.weight) def forward(self, heads, relations, tails): h = self.entity_embeddings(heads) r = self.relation_embeddings(relations) t = self.entity_embeddings(tails) # Score = -||h + r - t|| score = -torch.norm(h + r - t, p=2, dim=-1) return score def predict_tail(self, head, relation, top_k=10): """Prédit les entités les plus probables.""" h = self.entity_embeddings(head) r = self.relation_embeddings(relation) # Score pour toutes les entités all_entities = self.entity_embeddings.weight scores = -torch.norm( h + r - all_entities, p=2, dim=-1 ) return torch.topk(scores, top_k)
Architecture hybride : Graph + Vecteurs
Le design pattern recommandé
┌──────────────────────────────────────────────────────┐
│ REQUÊTE UTILISATEUR │
│ "Quels produits recommander aux clients qui achètent │
│ des articles similaires au produit X ?" │
└───────────────────────┬──────────────────────────────┘
│
┌─────────┴─────────┐
│ QUERY ANALYSIS │
│ (Intent + Entités)│
└────┬──────────┬───┘
│ │
┌──────────┘ └──────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ GRAPH SEARCH │ │ VECTOR SEARCH │
│ │ │ │
│ Neo4j Cypher │ │ Qdrant/Pinecone│
│ - Traversée │ │ - Similarité │
│ - Relations │ │ - Sémantique │
│ - Multi-hop │ │ - Fuzzy match │
└───────┬───────┘ └───────┬───────┘
│ │
└──────────┬───────────────────┘
│
┌──────┴──────┐
│ FUSION │
│ (Reranking │
│ + Scoring) │
└──────┬──────┘
│
┌──────┴──────┐
│ GÉNÉRATION │
│ (LLM) │
└─────────────┘
Implémentation avec Neo4j + LangChain
DEVELOPERpythonfrom langchain_community.graphs import Neo4jGraph from langchain.chains import GraphCypherQAChain from langchain_openai import ChatOpenAI from langchain_community.vectorstores import Neo4jVector # Connexion au graphe Neo4j graph = Neo4jGraph( url="bolt://localhost:7687", username="neo4j", password="your-password" ) # Créer le schéma du graphe graph.query(""" CREATE CONSTRAINT IF NOT EXISTS FOR (p:Product) REQUIRE p.id IS UNIQUE """) # Peupler le graphe avec des relations graph.query(""" MERGE (p:Product {id: 'prod_001', name: 'Widget Pro'}) MERGE (c:Category {name: 'Electronics'}) MERGE (s:Supplier {name: 'TechCorp', country: 'Germany'}) MERGE (p)-[:BELONGS_TO]->(c) MERGE (p)-[:SUPPLIED_BY]->(s) MERGE (s)-[:LOCATED_IN]->(:Country {name: 'Germany'}) """) # Chaîne QA avec Cypher automatique llm = ChatOpenAI(model="gpt-4o", temperature=0) cypher_chain = GraphCypherQAChain.from_llm( llm=llm, graph=graph, verbose=True, validate_cypher=True, top_k=10, ) # Requête multi-hop result = cypher_chain.invoke({ "query": "Quels produits sont fournis par des entreprises " "situées en Allemagne ?" }) # → Cypher généré automatiquement : # MATCH (p:Product)-[:SUPPLIED_BY]->(s:Supplier) # -[:LOCATED_IN]->(c:Country {name: 'Germany'}) # RETURN p.name, s.name print(result["result"])
Recherche hybride Graph + Vecteur avec Neo4j
DEVELOPERpythonfrom langchain_openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings(model="text-embedding-3-small") # Créer un index vectoriel dans Neo4j vector_store = Neo4jVector.from_existing_graph( embedding=embeddings, url="bolt://localhost:7687", username="neo4j", password="your-password", node_label="Product", text_node_properties=["name", "description"], embedding_node_property="embedding", ) def hybrid_graph_vector_search(query: str, top_k: int = 5): """Combine recherche vectorielle et traversée de graphe.""" # 1. Recherche vectorielle : trouver les entités proches vector_results = vector_store.similarity_search_with_score( query, k=top_k ) # 2. Enrichir avec le graphe : relations et contexte enriched_results = [] for doc, score in vector_results: node_id = doc.metadata.get("id") # Récupérer le voisinage du nœud neighbors = graph.query(f""" MATCH (n {{id: '{node_id}'}})-[r]-(m) RETURN type(r) as relation, labels(m)[0] as type, m.name as name LIMIT 20 """) enriched_results.append({ "entity": doc.page_content, "score": score, "relations": neighbors, "context": format_graph_context(neighbors) }) return enriched_results def format_graph_context(neighbors: list) -> str: """Formate le contexte du graphe pour le LLM.""" context_parts = [] for n in neighbors: context_parts.append( f"- {n['relation']} → {n['type']}: {n['name']}" ) return "\n".join(context_parts)
Construction du Knowledge Graph
Extraction automatique d'entités et relations
DEVELOPERpythonimport anthropic client = anthropic.Anthropic() def extract_knowledge_graph(text: str) -> dict: """Extrait les entités et relations d'un texte.""" response = client.messages.create( model="claude-sonnet-4-6", max_tokens=2000, messages=[{ "role": "user", "content": f"""Analyse le texte suivant et extrait : 1. Les entités (personnes, organisations, produits, lieux, concepts) 2. Les relations entre entités Retourne un JSON avec : {{ "entities": [ {{"id": "e1", "name": "...", "type": "Organization|Person|Product|Location|Concept"}} ], "relations": [ {{"source": "e1", "target": "e2", "type": "...", "properties": {{}}}} ] }} Texte : {text}""" }] ) return json.loads(response.content[0].text) def build_graph_from_documents(documents: list): """Construit le graphe à partir d'une liste de documents.""" all_entities = {} all_relations = [] for doc in documents: kg = extract_knowledge_graph(doc["text"]) # Dédoublonner les entités for entity in kg["entities"]: key = f"{entity['type']}:{entity['name'].lower()}" if key not in all_entities: all_entities[key] = entity else: # Fusionner les propriétés all_entities[key]["properties"] = { **all_entities[key].get("properties", {}), **entity.get("properties", {}) } all_relations.extend(kg["relations"]) # Insérer dans Neo4j insert_into_neo4j( list(all_entities.values()), all_relations ) def insert_into_neo4j(entities: list, relations: list): """Insère les entités et relations dans Neo4j.""" for entity in entities: graph.query(f""" MERGE (n:{entity['type']} {{name: $name}}) SET n += $properties """, { "name": entity["name"], "properties": entity.get("properties", {}) }) for rel in relations: graph.query(f""" MATCH (a {{name: $source}}) MATCH (b {{name: $target}}) MERGE (a)-[r:{rel['type']}]->(b) SET r += $properties """, { "source": rel["source"], "target": rel["target"], "properties": rel.get("properties", {}) })
Benchmarks : KG-only vs Vector-only vs Hybride
Test sur HotpotQA (questions multi-hop)
| Approche | Exact Match | F1 Score | Latence | Coût/requête |
|---|---|---|---|---|
| Vector-only (RAG classique) | 42.1% | 55.3% | 120ms | $0.002 |
| KG-only (Cypher) | 51.8% | 63.7% | 85ms | $0.001 |
| Hybride (KG + Vecteur) | 61.5% | 73.2% | 180ms | $0.004 |
| Hybride + Reranking | 65.2% | 76.8% | 250ms | $0.008 |
Test sur des questions d'entreprise (corpus interne)
| Type de question | Vector | KG | Hybride |
|---|---|---|---|
| "Qui est responsable du projet X ?" | 85% | 95% | 96% |
| "Quels projets utilisent la techno Y ?" | 72% | 91% | 93% |
| "Quel est le budget total du département Z ?" | 45% | 82% | 85% |
| "Quelle relation entre personne A et projet B ?" | 38% | 88% | 91% |
| "Historique des décisions sur le sujet S ?" | 78% | 65% | 88% |
Quand utiliser chaque approche
| Critère | Vector-only | KG-only | Hybride |
|---|---|---|---|
| Questions simples | Excellent | Bon | Excellent |
| Questions multi-hop | Faible | Excellent | Excellent |
| Texte non structuré | Excellent | Faible | Bon |
| Données structurées | Faible | Excellent | Excellent |
| Mise à jour en temps réel | Facile | Complexe | Complexe |
| Coût de setup | Faible | Élevé | Élevé |
| Scalabilité | Très haute | Moyenne | Haute |
| Explicabilité | Faible | Excellente | Bonne |
Cas d'usage avancés
Désambiguïsation d'entités
DEVELOPERpythondef disambiguate_entity(mention: str, context: str) -> dict: """Désambiguïse une mention d'entité via le graphe.""" # Chercher les candidats dans le graphe candidates = graph.query(""" MATCH (n) WHERE n.name CONTAINS $mention OR n.aliases CONTAINS $mention RETURN n, labels(n) as types, [(n)-[r]-(m) | {rel: type(r), node: m.name}] as relations LIMIT 10 """, {"mention": mention}) if len(candidates) <= 1: return candidates[0] if candidates else None # Utiliser le contexte pour désambiguïser scores = [] for candidate in candidates: # Score basé sur la similarité du contexte # avec les relations du candidat context_embedding = embed(context) candidate_context = " ".join( [f"{r['rel']} {r['node']}" for r in candidate["relations"]] ) candidate_embedding = embed(candidate_context) score = cosine_similarity(context_embedding, candidate_embedding) scores.append((candidate, score)) # Retourner le meilleur candidat return max(scores, key=lambda x: x[1])[0]
Raisonnement multi-hop avec chaîne de pensée
DEVELOPERpythondef multi_hop_reasoning(question: str, max_hops: int = 3): """Raisonnement multi-hop sur le knowledge graph.""" # Étape 1 : Identifier les entités de départ entities = extract_entities_from_question(question) # Étape 2 : Explorer le graphe pas à pas reasoning_chain = [] current_entities = entities for hop in range(max_hops): # Récupérer le voisinage neighborhood = graph.query(""" MATCH (n)-[r]-(m) WHERE n.name IN $entities RETURN n.name as source, type(r) as relation, m.name as target, labels(m)[0] as type """, {"entities": current_entities}) if not neighborhood: break # Demander au LLM quelles relations suivre next_step = select_relevant_relations( question, neighborhood, reasoning_chain ) reasoning_chain.append(next_step) current_entities = [step["target"] for step in next_step] # Étape 3 : Générer la réponse avec la chaîne de raisonnement answer = generate_answer_with_chain(question, reasoning_chain) return answer
Outils et frameworks
| Outil | Type | Forces | Limitations |
|---|---|---|---|
| Neo4j | Graph DB | Maturité, écosystème, vector search natif | Coût en production |
| Amazon Neptune | Graph DB (cloud) | Serverless, intégration AWS | Lock-in AWS |
| Microsoft GraphRAG | Framework RAG | Summarization, community detection | Coût LLM élevé |
| LangChain GraphQA | Framework | Cypher auto, facile à intégrer | Cypher parfois incorrect |
| LlamaIndex KG | Framework | Property graph, multiple backends | Complexe |
| PyKEEN | KG Embeddings | 40+ modèles, recherche | Pas de production-ready |
| DGL-KE | KG Embeddings | Scalable, GPU | Learning curve |
FAQ
Knowledge Graph ou GraphRAG de Microsoft, quelle différence ?
Le Knowledge Graph est une base de données de faits structurés (entités + relations). GraphRAG de Microsoft est une approche spécifique qui construit automatiquement un graphe à partir de documents, crée des résumés hiérarchiques par communauté, et les utilise pour répondre aux questions. GraphRAG est plus adapté aux questions globales ("résumé du corpus"), tandis que le KG classique excelle pour les questions précises multi-hop. Voir notre guide sur GraphRAG.
Quel est le coût de construction d'un Knowledge Graph ?
La construction automatique avec LLM coûte environ $0.50-2 par document (extraction d'entités/relations). Pour 10 000 documents, comptez $5 000-20 000 pour la construction initiale. Le coût de Neo4j en production est de ~$65/mois pour une instance dédiée. L'alternative open-source est d'utiliser Neo4j Community Edition (gratuit) ou Amazon Neptune serverless.
Les KG Embeddings sont-ils nécessaires pour un RAG hybride ?
Non, pour un RAG hybride graph+vecteur simple, les requêtes Cypher suffisent. Les KG Embeddings deviennent utiles quand : 1) vous avez des millions de triplets et la traversée est lente, 2) vous voulez prédire des relations manquantes (link prediction), 3) vous combinez la similarité d'entités avec la recherche sémantique. Pour la plupart des cas d'usage, commencez sans KG Embeddings et ajoutez-les si nécessaire.
Comment maintenir un Knowledge Graph à jour ?
Trois approches : 1) Extraction incrémentale quand de nouveaux documents sont ajoutés, 2) Validation périodique des relations existantes, 3) Versioning du graphe pour tracer les changements. L'extraction incrémentale est la plus courante : chaque nouveau document passe par le pipeline d'extraction et les nouvelles entités/relations sont ajoutées au graphe existant.
Un Knowledge Graph est-il nécessaire pour mon cas d'usage ?
Un KG est recommandé si : vous avez des questions multi-hop fréquentes, votre domaine a des relations complexes entre entités (org charts, supply chains, réglementations), ou vous avez besoin d'explicabilité (traçabilité du raisonnement). Si vos questions sont principalement factuelles simples, le RAG vectoriel classique suffit. Voir notre guide sur les stratégies de retrieval pour choisir la bonne approche.
Les Knowledge Graph Embeddings représentent la prochaine frontière du RAG : au-delà de la simple similarité sémantique, vers un véritable raisonnement sur les relations entre entités. Testez Ailog pour voir comment notre approche hybride graph+vecteur répond à vos questions les plus complexes.
Tags
Articles connexes
GraphRAG : La Révolution qui Rend le RAG Traditionnel Obsolète
Découvrez GraphRAG de Microsoft : knowledge graphs + recherche vectorielle pour mieux répondre aux questions multi-hop et globales. Architecture, comparaison et implémentation complète.
Fondamentaux du Retrieval : Comment fonctionne la recherche RAG
Maîtrisez les bases du retrieval dans les systèmes RAG : embeddings, recherche vectorielle, chunking et indexation pour des résultats pertinents.
Recherche IA 2026 : Perplexity, Google AI et ChatGPT Search Tuent-ils le SEO Traditionnel ?
Analyse complète de la révolution de la recherche IA en 2026 : Perplexity, Google AI Overviews, ChatGPT Search. Impact sur le trafic organique, comparatif des moteurs IA, et opportunités pour les chatbots RAG.