Implémenter une RAG graphique à l’aide de graphes de connaissances

La génération augmentée de récupération graphiques (Graph RAG) apparaît comme une technique puissante qui permet aux applications d’IA générative d’utiliser des connaissances spécifiques à un domaine et des informations pertinentes. La RAG graphique est une alternative aux méthodes de recherche vectorielle qui utilisent une base de données vectorielle

Lesgraphes de connaissances sont des systèmes de connaissances dans lesquels les bases de données graphiques telles que Neo4j ou Amazon Neptune peuvent représenter des données structurées. Dans un graphe de connaissances, les relations entre les points de données, appelées edges, sont aussi significatives que les connexions entre les points de données, appelées vertices ou parfois nœuds. Un graphe de connaissances facilite la navigation dans un réseau et le traitement de requêtes complexes concernant des données connectées. Les graphes de connaissances sont particulièrement bien adaptés aux cas d’utilisation impliquant les chatbots, la résolution d’identité, l’analyse de réseau, les moteurs de recommandation, la vue client à 360° et la détection des fraudes.

Une approche Graph RAG tire parti de la nature structurée des bases de données de graphes pour donner une plus grande profondeur et un plus grand contexte aux informations récupérées sur les réseaux ou les relations complexes.  Lorsqu'une base de données orientée graphe est associée à un grand modèle de langage (LLM), un développeur peut automatiser une grande partie du processus de création de graphes à partir de données non structurées telles que du texte. Un LLM peut traiter des données textuelles et identifier des entités, comprendre leurs relations et les représenter dans une structure de graphe.

Il existe plusieurs méthodes pour créer une application Graph RAG, par exemple GraphRAG de Microsoft ou l'association de GPT4 avec LlamaIndex. Pour ce tutoriel, vous utiliserez Memgraphe, une solution de base de données graphique open source pour créer un système de RAG en utilisant Llama-3 de Meta sur watsonx. Memgraph utilise Cypher, un langage de requête déclaratif. Il partage certaines similitudes avec SQL, mais se concentre sur les nœuds et les relations plutôt que sur les tableaux et les lignes. Llama 3 créera et remplira votre base de données graphique à partir de texte non structuré et interrogera des informations dans la base de données.

Etape 1

Bien que vous puissiez faire votre choix parmi plusieurs outils, ce tutoriel vous guide pas à pas pour configurer un compte IBM à l’aide d’un Jupyter Notebook.

Connectez-vous à watsonx.ai™ en utilisant votre compte IBM Cloud.

Créez un projet watsonx.ai.

Vous pouvez obtenir l’ID de votre projet à partir de ce dernier. Cliquez sur l’onglet Gérer. Ensuite, copiez l’ID du projet depuis la section Détails de la page Géneral Vous avez besoin de cet ID de projet pour accéder à ce tutoriel.

Ensuite, associez votre projet à l'exécution watsonx.ai

a. Créez une instance de service d'exécution watsonx.ai (choisissez le plan Lite, qui est une instance gratuite).

b. Générez une clé API dans l'exécution watsonx.ai. Enregistrez cette clé API pour l’utiliser dans ce tutoriel.

c. Accédez à votre projet et sélectionnez l’onglet Gérer.

d.  Dans l’onglet de gauche, cliquez sur « Services et intégrations ».

e.  Sélectionnez les services IBM

f. Sélectionnez le service Associé, puis choisissez l’environnement d’exécution watsonx.ai.

g. Associez l'exécution watsonx.ai au projet que vous avez créé dans watsonx.ai

Etape 2

Vous devez maintenant installer Docker.

Une fois Docker installé, installez MemGraph en utilisant son conteneur Docker. Sur OSX ou Linux, vous pouvez utiliser cette commande dans un terminal :

curl https://install.memgraph.com | sh

Sur un ordinateur Windows, utilisez :

iwr https://windows.memgraph.com | iex

Suivez les étapes d'installation pour que le moteur MemGraph et le laboratoire MemGraph soient opérationnels.

Sur votre ordinateur, créez un nouvel environnement virtuel pour ce projet :

virtualenv kg_rag --python=python3.12

Dans l'environnement Python de votre bloc-notes, installez les bibliothèques Python suivantes :

./kg_rag/bin/pip install langchain langchain-openai langchain_experimental langchain-community==0.3.15 neo4j langchain_ibm jupyterlab json-repair getpass4

Vous êtes maintenant prêt à vous connecter à Memgraph.

Etape 3

Si vous avez configuré Memgraph pour utiliser un nom d'utilisateur et un mot de passe, veuillez les définir ici. Sinon, vous pouvez utiliser les paramètres par défaut qui ne nécessitent ni nom d'utilisateur ni mot de passe. Bien que cela ne soit pas recommandé pour une base de données de production, cela ne pose pas de problème pour un environnement de développement local qui ne stocke pas de données sensibles.

import os
from langchain_community.chains.graph_qa.memgraph import MemgraphQAChain
from langchain_community.graphs import MemgraphGraph

url = os.environ.get("MEMGRAPH_URI", "bolt://localhost:7687")
username = os.environ.get("MEMGRAPH_USERNAME", "")
password = os.environ.get("MEMGRAPH_PASSWORD", "")

#initialize memgraph connection
graph = MemgraphGraph(
    url=url, username=username, password=password, refresh_schema=True
)

Créez maintenant un exemple de chaîne qui décrit un jeu de données de relations que vous pouvez utiliser pour tester les capacités de génération de graphiques de votre système LLM. Vous pouvez utiliser des sources de données plus complexes, mais cet exemple simple nous aide à démontrer l’algorithme.

graph_text = “””
John’s title is Director of the Digital Marketing Group.
John works with Jane whose title is Chief Marketing Officer.
Jane works in the Executive Group.
Jane works with Sharon whose title is the Director of Client Outreach.
Sharon works in the Sales Group.
“””

Saisissez la clé API watsonx que vous avez créée lors de la première étape :

from getpass import getpass

watsonx_api_key = getpass()
os.environ[“WATSONX_APIKEY”] = watsonx_api_key
watsonx_project_id = getpass()
os.environ[“WATSONX_PROJECT_ID”] = watsonx_project_id

Configurez maintenant une instance WatsonxLLM pour générer du texte. La température doit être assez basse et le nombre de tokens élevé pour encourager le modèle à générer autant de détails que possible sans halluciner les entités ou les relations qui ne sont pas présentes.

from langchain_ibm import WatsonxLLM
from ibm_watsonx_ai.metanames import GenTextParamsMetaNames

graph_gen_parameters = {   
    GenTextParamsMetaNames.DECODING_METHOD: “sample”,
    GenTextParamsMetaNames.MAX_NEW_TOKENS: 1000,
    GenTextParamsMetaNames.MIN_NEW_TOKENS: 1,
    GenTextParamsMetaNames.TEMPERATURE: 0.3,
    GenTextParamsMetaNames.TOP_K: 10,
    GenTextParamsMetaNames.TOP_P: 0.8
}
watsonx_llm = WatsonxLLM(
    model_id=”meta-llama/llama-3-3-70b-instruct”,
    url=”https://us-south.ml.cloud.ibm.com”,
    project_id=os.getenv(“WATSONX_PROJECT_ID”),
    params=graph_gen_parameters,
)

Le LLMGraphTransformer vous permet de définir les types de nœuds et de relations que vous souhaitez que le LLM génère. Dans votre cas, le texte décrit les employés d'une entreprise, les groupes dans lesquels ils travaillent et leurs titres de poste. En limitant le LLM à ces entités, vous avez plus de chances d’obtenir une bonne représentation des connaissances dans un graphique.

L'appel convert_to_graphe_documents permet au LLMGraphTransformer de créer un graphe de connaissances à partir du texte. Cette étape génère la syntaxe Neo4j appropriée pour insérer les informations dans la base de données orientée graphe afin de représenter le contexte pertinent et les entités pertinentes.

from langchain_experimental.graph_transformers.llm import LLMGraphTransformer
from langchain_core.documents import Document

llm_transformer = LLMGraphTransformer(
    llm=watsonx_llm,
    allowed_nodes=[“Person”, “Title”, “Group”],
    allowed_relationships=[“TITLE”, “COLLABORATES”, “GROUP”]
)
documents = [Document(page_content=graph_text)]
graph_documents = llm_transformer.convert_to_graph_documents(documents)

Supprimez maintenant toutes les anciennes données de la base de données Memgraph et insérez les nouveaux nœuds et edges.

