20. September 2024 von Immo Weber und Sascha Windisch
GraphRAG: Komplexe Datenbeziehungen für effizientere LLM-Abfragen nutzen
Wer schonmal ein Eichhörnchen im Wald beobachtet hat, wird vielleicht bemerkt haben, dass es Eicheln an verschiedenen Stellen im Boden vergräbt. Obwohl das Futter als Vorrat für den Winter dienen soll, vergessen die Tiere häufig ihre angelegten Futterspeicher. Der Wald ist groß und das Auffinden jeder einzelnen Eichel könnte lange dauern.
Stellen Sie sich vor, das Eichhörnchen hätten eine detaillierte Karte, die nicht nur zeigt, wo die Eicheln vergraben sind, sondern auch, wie diese verschiedenen Verstecke miteinander verbunden sind. Mit dieser Karte können die Orte gezielt angesteuert werden, an denen die wertvollsten Eicheln vergraben sind, ohne planlos im Wald umherzuirren.
Ähnlich wie das Eichhörnchen, das sich auf die Suche nach seinen Vorräten macht, stehen Unternehmen und Behörden oft vor der Herausforderung, relevante Informationen in riesigen Datenmengen zu finden. Obwohl mit Retrieval Augmented Generation (RAG) eine noch recht junge Technologie zur Verfügung steht um lokales Domänenwissen gezielt abzufragen, scheitert die Technologie oft daran komplexe verteilte Informationen zu aggregieren. Hier kommt GraphRAG ins Spiel.
Klassische Retrieval Augmented Generation
GraphRAG ist eine Synthese aus RAG und „Knowledge Graphen“. Das Thema RAG hatten wir bereits in vorangegangenen Blog-Beitrag ausführlich beleuchtet, daher an dieser Stelle nur eine kurze Zusammenfassung. RAG ist eine Technik bzw. Systemarchitektur um den Kontext von Sprachmodellen (Large Language Models, LLM) wie GPT-4o (OpenAI) mit spezifischem Wissen anzureichern. Dieses zusätzliche Wissen kann insbesondere hochspezifisches lokales Domänenwissen beinhalten, welches nicht Teil der Trainingsdaten des LLM war. Der große Vorteil von RAG ist, dass dieses Wissen dynamisch bereitgestellt und abgefragt werden kann, ohne aufwändiges und kostspieliges Finetuning oder Nachtrainings durchzuführen.
Die Nutzung von RAG gliedert sich in eine Vorbereitungsphase und eine Inferenzphase. In der Vorbereitungsphase werden Quelldokumente aufbereitet, in Textblöcke (Chunks) segmentiert, mittels eines sog. Embedding-Modells in ein nummerisches Format (Vektoren) konvertiert und schließlich in einer Vektordatenbank hinterlegt. Richten die Anwendenden in der Inferenzphase nun eine Anfrage an das RAG-System wird diese zunächst auch vektorisiert und anschließend eine Ähnlichkeitssuche durchgeführt. Dabei werden die Textsegmente in der Vektordatenbank gesucht, welche die größte Ähnlichkeit mit der ursprünglichen Anfrage aufweisen. Die ähnlichsten Segmente werden schließlich zusammen mit der Anfrage an ein Sprachmodell übergeben, um die finale Antwort zu generieren.
Wo klassisches RAG an seine Grenzen stößt
Die klassische RAG-Architektur funktioniert gut, wenn Anfragen durch explizite Informationen aus den Quelldokumenten beantwortet werden können. Im Idealfall ist die Antwort auf eine Anfrage faktisch direkt in einem vorgehaltenen Quelldokument zu finden. Wenn jedoch eine Antwort eine Kombination von Informationen aus verschiedenen Quelldokumenten benötigt, stößt die klassische RAG-Architektur schnell an ihre Grenzen.
Dies liegt insbesondere in der Art und Weise begründet, wie klassische RAG zu einer Anfrage passende relevante Textsegmente aus den Quelldokumenten findet („Retrieval“-Phase). Klassischerweise wird hierfür eine semantische Ähnlichkeitssuche innerhalb der vorgehaltenen Quelldokumente durchgeführt. Diese führt jedoch zu keinem eindeutigen Ergebnis, wenn die relevanten Informationen auf mehrere Dokumente verteilt sind oder wenn die gesuchten Informationen nur indirekt miteinander in Beziehung stehen.
Ein Beispiel für eine solche Anfrage könnte sein: "Wie hat die Klimapolitik in den letzten zwei Jahrzehnten die weltweiten CO2-Emissionen beeinflusst?" Diese Anfrage erfordert eine Kombination von Informationen aus verschiedenen Zeiträumen und Dokumenten, was dazu führen kann, dass die RAG-Architektur Schwierigkeiten hat, eine präzise Antwort zu liefern. In solchen Fällen kann die semantische Ähnlichkeitssuche zwar einzelne relevante Segmente identifizieren, jedoch gelingt es der klassischen RAG-Architektur oft nicht, diese Segmente so zu kombinieren, dass eine kohärente und präzise Antwort entsteht. Dies führt zu einem Informationsverlust oder ungenauen Antworten, da die Retrieval-Phase nicht dafür ausgelegt ist, komplexe Zusammenhänge zwischen verschiedenen Dokumenten zu erkennen und zu verknüpfen.
Um komplexere Anfragen zu beantworten, existieren vielfältige fortgeschrittene RAG-Techniken, welche kontinuierlich weiterentwickelt werden. Mögliche Ansätze sind hierbei zum Beispiel „Query Transformation“ oder „Context Enrichment“ (siehe hierzu Blog-Beitrag From RAGs to Riches). Eine neue Technik ist die Synthese aus klassischer RAG und dem Konzept von sogenannten Knowledge Graphen („Wissensgraphen“), welche initial auf das Paper G-Retriever: Retrieval-Augmented Generation for Textual Graph Understanding and Question Answering von He et al. (2024) zurückzuführen ist.
Was sind Knowledge Graphen?
Bevor wir uns dem Thema GraphRAG ausführlicher widmen, möchten wir einen kurzen Überblick über Knowledge Graphen geben. Ein Knowledge Graph ist eine datengetriebene Struktur, die Wissen in Form von Entitäten (Personen, Orte, Objekte) und den Beziehungen zwischen diesen Entitäten darstellt. Im Gegensatz zu traditionellen Datenbanken, die Informationen oft in isolierten Tabellen speichern, verknüpft ein Knowledge Graph Daten auf eine Weise, die es ermöglicht, komplexe Zusammenhänge und Interaktionen zwischen verschiedenen Informationspunkten zu erkennen und zu analysieren.
Ein Knowledge Graph besteht aus drei Hauptkomponenten:
- 1. Entitäten (Knoten): Diese repräsentieren die grundlegenden Informationseinheiten, wie zum Beispiel „Berlin“ als Stadt, „Benoit Mandelbrot“ als Person oder „Künstliche Intelligenz“ als Konzept.
- 2. Beziehungen (Kanten): Diese beschreiben die Verbindungen zwischen den Entitäten. Zum Beispiel könnte die Beziehung zwischen „Benoit Mandelbrot“ und „Fraktale Geometrie“ als „entwickelte“ dargestellt werden, oder die Beziehung zwischen „Berlin“ und „Deutschland“ als „Hauptstadt von“.
- 3. Attribute (Eigenschaften): Diese geben zusätzliche Informationen über die Entitäten oder Beziehungen. Beispielsweise könnte eine Entität „Albert Einstein“ Attribute wie „Geburtsjahr: 1924“ oder „Nationalität: Französisch“ haben.
Der Hauptvorteil eines Knowledge Graphen liegt in seiner Fähigkeit, kontextuelle Informationen zu erfassen und dadurch ein tieferes Verständnis der Daten zu ermöglichen. Wenn beispielsweise eine Suchanfrage nach „berühmte Mathematiker“ gestellt wird, kann ein Knowledge Graph nicht nur eine Liste von Mathematikern liefern, sondern auch relevante Informationen über deren Werke, Einflussbereiche und Verbindungen zu anderen Wissenschaftlern.
Von RAG und Graphen zu GraphRAG
Nachdem wir uns sowohl klassisches RAG als auch Knowledge Graphen individuell angeschaut haben stellt sich nun die berechtigte Frage, wie sich diese beiden Techniken sinnvoll verbinden lassen. Tatsächlich sind zwei komplementäre Ansätze denkbar, welche sich im Groben auf zwei Prozesse reduzieren lassen:
- 1. Indizieren
- 2. Abfragen
Indizieren: Erstellen von Knowledge Graphen mittels LLM
Der Prozess der Indizierung, also das Erstellen von Knowledge Graphen mittels LLM kann auch unabhängig von der Einbindung in GraphRAG genutzt werden. Im Kontext von GraphRAG entspricht dieser Prozess jedoch im Wesentlichen der Vorbereitungsphase in klassischen RAG-Systemen (siehe Blog-Beitrag Retrieval Augmented Generation). Statt einer (reinen) Vektordatenbank kann für das Vorhalten der Daten jedoch eine sogenannte (hybride) Graphdatenbank genutzt werden.
Der Prozess beginnt wie beim klassischen RAG zunächst
- I) mit der Extraktion und
- II) dem Segmentieren von Textblöcken (Abbildung 1). Wie beim klassischen RAG können auch hier theoretisch zusätzlich fortgeschrittene Segmentierungsmethoden wie z.B. im Rahmen von Context Expansion genutzt werden (siehe hierzu Blog-Beitrag From RAGs to Riches). Statt die Textsegmente jedoch direkt mittels Embedding-Modell zu vektorisieren werden hier zunächst
- IIIa) Entitäten und Beziehungen aus diesen mittels eines Sprachmodells extrahiert. Zusätzlich können ähnlich des HyDe („Hypothetical Document Embedding“) Ansatzes (siehe hierzu Blog-Beitrag From RAGs to Riches)
- IIIb) die Kernaussagen der jeweiligen Textsegmente mit den Entitäten und Beziehungen verknüpft werden. Mittels der Leiden Methode werden in einem nächsten Schritt
- IV) die extrahierten Entitäten mit deren Beziehungen in Communities geclustert. Eine Community ist dabei ein lokales Netzwerk aus dicht verbundenen Knotenpunkten, welche jedoch nur wenige Verbindungen zu anderen Communities haben. Im letzten Schritt der Indizierung werden
- V) hierarchische Zusammenfassungen auf Community-Ebene erstellt. Diese Community-Reports beinhalten Informationen über die häufigsten enthaltenen Entitäten, deren Verbindungen und HyDe-Inhalte. Die erstellten Knowledge Graphen, vektorisierten Entitäten und Community Reports werden schließlich
- VI) entweder separat oder in einer hybriden Vektor-/Graphdatenbank hinterlegt.
Abfragen: Nutzen von Knowledge Graphen um komplexe Anfragen zu beantworten
In der Abfrage-Phase kann ein GraphRAG System auf zwei Arten genutzt werden:
- 1) Abfrage von globalen Informationen
- 2) Abfrage von lokalen Informationen.
Den Überblick bekommen: die makroskopische Sicht
Bei globalen Abfragen werden eher allgemeinere Anfragen an die Daten gerichtet, welche es nötig machen Informationen auf einem makroskopischen Level zu aggregieren. Eine Frage könnte zum Beispiel sein: „Was sind die wesentlichen Inhalte des Datensatzes?“ Hierfür werden in erster Linie die Community Reports auf höheren Hierarchiestufen genutzt.
Das Vorgehen hierbei gestaltet sich wie folgt: Zunächst werden die Community Reports in zufälligen Stapeln (Batches) kombiniert und wiederum segmentiert. Dann wird jedes Textsegment zusammen mit der initialen Anfrage an ein LLM übergeben, um eine Zwischenantwort zu generieren. Diese Zwischenantwort enthält eine Liste mit Punkten, welche nach Relevanz bewertet wird. Die relevantesten Punkte aller Zwischenantworten werden schließlich als Kontext mit der initialen Frage an ein LLM übergeben, um die finale Antwort zu generieren.
Unter dem Brennglas: die mikroskopische Sicht
Lokale Anfragen werden genutzt, um spezifischere Anfragen an die eigenen Daten zu richten. Das Vorgehen entspricht hierbei zunächst dem klassischen RAG-Prozess: Die Anfrage wird zunächst
- 1) mittels Embedding Modell vektorisiert (Abbildung 1). Dann wird
- 2) eine Ähnlichkeitssuche über die vektorisierten Entitäten durchgeführt, um
- 3) die relevantesten Inhalte zu finden. Ausgehend vom Knowledge Graphen, werden
- 4) die nächsten Beziehungen, benachbarten Entitäten und zugehörigen Community Reports extrahiert. Das Mapping der vektorisierten Entitäten auf den Knowledge Graphen kann hierbei entweder über zwei getrennte Datenbanken (Vektor und Graphdatenbank) oder eine einzelne hybride Datenbank erfolgen. Schließlich werden diese Inhalte als Kontext
- (5a) zusammen mit der initialen Anfrage
- (5b) an ein LLM übergeben und
- (6) die finale Antwort generiert.
Vor- und Nachteile GraphRAG
Ähnlich wie klassisches RAG ist auch GraphRAG nicht immer die effizienteste Lösung. Entsprechend sollte je nach Anwendungsfall abgewogen werden, ob der Aufwand einer Implementierung gerechtfertigt ist. Grundsätzlich ergeben sich folgende Vor- und Nachteile aus dem Ansatz.
Vorteile von GraphRAG
- Erklärbarkeit: Die Nutzung von strukturierten Knowledge Graphen bietet eine transparente und nachvollziehbare Informationsrepräsentation, was das Vertrauen in die Ergebnisse stärkt und die Grundlage für weiterführende Analysen schafft.
- Vielschichtige Analysen: GraphRAG ist in der Lage, komplexe Anfragen aus mehreren Blickwinkeln zu bearbeiten, indem es relevante Informationen aus verschiedenen Bereichen des Knowledge Graphen zusammenführt.
- Kontextbezogenere Abfragen: Dank des Knowledge Graphen kann GraphRAG die semantischen Zusammenhänge und Beziehungen zwischen verschiedenen Konzepten verstehen, was zu präziseren und relevanteren Ergebnissen führt.
Mögliche Nachteile von GraphRAG
- Herausforderungen bei der Skalierbarkeit: Mit der wachsenden Komplexität und Größe des Knowledge Graphen steigen auch die Anforderungen an die Rechenleistung, was besonders für Echtzeitanwendungen problematisch sein kann.
- Abhängigkeit von den zugrunde liegenden Daten: Die Effektivität von GraphRAG hängt stark von der Qualität und Vollständigkeit der genutzten Datenquellen ab. Wenn diese unvollständig oder verzerrt sind, kann dies die Leistungsfähigkeit des Systems beeinträchtigen.
- Komplexität beim Aufbau des Knowledge Graphen: Die Erstellung eines präzisen und umfassenden Knowledge Graphen erfordert sorgfältiges Prompt Engineering zur Extraktion von Entitäten und Modellierung von Beziehungen, was zeitaufwändig und technisch anspruchsvoll sein kann.
Code-Beispiel für Indizierung und lokale GraphRAG-Abfrage
Das Coding-Beispiel werden wir wieder, ähnlich zu den vorhergehenden Artikeln, relativ einfach halten. Vorausgesetzt wird eine Python-Umgebung, in der grundlegende Bibliotheken installiert wurden. Für die Kapselung der KI-Berechnungen und -Aufrufe nutzen wir wie gewohnt die Bibliothek LlamaIndex.
LlamaIndex bietet verschiedene Anbindungen an Graphen-Datenbanken an, von denen wir in diesem Beispiel die Graphen-Datenbank Neo4j nutzen werden. Diese hat die Vorteile, dass die Datenbank als Open-Source Lösung lokal oder in einem Docker-Container betrieben werden kann und dass wir uns die Inhalte graphisch ausgeben lassen können.
Die Anbindung geschieht über das Anlegen einer Konfiguration für den Zugriff auf die Datenbank.
graph_store = Neo4jPropertyGraphStore(
username="***",
password="***",
url="bolt://localhost:7687",
database="neo4j",
)
Im nächsten Schritt legen wir das Indizierungsobjekt an, dem wir die Neo4J-Verbindung und die zu benutzenden Sprachmodelle mitgeben. Es wird dabei zwischen einem LLM für die Erstellung der Embedding-Vektoren und einem LLM für die spätere Text- bzw. Antwortgenerierung unterschieden. In unserem Beispiel nutzen wir für beide Prozesse die LLMs von OpenAI. Hier könnten jedoch auch andere, lokale Sprachmodelle genutzt werden. Wir geben hier das konkrete Sprachmodell und eine Temperatur von 0 mit, mit der wir angeben, dass die relevanteste Antwort erzeugt wird und Kreativität, einhergehend mit der Gefahr von Halluzinationen, eher vernachlässigbar ist.
index = PropertyGraphIndex.from_documents(
documents,
embed_model=OpenAIEmbedding(model_name="text-embedding-3-small"),
kg_extractors=[
SchemaLLMPathExtractor(
llm=OpenAI(model="gpt-3.5-turbo", temperature=0.0)
)
],
property_graph_store=graph_store,
show_progress=True,
)
Wir geben weiter den Graphen-Extractor SchemaLLMPathExtractor als Parameter mit. Mit diesem Objekt können für die Knoten und Beziehungen des Graphen detaillierte Parameter gesetzt werden, so dass ein passender Graph erstellt werden kann. In unserem Beispiel nutzen wir die Default-Werte.
Mit der Ausführung des Codes wird in Neo4j der Graph angelegt. Mit eingeschalteter Protokollierung können wir dann auch nachvollziehen, wie die einzelnen Chunks aus der Zerlegung der Dokumente an das LLM zur Erstellung der Embedding-Vektoren geschickt werden.
Schauen wir nun von Weitem auf den Knowledge Graphen, sehen wir folgendes Bild von unterschiedlichen Beziehungen zwischen den Knoten. In unserem Beispiel gibt es scheinbar zwei Hauptknoten, von denen aus sich der Graph entwickelt hat.
Selbst ohne detaillierte Konfiguration des Extractors sehen wir in Abbildung 2B, dass der Index bereits passende Labels für die Knoten und Beziehungsarten erstellt hat.
Schauen wir uns nun die Daten eines Knotens genauer an, beispielsweise „Paul Graham“, sehen wir nicht nur die Quelle, aus der dieser Knoten entstanden ist (Dateinamen), sondern auch den Embedding-Vektor zu diesem Knoten, der in der späteren Suche genutzt werden kann.
Einmal generiert, kann die Datenbank nachgenutzt werden, ohne diese erneut erstellen zu müssen. Dazu erzeugen wir wiederum ein Index-Objekt, aber diesmal basierend auf der existierenden Datenbank.
index = PropertyGraphIndex.from_existing(
property_graph_store=graph_store,
llm=OpenAI(model="gpt-3.5-turbo", temperature=0.3),
embed_model=OpenAIEmbedding(model_name="text-embedding-3-small"),
)
Einmal erzeugt, kann der Graph nun für Inferenzen (Abfragen) genutzt werden. Dazu wird zunächst ein parametrisierbares Abfrage-Objekt (Query-Engine) erzeugt.
query_engine = index.as_query_engine(
include_text=True
)
Mit der folgenden Anfrage an die Query-Engine erhalten wir ein Response-Objekt mit der Antwort aus dem GraphRAG und dem Sprachmodell. Im Response-Objekt erkennen wir, dass mehrere Knoten gefunden wurden und zur Generierung genutzt wurden.
response = query_engine.query("What happened at Interleaf and Viaweb?")
print(str(response))
Wir stellen die zusammengesetzte Frage: "What happened at Interleaf and Viaweb?" und erhalten als längere komplexe Antwort: “Interleaf was one of many companies that had smart people and built impressive technology, but got crushed by Moore's Law. Viaweb, on the other hand, was a company that Paul Graham worked on, which allowed users to define their own page styles by editing Lisp expressions underneath.”
Zusammenfassung und Ausblick
In diesem Blog-Beitrag haben wir die Technik GraphRAG vorgestellt, welche die Vorteile klassischer Retrieval Augmented Generation und Knowledge Graphen in sich vereint. Hierbei können sowohl LLM genutzt werden, um Knowledge Graphen zu erstellen, als auch Graphen genutzt werden, um komplexe RAG-Abfragen zu beantworten. Der Ansatz zeigt einmal mehr, wie vielfältig die Technik RAG eingesetzt werden kann und welches Potential sie nach wie vor birgt. Einen weiteren Ansatz RAG im Zusammenhang mit Datenbanken zu nutzen, schauen wir uns im nächsten Artikel an, in welchem wir mit Text2SQL eine Technik vorstellen, um natürlich sprachliche Anfragen in SQL-Syntax zu übersetzen.
Ihr möchtet gern mehr über spannende Themen aus der adesso-Welt erfahren? Dann werft auch einen Blick in unsere bisher erschienenen Blog-Beiträge.
Auch interessant: