Implementierung von Graph RAG mithilfe von Wissensgraphen

Die erweiterte Generation (Graph Retrieval-Augmented Generation) (Graph RAG) entwickelt sich zu einer leistungsstarken Technik für generative KI-Anwendungen, um domänenspezifisches Wissen und relevante Informationen zu nutzen. Graph RAG ist eine Alternative zu Vektorsuchmethoden, die eine Vektordatenbank verwenden. 

Wissensgraphen (Knowledge Graphs) sind Wissenssysteme, in denen Graphdatenbanken wie Neo4j oder Amazon Neptune strukturierte Daten darstellen können. In einem Wissensgraphen sind die Beziehungen zwischen Datenpunkten, als Edges bezeichnet, genauso aussagekräftig wie die Verbindungen zwischen Datenpunkten, die als Knoten bezeichnet werden. Ein Wissensgraph erleichtert das Durchlaufen eines Netzwerks und das Verarbeiten komplexer Abfragen zu verbundenen Daten. Wissensgraphen eignen sich besonders gut für Anwendungsfälle, die Chatbots, Identitätsauflösung, Netzwerkanalyse, Empfehlungsmaschinen, Customer 360 und Betrugserkennung beinhalten.

Ein Graph RAG-Ansatz nutzt die strukturierte Natur von Diagrammdatenbanken, um den abgerufenen Informationen über Netzwerke oder komplexe Beziehungen mehr Tiefe und Kontext zu verleihen.  Eine Graphdatenbank wird mit einem Large Language Model (LLM) gepaart. So kann ein Entwickler wichtige Teile des Graphen-Erstellungsprozesses aus unstrukturierten Daten wie Text automatisieren. Ein LLM kann Textdaten verarbeiten und Entitäten identifizieren, ihre Beziehungen verstehen und sie in einer Diagrammstruktur darstellen.

Es gibt verschiedene Möglichkeiten, eine Graph-RAG-Anwendung zu erstellen, beispielsweise mit GraphRAG von Microsoft oder durch die Kombination von GPT4 mit LlamaIndex. Für dieses Tutorial verwenden Sie Memgraph, eine Open-Source-Lösung für Graphdatenbanken, um ein Rag-System mit Meta's Llama-3 auf Watsonx zu erstellen. Memgraph verwendet Cypher, eine deklarative Abfragesprache. Es weist einige Ähnlichkeiten mit SQL auf, konzentriert sich jedoch eher auf Knoten und Beziehungen als auf Tabellen und Zeilen. Llama 3 erstellt und füllt Ihre Diagrammdatenbank aus unstrukturiertem Text und Abfrageinformationen in der Datenbank.

Schritt 1

Sie können zwar aus mehreren Tools wählen, aber dieses Tutorial führt Sie durch die Einrichtung eines IBM® Kontos für die Verwendung eines Jupyter Notebook.

Melden Sie sich mit Ihrem IBM Cloud-Konto bei watsonx.ai an.

Erstellen Sie ein watsonx.ai-Projekt.

Sie können Ihre Projekt-ID in Ihrem Projekt abrufen. Klicken Sie auf die Registerkarte „Verwalten“. Kopieren Sie dann die Projekt-ID aus dem Abschnitt „Details“ der Seite „Allgemein“. Sie benötigen diese Projekt-ID für dieses Tutorial.

Verknüpfen Sie als Nächstes Ihr Projekt mit watsonx.ai Runtime

a.  Erstellen Sie eine Instanz des watsonx.ai Runtime Service (wählen Sie den Lite-Plan als kostenlose Instanz).

b.  Generieren Sie einen API-Schlüssel in watsonx.ai Runtime. Speichern Sie diesen API-Schlüssel für die Verwendung in diesem Tutorial.

c. Gehen Sie zu Ihrem Projekt und wählen Sie die Registerkarte „Verwalten“ aus

d.  Wählen Sie auf der linken Registerkarte „Services und Integrationen“ aus

e. Wählen Sie IBM Services aus

f. Wählen Sie Associate Service und watsonx.ai Runtime aus.

g. Verknüpfen Sie watsonx.ai Runtime mit dem Projekt, das Sie in watsonx.ai erstellt haben

Schritt 2

Nun müssen Sie Docker installieren.

Nachdem Sie Docker installiert haben, installieren Sie Memgraph mithilfe des Docker-Containers. Unter OSX oder Linux können Sie diesen Befehl in einem Terminal verwenden:

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