# make sure the database is empty
graph.query(“STORAGE MODE IN_MEMORY_ANALYTICAL”)
graph.query(“DROP GRAPH”)
graph.query(“STORAGE MODE IN_MEMORY_TRANSACTIONAL”)

# create knowledge graph
graph.add_graph_documents(graph_documents)

La syntaxe de Cypher générée est stockée dans les objets graph_documents. Vous pouvez l'examiner simplement en l'imprimant sous forme de chaîne.

print(f”{graph_documents}”)

Le schéma et les types de données créés par le cryptage sont visibles dans la propriété 'get_schema' des graphiques.

graph.refresh_schema()
print(graph.get_schema)

Cela affiche ce qui suit :

Node labels and properties (name and type) are:
- labels: (:Title)
properties:
- id: string
- labels: (:Group)
properties:
- id: string
- labels: (:Person)
properties:
- id: string

Nodes are connected with the following relationships:
(:Person)-[:COLLABORATES]->(:Person)
(:Person)-[:GROUP]->(:Group)
(:Person)-[:TITLE]->(:Title)

Vous pouvez également visualiser la structure du graphe dans la visionneuse Memgraph Labs :

 

Une image de réseau graphique montrant des nœuds et des edges Le réseau Memgraphe généré à partir du texte d’entrée

Le LLM a fait un travail raisonnable dans la création des nœuds et des relations corrects. Il est maintenant temps d’interroger le graphique de connaissances.

Étape 4

Pour utiliser correctement le LLM, il est nécessaire de procéder au prompt engineering. LangChain fournit un FewShotPromptTemplate qui peut être utilisé pour donner des exemples au LLM dans le prompt afin de s'assurer qu'il écrit une syntaxe Cypher correcte et succincte. Le code suivant donne plusieurs exemples de questions et de requêtes que le LLM doit utiliser. Il montre également comment limiter la sortie du modèle à la seule requête. Un LLM trop conversationnel peut ajouter des informations supplémentaires qui conduiraient à des requêtes Cypher non valides, de sorte que le prompt demande au modèle de générer uniquement la requête elle-même.

L'ajout d'un préfixe instructif contribue également à restreindre le comportement du modèle et augmente les chances que le LLM génère une syntaxe Cypher correcte.

from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate

examples = [
{
“question”: “<|begin_of_text|>What group is Charles in?<|eot_id|>“,
“query”: “<|begin_of_text|>MATCH (p:Person {{id: ‘Charles’}})-[:GROUP]->(g:Group) RETURN g.id<|eot_id|>“,
},
{
“question”: “<|begin_of_text|>Who does Paul work with?<|eot_id|>“,
“query”: “<|begin_of_text|>MATCH (a:Person {{id: ‘Paul’}})-[:COLLABORATES]->(p:Person) RETURN p.id<|eot_id|>“,
},
{
“question”: “What title does Rico have?<|eot_id|>“,
“query”: “<|begin_of_text|>MATCH (p:Person {{id: ‘Rico’}})-[:TITLE]->(t:Title) RETURN t.id<|eot_id|>“,
}
]

example_prompt = PromptTemplate.from_template(
“<|begin_of_text|>{query}<|eot_id|>“
)

prefix = “””
Instructions:
- Respond with ONE and ONLY ONE query.
- Use provided node and relationship labels and property names from the
schema which describes the database’s structure. Upon receiving a user
question, synthesize the schema to craft a precise Cypher query that
directly corresponds to the user’s intent.
- Generate valid executable Cypher queries on top of Memgraph database.
Any explanation, context, or additional information that is not a part
of the Cypher query syntax should be omitted entirely.
- Use Memgraph MAGE procedures instead of Neo4j APOC procedures.
- Do not include any explanations or apologies in your responses. Only answer the question asked.
- Do not include additional questions. Only the original user question.
- Do not include any text except the generated Cypher statement.
- For queries that ask for information or functionalities outside the direct
generation of Cypher queries, use the Cypher query format to communicate
limitations or capabilities. For example: RETURN “I am designed to generate Cypher queries based on the provided schema only.”

Here is the schema information

{schema}

With all the above information and instructions, generate Cypher query for the
user question.

The question is:

{question}