Verwenden Sie einen Windows-Computer:

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

Folgen Sie den Installationsschritten, um die Memgraph-Engine und das Memgraph-Labor zum Laufen zu bringen.

Erstellen Sie auf Ihrem Computer eine neue virtualenv für dieses Projekt:

virtualenv kg_rag --python=python3.12

Installieren Sie in der Python-Umgebung für Ihr Notebook die folgenden Python-Bibliotheken:

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

Jetzt können Sie sich mit Memgraph verbinden.

Schritt 3

Wenn Sie Memgraph für die Verwendung eines Benutzernamens und eines Passworts konfiguriert haben, geben Sie diese hier ein. Andernfalls können Sie die Standardeinstellungen verwenden, bei denen weder Benutzername noch Passwort erforderlich sind. Für eine Produktionsdatenbank ist dies nicht empfehlenswert, jedoch stellt es für eine lokale Entwicklungsumgebung, in der keine sensiblen Daten gespeichert werden, kein Problem dar.

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
)

Erstellen Sie nun einen Beispielstring, der einen Datensatz mit Beziehungen beschreibt, mit dem Sie die Fähigkeiten Ihres LLM-Systems zur Diagrammerstellung testen können. Sie können auch komplexere Datenquellen verwenden, aber dieses einfache Beispiel hilft uns, den Algorithmus zu demonstrieren.

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.
“””

Geben Sie den watsonx API-Schlüssel ein, den Sie im ersten Schritt erstellt haben:

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

Konfigurieren Sie nun eine watsonxLLM-Instanz, um Text zu generieren. Die Temperatur sollte relativ niedrig und die Anzahl der Token hoch sein, um das Modell zu ermutigen, so viele Details wie möglich zu generieren, ohne dabei Entitäten oder Beziehungen zu halluzinieren, die nicht vorhanden sind.

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,
)

Mit dem LLMGraphTransformer können Sie festlegen, welche Arten von Knoten und Beziehungen das LLM generieren soll. In Ihrem Fall beschreibt der Text die Mitarbeiter eines Unternehmens, die Gruppen, in denen sie arbeiten, und ihre Berufsbezeichnungen. Wenn Sie das LLM auf diese Entitäten beschränken, ist es wahrscheinlicher, dass Sie eine gute Darstellung des Wissens in einem Diagramm erhalten.

Der Aufruf von convert_to_graph_documents lässt den LLMGraphTransformer einen Wissensgraphen aus dem Text erstellen. Dieser Schritt generiert die korrekte Neo4j-Syntax zum Einfügen der Informationen in die Graphdatenbank, um den relevanten Kontext und die relevanten Entitäten darzustellen.

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)

Löschen Sie nun alle alten Daten aus der Memgraph-Datenbank und fügen Sie die neuen Knoten und Edges ein.

# 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)

Die generierte Cypher-Syntax wird in den Objekten graph_documents gespeichert. Sie können sie einfach überprüfen, indem Sie sie als Zeichenfolge ausdrucken.

print(f”{graph_documents}”)

Das Schema und die Datentypen, die von Cypher erstellt wurden, können in der Eigenschaft „get_schema” der Graphen eingesehen werden.

graph.refresh_schema()
print(graph.get_schema)

Dies ergibt Folgendes:

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)

Sie können die Diagrammstruktur auch im Memgraph Labs Viewer sehen:

 

Ein Graphennetzwerkbild, das Knoten und Edges darstellt Das Memgraph-Netzwerk wurde aus dem Eingabetext generiert

Das LLM hat angemessene Arbeit bei der Erstellung der richtigen Knotenpunkte und Beziehungen geleistet. Jetzt ist es an der Zeit, den Wissensgraphen abzufragen.

Schritt 4

Das korrekte Prompting des LLM erfordert ein gewisses Prompt-Engineering. LangChain stellt ein FewShotPromptTemplate bereit, das verwendet werden kann, um dem LLM im Prompt Beispiele zu geben, um sicherzustellen, dass es die korrekte und prägnante Cypher-Syntax schreibt. Der folgende Code enthält mehrere Beispiele für Fragen und Abfragen, die das LLM verwenden sollte. Außerdem wird die Ausgabe des Modells auf die Abfrage beschränkt. Ein übermäßig gesprächiger LLM könnte zusätzliche Informationen hinzufügen, die zu ungültigen Cypher-Abfragen führen würden. Daher fordert die Prompt-Vorlage das Modell auf, nur die Abfrage selbst auszugeben.

Das Hinzufügen eines instruktiven Präfixes hilft auch, das Verhalten des Modells einzuschränken und macht es wahrscheinlicher, dass der LLM die korrekte Cypher-Syntax ausgibt.

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”],
)

Als Nächstes erstellen Sie einen Prompt, um zu steuern, wie das LLM die Frage mit den von Memgraph zurückgegebenen Informationen beantwortet. Wir werden dem LLM mehrere Beispiele und Anweisungen zur Verfügung stellen, wie es reagieren soll, sobald es Kontextinformationen aus der Graphdatenbank zurückerhalten hat.

 

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=” “
)

Jetzt ist es an der Zeit, die Frage-Antwort-Kette zu erstellen. Mit MemgraphQAChain können Sie festlegen, welches LLM Sie verwenden möchten, welches Graphschema verwendet werden soll und Informationen zum Debugging. Die Verwendung einer Temperatur von 0 und einer Längenstrafe ermutigt das LLM, den Cypher-Prompt kurz und einfach zu halten.

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
)

Nun können Sie die Kette mit einer Frage in natürlicher Sprache aufrufen (bitte beachten Sie, dass Ihre Antworten leicht abweichen können, da LLMs nicht rein deterministisch sind).

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

Dies wird Folgendes zur Ausgabe bringen:

> Entering new MemgraphQAChain chain...
Generated Cypher:
 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'}]}]}

Stellen Sie der Kette in der nächsten Frage eine etwas komplexere Frage:

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

Dies sollte Folgendes liefern:

> 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’}}]}]}

Die richtige Antwort ist in der Antwort enthalten. In einigen Fällen kann es zusätzlichen Text geben, den Sie entfernen möchten, bevor Sie die Antwort an einen Endbenutzer zurückgeben.

Sie können die Memgraph-Kette nach Gruppenbeziehungen fragen:

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

Dies führt zu folgendem Ergebnis:

> 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’}]}]}

Das ist die richtige Antwort.

Stellen Sie der Kette abschließend eine Frage mit zwei Ausgaben:

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

Dies sollte Folgendes ergeben:

> 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’}}]}]}

Die Kette identifiziert beide Beteiligten korrekt.

Zusammenfassung

In diesem Tutorial haben Sie eine Graph RAG-Anwendung mit Memgraph und watsonx erstellt, um die Diagrammdatenstrukturen zu generieren und abzufragen. Mithilfe eines LLM über watsonx haben Sie Knoten- und Edge-Informationen aus dem Quelltext in natürlicher Sprache extrahiert und eine Cypher-Abfragesyntax generiert, um eine Diagrammdatenbank zu füllen. Anschließend haben Sie watsonx verwendet, um Fragen in natürlicher Sprache zu diesem Quelltext in Cypher-Abfragen umzuwandeln, die Informationen aus der Diagrammdatenbank extrahiert haben. Mithilfe von Prompt Engineering wandelte das LLM die Ergebnisse aus der Memgraph-Datenbank in Antworten in natürlicher Sprache um.

Weiterführende Lösungen
IBM watsonx.ai

Trainieren, validieren, optimieren und implementieren Sie generative KI, Foundation Models und maschinelle Lernfunktionen mit IBM watsonx.ai, einem Studio der nächsten Generation für AI Builder in Unternehmen. Erstellen Sie KI-Anwendungen mit einem Bruchteil der Zeit und Daten.

watsonx.ai erkunden
Lösungen im Bereich künstlicher Intelligenz

Setzen Sie KI in Ihrem Unternehmen ein
– mit branchenführendem Fachwissen im Bereich KI und dem umfassenden Lösungsportfolio von IBM an Ihrer Seite.

Erkunden Sie KI-Lösungen
Beratung und Services zu künstlicher Intelligenz (KI)

Die KI-Services von IBM Consulting unterstützen Sie dabei, die Art und Weise, wie Unternehmen mit KI arbeiten, neu zu denken.

KI-Services entdecken
Machen Sie den nächsten Schritt

Mithilfe von KI liefert IBM Concert wichtige Erkenntnisse über Ihre Abläufe und gibt anwendungsspezifische Empfehlungen zur Verbesserung. Entdecken Sie, wie Concert Ihr Unternehmen voranbringen kann.

Entdecken Sie Concert Erkunden Sie Lösungen zur Geschäftsprozessautomatisierung