Below are a number of examples of questions and their corresponding Cypher queries.”””

cypher_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix=prefix,
    suffix=”User input: {question}\nCypher query: “,
    input_variables=[“question”, “schema”],
)

Vous allez ensuite créer un prompt pour contrôler la façon dont le LLM répond à la question avec les informations fournies par Memgraphe. Nous fournirons au LLM plusieurs exemples et instructions sur la manière de répondre une fois qu'il aura reçu les informations contextuelles provenant de la base de données graphique.

 

qa_examples = [
    {
        “question”: “<|begin_of_text|>What group is Charles in?<|eot_id|>“,
        “context”: “[{{‘g.id’: ‘Executive Group’}}]”,
        “response”: “Charles is in the Executive Group<|eot_id|>“
    },
    {
        “question”: “<|begin_of_text|>Who does Paul work with?<|eot_id|>“,
        “context”: “[{{‘p.id’: ‘Greg’}}, {{‘p2.id’: ‘Norma’}}]”,
        “response”: “Paul works with Greg and Norma<|eot_id|>“
    },
    {
        “question”: “What title does Rico have?<|eot_id|>“,
        “context”: “[{{‘t.id’: ‘Vice President of Sales’}}]”,
        “response”: “Vice President of Sales<|eot_id|>“
    }
]

qa_template = “””
Use the provided question and context to create an answer.Question: {question}

Context: {context}
Use only names departments or titles contained within {question} and {context}.
“””
qa_example_prompt = PromptTemplate.from_template(“”)

qa_prompt = FewShotPromptTemplate(
    examples=qa_examples,
    prefix=qa_template,
    input_variables=[“question”, “context”],
    example_prompt=qa_example_prompt,
    suffix=” “
)

Il est maintenant temps de créer la chaîne de réponse aux questions. Le MemgraphQAChain vous permet de définir le LLM que vous souhaitez utiliser, le schéma de graphique à utiliser et les informations sur le débogage. L'utilisation d'une température de 0 et d'une pénalité de longueur encourage le LLM à maintenir le prompt Cypher court et simple.

query_gen_parameters = {
    GenTextParamsMetaNames.DECODING_METHOD: “sample”,
    GenTextParamsMetaNames.MAX_NEW_TOKENS: 100,
    GenTextParamsMetaNames.MIN_NEW_TOKENS: 1,
    GenTextParamsMetaNames.TEMPERATURE: 0.0,
    GenTextParamsMetaNames.TOP_K: 1,
    GenTextParamsMetaNames.TOP_P: 0.9,
    GenTextParamsMetaNames.LENGTH_PENALTY: {‘decay_factor’: 1.2, ‘start_index’: 20}
}

chain = MemgraphQAChain.from_llm(
        llm = WatsonxLLM(
        model_id=”meta-llama/llama-3-3-70b-instruct”,
        url=”https://us-south.ml.cloud.ibm.com”,
        project_id=”dfe8787b-1f6f-4e18-b36a-e22c00f141d1”,
        params=query_gen_parameters
    ),
    graph = graph,
    allow_dangerous_requests = True,
    verbose = True,
    return_intermediate_steps = True, # for debugging
    cypher_prompt=cypher_prompt,
    qa_prompt=qa_prompt
)

Vous pouvez désormais invoquer la chaîne à l'aide d'une question en langage naturel (veuillez noter que vos réponses peuvent être légèrement différentes, car les LLM ne sont pas purement déterministes).

chain.invoke(“What is Johns title?”)

Ceci affichera :

> Saisie de la nouvelle chaîne MemGraphQACain... Chiffrement généré :  MATCH (p:Person {id: 'John'})-[:TITLE]->(t:Title) RETURN t.id
Full Context:
[{'t.id': 'Director of the Digital Marketing Group'}]

> Finished chain.
{'query': 'What is Johns title?',
 'result': ' \nAnswer: Director of the Digital Marketing Group.',
 'intermediate_steps': [{'query': " MATCH (p:Person {id: 'John'})-[:TITLE]->(t:Title) RETURN t.id"},
  {'context': [{'t.id': 'Director of the Digital Marketing Group'}]}]}

À la question suivante, posez à la chaîne une question un peu plus complexe :

chain.invoke(“Who does John collaborate with?”)

Elle devrait renvoyer :

> Entering new MemgraphQAChain chain...
Generated Cypher:
MATCH (p:Person {id: ‘John’})-[:COLLABORATES]->(c:Person) RETURN c
Full Context:
[{‘c’: {‘id’: ‘Jane’}}]

> Finished chain.
{‘query’: ‘Who does John collaborate with?’,
‘result’: ‘ \nAnswer: John collaborates with Jane.’,
‘intermediate_steps’: [{‘query’: “ MATCH (p:Person {id: ‘John’})-[:COLLABORATES]->(c:Person) RETURN c”},
{‘context’: [{‘c’: {‘id’: ‘Jane’}}]}]}

La réponse correcte est contenue dans la réponse. Dans certains cas, il peut y avoir un texte supplémentaire que vous souhaiterez supprimer avant de renvoyer la réponse à un utilisateur final.

Vous pouvez interroger la chaîne Memgraph sur les relations entre les groupes :

chain.invoke(“What group is Jane in?”)

Elle renverra :

> Entering new MemgraphQAChain chain...
Generated Cypher:
MATCH (p:Person {id: ‘Jane’})-[:GROUP]->(g:Group) RETURN g.id
Full Context:
[{‘g.id’: ‘Executive Group’}]

> Finished chain.
{‘query’: ‘What group is Jane in?’,
‘result’: ‘Jane is in Executive Group.’,
‘intermediate_steps’: [{‘query’: “ MATCH (p:Person {id: ‘Jane’})-[:GROUP]->(g:Group) RETURN g.id”},
{‘context’: [{‘g.id’: ‘Executive Group’}]}]}

C’est la bonne réponse.

Enfin, posez une question à la chaîne avec deux résultats :

chain.invoke(“Who does Jane collaborate with?”)

Cela devrait générer le résultat suivant :

> Entering new MemgraphQAChain chain...
Generated Cypher:
MATCH (p:Person {id: ‘Jane’})-[:COLLABORATES]->(c:Person) RETURN c
Full Context:
[{‘c’: {‘id’: ‘Sharon’}}]

> Finished chain.
{‘query’: ‘Who does Jane collaborate with?’,
‘result’: ‘ Jane collaborates with Sharon.’,
‘intermediate_steps’: [{‘query’: “ MATCH (p:Person {id: ‘Jane’})-[:COLLABORATES]->(c:Person) RETURN c”},
{‘context’: [{‘c’: {‘id’: ‘Sharon’}}]}]}

La chaîne identifie correctement les deux collaborateurs.

Conclusion

Dans ce tutoriel, vous avez créé une application Graph RAG en utilisant MemGraph et Watsonx pour générer les structures de données de graphes et les interroger. En utilisant un LLM via watsonx, vous avez extrait des informations sur les nœuds et les edges à partir d’une source de texte en langage naturel et vous avez généré une syntaxe de requête Cypher pour alimenter une base de données graphique. Vous avez ensuite utilisé watsonx pour transformer les questions en langage naturel sur ce texte source en requêtes Cypher qui ont extrait des informations de la base de données graphique. Grâce au prompt engineering, le LLM a transformé les résultats de la base de données Memgraphe en réponses en langage naturel.

Solutions connexes
IBM watsonx.ai

Entraînez, validez, réglez et déployez une IA générative, des modèles de fondation et des capacités de machine learning avec IBM watsonx.ai, un studio d’entreprise nouvelle génération pour les générateurs d’IA. Créez des applications d’IA en peu de temps et avec moins de données.

Découvrir watsonx.ai
Solutions d’intelligence artificielle

Mettez l’IA au service de votre entreprise grâce à l’expertise de pointe d’IBM en matière d’IA et à son portefeuille de solutions.

Découvrir les solutions d’IA
Conseil et services en Intelligence Artificielle (IA)

IBM Consulting et ses services d'IA accompagnent les entreprises dans la redéfinition de leurs activités avec l'intelligence artificielle pour mener leur transformation.

Découvrir les services d’IA
Passez à l’étape suivante

Grâce à l’IA, IBM Concert révèle des informations cruciales sur vos opérations et fournit des recommandations d’amélioration spécifiques aux applications. Découvrez comment Concert peut faire avancer votre entreprise.

Découvrir Concert Découvrir les solutions d’automatisation des processus métier