Python-Implementierung von RAG und Wissensabruf
Dieses Kapitel führt in die Technologie der Retrieval-Augmented Generation (RAG) ein.
RAG ist eine der Kerntechnologien zum Aufbau wissensintensiver Agenten.
Durch die Kombination von Vektorabruf mit Sprachgenerierungsmodellen verleiht RAG Agenten die Fähigkeit, auf externes Wissen zuzugreifen und es zu nutzen, statt sich ausschließlich auf Inhalte zu verlassen, die in den Modellparametern "erinnert" werden.
RAG-Grundlagen
Die Kernidee von RAG besteht darin, zwei Phasen zu kombinieren, "Informationsabruf" und "Sprachgenerierung", um eine vollständige Frage-Antwort-Pipeline zu bilden.
Wenn ein Benutzer eine Frage stellt, ruft das System zuerst aus der Wissensbasis Inhalte ab, die für die Frage relevant sind.
Dann stellt das System dem Generierungsmodell den abgerufenen Inhalt als Kontext bereit und hilft ihm, genauere und besser belegte Antworten zu erzeugen.
Warum RAG benötigt wird
Sprachmodelle haben einen Wissensstichtag und können Ereignisse, die nach Abschluss des Trainings eingetreten sind, nicht wahrnehmen.
Modellparameter sind endlich, und es ist unmöglich, das gesamte Wissen der Welt darin auswendig zu lernen.
Wenn das Modell sich vollständig auf das „Erinnern“ von Wissen aus Parametern verlässt, erfindet es oft mit unbewegter Miene Antworten, wenn es auf Inhalte stößt, an die es sich nicht genau erinnern kann oder die es nie gelernt hat. Das nennt man gemeinhin „Halluzination“.
Der Ansatz von RAG besteht darin, das Modell echte Daten konsultieren zu lassen, bevor es antwortet, wodurch „Antworten aus dem Gedächtnis“ in „Antworten mit offenem Buch“ verwandelt werden. Die Wissensquelle kann außerdem jederzeit aktualisiert werden, ohne das Modell neu zu trainieren.
Kernarchitektur
Ein vollständiges RAG-System besteht in der Regel aus drei Kernkomponenten:
Retriever: Verantwortlich dafür, die relevantesten Informationsschnipsel aus der Wissensbasis zu finden.
Vektorspeicher: Speichert Vektordarstellungen von Dokumentinhalten und unterstützt eine groß angelegte, latenzarme Ähnlichkeitssuche.
Generator: Erzeugt die endgültige natürlichsprachliche Antwort basierend auf den abgerufenen Inhalten und der ursprünglichen Frage des Nutzers.
Die Beziehung zwischen den drei lässt sich so verstehen: Der Retriever ist für das „Finden von Material“ zuständig, der Vektorspeicher für das „Speichern von Material und das schnelle Finden ähnlicher Materialien“ und der Generator für das „Organisieren der Antwort, während er das Material betrachtet“.
Workflow
Schritt 1:Indexierungsphase. Teilen Sie die Originaldokumente in mehrere Textabschnitte auf, vektorisieren Sie sie einzeln und speichern Sie sie in der Vektordatenbank. Dieser Schritt erfolgt normalerweise offline und im Voraus.
Schritt 2:Abrufphase. Wenn ein Benutzer eine Anfrage stellt, wird auch die Anfrage vektorisiert und eine Ähnlichkeitssuche in der Vektordatenbank durchgeführt, um die relevantesten Textabschnitte zu finden.
Schritt 3:Augmentierungsphase. Verketten Sie die abgerufenen relevanten Textabschnitte mit der ursprünglichen Frage des Benutzers, um einen vollständigeren Prompt zu erstellen.
Schritt 4:Generierungsphase. Das Generierungsmodell erzeugt die endgültige Antwort basierend auf diesem augmentierten Kontext.
Von diesen vier Schritten ist nur der erste eine Offline-Vorverarbeitung; die anderen drei finden in Echtzeit statt, wenn der Benutzer eine Anfrage stellt. Daher bestimmen Abrufgeschwindigkeit und Abrufqualität direkt die Antwortgeschwindigkeit und Antwortqualität des gesamten Systems.
Code-Implementierung
Grundlegende RAG-Implementierung
"""
Vereinfachte RAG-Systemimplementierung
Enthält drei Kernkomponenten: Embedder, Vektorspeicher, Generator
"""
def __init__(self, embedder, vector_store, generator):
# Embedder: verantwortlich für die Umwandlung von Text in Vektoren
self.embedder = embedder
# Vektordatenbank: verantwortlich für das Speichern von Vektoren und die Unterstützung der Ähnlichkeitssuche
self.vector_store = vector_store
# Generator: erzeugt Antworten basierend auf dem Kontext
self.generator = generator
def add_documents(self, documents):
"""
Dokumente zur Wissensbasis hinzufügen
:param documents: ursprüngliche Dokumentliste (jedes Element ist ein vollständiger Text)
"""
# Schritt 1: Lange Dokumente in kleinere Textabschnitte aufteilen für präzises Retrieval
chunks = self.chunk_documents(documents)
# Schritt 2: Alle Textabschnitte in Vektoren umwandeln
embeddings = self.embedder.embed(chunks)
# Schritt 3: Die Vektoren zusammen mit dem entsprechenden Originaltext in der Vektordatenbank speichern
self.vector_store.add(embeddings, chunks)
def chunk_documents(self, documents, chunk_size=500, overlap=50):
"""
Dokumentaufteilung: Lange Dokumente in Textabschnitte fester Länge mit etwas Überlappung aufteilen.
:param documents: Liste der Rohdokumente
:param chunk_size: Anzahl der Zeichen pro Textabschnitt
:param overlap: Anzahl der überlappenden Zeichen zwischen benachbarten Textabschnitten (um zu vermeiden, dass die Semantik an der Trennstelle abgeschnitten wird)
:return: Liste der Textabschnitte nach der Aufteilung
"""
chunks = []
for doc in documents:
# Gleitende Aufteilung mit einer Schrittweite von (chunk_size - overlap), wobei ein bestimmter überlappender Bereich beibehalten wird
for i in range(0, len(doc), chunk_size - overlap):
chunk = doc[i:i + chunk_size]
chunks.append(chunk)
return chunks
def query(self, question, top_k=5):
"""
Benutzeranfrage verarbeiten und die generierte Antwort zurückgeben
:param question: vom Benutzer gestellte Frage
:param top_k: Anzahl der relevanten Textabschnitte, die beim Abruf zurückgegeben werden
:return: endgültige Antwort des Generierungsmodells
"""
# 1. Die Frage in einen Vektor umwandeln, damit sie mit Vektoren in der Wissensdatenbank verglichen werden kann
question_embedding = self.embedder.embed([question])[0]
# 2. Die top_k ähnlichsten Textabschnitte aus der Vektordatenbank abrufen
results = self.vector_store.search(question_embedding, top_k)
# 3. Die abgerufenen Textabschnitte zu einem Kontext verketten
context = "\n".join([doc for doc, _ in results])
# 4. Den Kontext und die ursprüngliche Frage zu einem Prompt zusammenstellen
prompt = f"Beantworte die Frage basierend auf folgendem Inhalt:\n{context}\n\nFrage: {question}"
# 5. Es dem Generierungsmodell übergeben, um die endgültige Antwort zu erzeugen
return self.generator.generate(prompt)
Diese Basisimplementierung kann bereits die vollständige RAG-Pipeline ausführen, ist in realen Projekten jedoch oft nicht ausreichend: Die Chunking-Methode ist zu grob, und das Retrieval-Ranking stützt sich nur auf eine einzige Vektorähnlichkeitsberechnung, was zu begrenzter Genauigkeit führt. Im Folgenden werden einige gängige Optimierungsideen vorgestellt.
Fortgeschrittenes RAG
Häufige Probleme bei Basis-RAG sind: schlechte Qualität der abgerufenen Inhalte, unzureichende Ranking-Genauigkeit und irrelevante Informationen, die in den Kontext eingemischt werden.
Fortgeschrittenes RAG optimiert die Basispipeline weiter, indem Techniken wie Reranking und Hybrid-Retrieval eingeführt werden, wodurch die Genauigkeit der endgültigen Antwort verbessert wird.
Reranking
Die Vektorsuche in Basis-RAG ist schnell, aber die Ranking-Genauigkeit ist begrenzt, und sie kann Inhalte, die semantisch weniger relevant sind, leicht höher einordnen.
Der Reranking-Ansatz ist: Zuerst wird mithilfe der Vektorsuche schnell eine Menge von Kandidatentexten herausgefiltert (z. B. top-20), dann wird ein rechenintensiverer, aber genauerer Cross-Encoder (Cross-Encoder) verwendet, um jeden Kandidaten einzeln zu bewerten, sie neu zu ordnen und die wirklich relevantesten wenigen (z. B. top-5) herauszuziehen.
Dieser zweistufige Ansatz aus "zuerst grobem Filtern, dann präzisem Ranking" bringt Abrufgeschwindigkeit und Abrufgenauigkeit in ein ausgewogenes Verhältnis.
RAG mit Reranking
"""
Fortgeschrittenes RAG-System
Ergänzt die Basispipeline um einen Reranking-Schritt
"""
def __init__(self, embedder, vector_store, reranker, generator):
# Embedder
self.embedder = embedder
# Vektordatenbank
self.vector_store = vector_store
# Reranking-Modell (Cross-Encoder) für feingranulares Ranking
self.reranker = reranker
# Generator
self.generator = generator
def query(self, question, initial_k=20, final_k=5):
"""
Verarbeitet die Anfrage, einschließlich des zweistufigen Abrufs von "grobem Filtern + präzisem Ranking"
: :param question: Benutzerfrage
: :param initial_k: Die Anzahl der Kandidaten, die von der ersten Stufe der Vektorsuche zurückgegeben werden.
:param final_k: Die Anzahl der Elemente, die nach dem Re-Ranking letztendlich beibehalten werden.
"""
# ==================== Phase 1: Initiale Abfrage (Grobauswahl) ====================
Das Problem in einen Vektor umwandeln.
question_embedding = self.embedder.embed([question])[0]
# Schnelles Abrufen einer Charge von Kandidatentexten mithilfe von Vektorähnlichkeit (schnell, durchschnittliche Genauigkeit)
initial_results = self.vector_store.search(
question_embedding,
initial_k
)
# ==================== Phase 2: Re-Ranking (Feinranking) ====================
(Frage, Kandidatendokument)-Paare bilden und sie nacheinander dem Cross-Encoder zur Bewertung zuführen.
doc_pairs = [(question, doc) for doc, _ in initial_results]
Cross-Encoder kodieren die Frage und das Dokument gleichzeitig, was eine genauere Beurteilung ihrer Relevanz ermöglicht.
reranked = self.reranker.rerank(doc_pairs)
# Die wirklich relevantesten final_k Elemente nach dem Re-Ranking nehmen.
final_context = "\n".join([doc for doc, _ in reranked[:final_k]])
# ==================== Stufe 3: Generierung =====================
prompt = fKontext: {final_context}\n\nFrage: {question}
return self.generator.generate(prompt)
Hybrides Retrieval
Hybrides Retrieval kombiniert die Vorteile von Dense Retrieval und Sparse Retrieval.
Dense Retrieval basiert auf Vektorähnlichkeit und zeichnet sich durch die Erfassung semantischer Relevanz aus; es gleicht Benutzeranfragen und Dokumente ab, selbst wenn sie nicht exakt dieselben Wörter verwenden.
Sparse Retrieval (z. B. BM25) basiert auf Schlüsselwort-Häufigkeitsstatistiken, zeichnet sich durch exakte wörtliche Übereinstimmung aus und ist oft zuverlässiger als Vektor-Retrieval für Szenarien wie Eigennamen, Zahlen und Codeausschnitte.
Beide haben blinde Flecken: Vektor-Retrieval kann "die Bedeutung verstehen, aber die genauen Schlüsselwörter nicht finden", während Schlüsselwort-Retrieval "die Wörter abgleichen, aber die Semantik nicht verstehen" kann. Hybrides Retrieval fusioniert die Ergebnisse der beiden Retrieval-Pfade und ergänzt so die jeweiligen Stärken und Schwächen.
Hybrides Retrieval RAG
"""
Hybrides Retrieval RAG
Kombination von Dense Retrieval (Vektor) und Sparse Retrieval (Schlüsselwort)
"""
def __init__(self, dense_retriever, sparse_retriever, fusion_fn):
# Dense Retriever: Basiert auf Vektorähnlichkeit, zeichnet sich durch semantisches Matching aus.
self.dense_retriever = dense_retriever
# Sparse Retriever: Basiert auf Schlüsselwortstatistiken (z. B. BM25), zeichnet sich durch exaktes Matching aus.
self.sparse_retriever = sparse_retriever
# Fusion-Funktion: Wird verwendet, um zwei Retrieval-Ergebnisse zusammenzuführen, z. B. RRF (siehe Implementierung unten)
self.fusion_fn = fusion_fn
def query(self, question, top_k):
"""
Hybrides Retrieval durchführen und die fusionierten Ranglistenergebnisse zurückgeben.
: :param question: Benutzerfrage
:param top_k: Die endgültige Anzahl der zurückzugebenden Ergebnisse.
"""
# ==================== Dense Retrieval =====================
# Mehr Kandidaten abrufen (top_k * 2), um Spielraum für die Filterung während der Fusion zu lassen
dense_results = self.dense_retriever.search(question, top_k * 2)
# ==================== Sparse Retrieval ====================
# Schlüsselwortbasiertes Retrieval, ebenfalls mehr Kandidaten abrufen
sparse_results = self.sparse_retriever.search(question, top_k * 2)
# ==================== Ergebnis-Fusion ====================
# Strategien wie Reciprocal Rank Fusion (RRF) verwenden, um die Ranking-Ergebnisse beider Kanäle zu fusionieren
fused = self.fusion_fn(dense_results, sparse_results)
# Die top_k Elemente mit dem höchsten fusionierten Ranking nehmen
return fused[:top_k]
def reciprocal_rank_fusion(results_a, results_b, k=60):
"""
Reciprocal Rank Fusion (RRF)-Algorithmus
Wird verwendet, um die Ranking-Ergebnisse aus zwei (oder mehreren) Retrieval-Kanälen zu einem einheitlichen Ranking zu fusionieren
Kernidee: Betrachte nur den "Rang" jedes Dokuments in den Ergebnissen jedes Kanals, nicht den konkreten Score,
Dies vermeidet das Problem, dass Vektorähnlichkeits-Scores und BM25-Scores unterschiedliche Skalen haben und nicht direkt verglichen werden können.
:param results_a: Abrufergebnisse A, formatiert als [(doc, score), ...]
:param results_b: Abrufergebnisse B, formatiert als [(doc, score), ...]
:param k: Glättungskonstante, verwendet, um den Gewichtsunterschied für niedriger eingestufte Dokumente zu reduzieren, normalerweise auf 60 gesetzt
:return: Liste von [(doc, score), ...], sortiert nach fusioniertem Score von hoch nach niedrig
"""
scores = {}
# Ergebnisse A verarbeiten: Je höher der Rang, desto höher der beigesteuerte Score
for rank, (doc, _) in enumerate(results_a):
scores[doc] = scores.get(doc, 0) + 1 / (k + rank + 1)
# Ergebnisse B verarbeiten, Scores ebenfalls nach Rang akkumulieren
for rank, (doc, _) in enumerate(results_b):
scores[doc] = scores.get(doc, 0) + 1 / (k + rank + 1)
# Nach dem akkumulierten fusionierten Score von hoch nach niedrig sortieren
sorted_docs = sorted(scores.items(), key=lambda x: -x[1])
return sorted_docs
Weitere Optimierungsstrategien
Query-Erweiterung: Die ursprüngliche Abfrage durch Synonyme oder verwandte Begriffe erweitern, um den Recall-Bereich zu vergrößern und die Trefferquote zu verbessern.
Query-Umschreibung: Modelle verwenden, um die wahre Absicht des Nutzers zu verstehen und umgangssprachliche oder mehrdeutige Fragen in für die Suche besser geeignete Formulierungen umzuschreiben.
Kontextkomprimierung: Bevor Abrufergebnisse an das Generierungsmodell übergeben werden, redundante und irrelevante Teile entfernen und nur die Schlüsselinformationen behalten. Dies senkt die Kosten und verringert auch das Risiko, dass das Generierungsmodell durch irrelevanten Inhalt gestört wird.
Vektordatenbank
Eine Vektordatenbank ist die Infrastruktur des RAG-Systems, verantwortlich für die Speicherung massiver hochdimensionaler Vektoren und die Unterstützung groß angelegter Ähnlichkeitssuche mit niedriger Latenz.
Kernkonzepte
Embedding: Der Prozess, Text (oder Bilder, Audio usw.) in eine Menge dichter numerischer Vektoren umzuwandeln, wobei semantisch ähnliche Inhalte im Vektorraum näher beieinander liegen.
VektordimensionDie Länge des Embedding-Vektors. Je höher die Dimension, desto reichhaltiger sind theoretisch die semantischen Informationen, die ausgedrückt werden können, aber die Speicher- und Rechenkosten steigen entsprechend.
Ähnlichkeitsmaß.: Eine spezifische Berechnungsmethode zur Messung der "Ähnlichkeit" oder "Distanz" zwischen zwei Vektoren. Unterschiedliche Metriken eignen sich für unterschiedliche Szenarien.
Gängige Ähnlichkeitsmetriken
| Messmethode | Formel | Funktionen | Anwendbare Szenarien |
|---|---|---|---|
| Kosinusähnlichkeit. | cos(A,B) = A·B/(|A||B|) | Misst nur die Richtungsähnlichkeit, unabhängig von der Vektorlänge, mit einem Wertebereich von [-1,1] | Dokument-/Textähnlichkeit |
| Euklidische Distanz | d(A,B) = sqrt(sum((Ai-Bi)^2)) | Misst den absoluten räumlichen Abstand zwischen Vektoren, mit einem Wertebereich von [0,+∞) | Bildmerkmalsabgleich |
| Skalarprodukt | A·B = sum(Ai*Bi) | Wird sowohl von Richtung als auch Länge beeinflusst, mit hoher Recheneffizienz | Empfehlungssystem |
Vergleich gängiger Vektordatenbanken
| Datenbank | Type | Funktionen | Anwendbare Szenarien |
|---|---|---|---|
| Pinecone | Cloud-Dienst | Vollständig verwalteter Dienst, einfach einzurichten, automatische Skalierung, keine manuellen Eingriffe erforderlich | Produktionsumgebungen, die schnell live gehen müssen |
| Weaviate | Open Source | Unterstützt nativ hybride Suche (Vektor + Schlüsselwort), hochgradig modular | Szenarien, die eine flexible Anpassung der Abruflogik erfordern |
| Milvus | Open Source | Hohe Verfügbarkeit, einfache horizontale Skalierung, kann Vektorskalen von Milliarden und mehr unterstützen | Vektorsuche im extrem großen Maßstab |
| Chroma | Open Source | Leichtgewichtig, einfache API, mit nur wenigen Codezeilen einsatzbereit | Prototypenentwicklung, Lehre und lokale Tests |
| Qdrant | Open Source | Basiert auf Rust, exzellente Leistung, unterstützt umfangreiche Filterbedingungen | Szenarien, die hohen Durchsatz und komplexe Filterung erfordern |
Codebeispiele
Verwendung der Chroma-Vektordatenbank
from chroma_client import ChromaClient
# Einen Client erstellen (kann sich mit einer lokalen Instanz oder einem Remote-Dienst verbinden)
client = ChromaClient()
# Eine Collection erstellen oder abrufen, entspricht einer 'Tabelle' in einer relationalen Datenbank
collection = client.get_or_create_collection("knowledge_base")
# Dokumente hinzufügen: ID, Vektor, Originaltext und Metadaten müssen eins zu eins übereinstimmen
collection.add(
ids=["doc1", "doc2", "doc3"], # Eindeutige Kennung für jedes Dokument
embeddings=[
[0.1, 0.2, 0.3], # Vektor, der Dokument 1 entspricht (die tatsächliche Dimension ist normalerweise viel höher als 3 Dimensionen)
[0.4, 0.5, 0.6], # Vektor, der Dokument 2 entspricht
[0.7, 0.8, 0.9], # Vektor, der Dokument 3 entspricht
],
documents=[
Künstliche Intelligenz ist ein Teilgebiet der Informatik.,
"Maschinelles Lernen ist ein Teilgebiet der künstlichen Intelligenz",
"Deep Learning ist ein Teilgebiet des maschinellen Lernens"
],
metadatas=[
{"source": “textbook”}, # Quellinformation von Dokument 1, kann für spätere Filterung verwendet werden
{"source": Paper},
{"source": Paper}
]
)
# Abrufen: Abfragevektor übergeben, die ähnlichsten n_results Ergebnisse zurückgeben
results = collection.query(
query_embeddings=[[0.15, 0.25, 0.35]], # Vektor, der der Abfrage entspricht
n_results=2 # Top-2 ähnlichste Ergebnisse zurückgeben
)
# Abrufergebnisse ausgeben
print(results["documents"]) # Übereinstimmender Originaltextinhalt
print(results["distances"]) # Entsprechender Distanz-/Ähnlichkeitswert; kleinere Werte weisen normalerweise auf eine höhere Ähnlichkeit hin
Bei der Auswahl einer Vektordatenbank müssen Datenumfang, Anforderungen an die Abfragelatenz, Bereitstellungsmethode (cloudgehostet oder selbst gehostet) und die Betriebsfähigkeiten des Teams umfassend berücksichtigt werden. Für die Entwicklung kleiner Prototypen ist Chroma vorzuziehen; für eine sofort einsatzbereite Produktionsumgebung wählen Sie Pinecone; für ultragroße Szenarien, die Autonomie und Kontrolle erfordern, sind Milvus oder Qdrant besser geeignet.
GraphRAG
GraphRAG ist eine fortgeschrittene Lösung, die einen Wissensgraphen mit RAG kombiniert.
Es verbessert die Abruf- und Schlussfolgerungsfähigkeiten des Systems, indem es eine aus Entitäten und Beziehungen bestehende Graphstruktur aufbaut.
Warum GraphRAG notwendig ist
Traditionelles RAG basiert auf Ähnlichkeitsabruf über Textabschnitte, wodurch es schwierig ist, Probleme zu behandeln, die „Multi-Hop-Schlussfolgerung“ erfordern (d. h. Antworten, die das Verbinden mehrerer Informationen erfordern).
Traditionelles RAG hat außerdem Schwierigkeiten, semantische Beziehungen zwischen Entitäten explizit zu erfassen, etwa strukturierte Informationen wie „wer wessen Vorgesetzter ist“ oder „welches Unternehmen welches Unternehmen übernommen hat“.
GraphRAG organisiert diese Entitäten und Beziehungen explizit in einer Graphstruktur, sodass das System entlang von Beziehungspfaden schlussfolgern kann, anstatt nur „ähnlichen Text zu finden“.
Kernvorteile
Kann semantische Beziehungen zwischen Entitäten erfassen und bildet ein strukturiertes Wissensnetzwerk statt verstreuter Textfragmente.
Unterstützt pfadbasierte Schlussfolgerungen und kann relationale Fragen besser beantworten.
Kann komplexe Probleme bewältigen, die Multi-Hop-Schlussfolgerungen erfordern, wie „wer der Betreuer des Betreuers von jemandem ist“, bei denen mehrere Beziehungsknoten nacheinander durchlaufen werden müssen, um die Antwort abzuleiten.
Code-Implementierung
GraphRAG-Implementierung
"""
GraphRAG-System
Kombiniert Wissensgraph-Abruf und Vektorabruf und gleicht relationale Schlussfolgerung und semantische Ähnlichkeit aus.
"""
def __init__(self, kg_builder, embedder, vector_store, generator):
# Wissensgraph-Builder: zuständig für Entitäts-/Relationsextraktion und Graphabfragen
self.kg_builder = kg_builder
# Embedder: wird verwendet, um Dokumente und Fragen in Vektoren umzuwandeln
self.embedder = embedder
# Vektordatenbank: dient als ergänzender Kanal für das Wissensgraph-Retrieval
self.vector_store = vector_store
# Generator: generiert die endgültige Antwort basierend auf dem fusionierten Kontext
self.generator = generator
def index_documents(self, documents):
"""
Dokumente sowohl in den Wissensgraphen als auch in die Vektordatenbank indexieren
:param documents: Liste von Dokumenten
"""
for doc in documents:
# Entitäten aus Dokumenten extrahieren
# Zum Beispiel aus "OpenAI wurde 2015 gegründet" extrahieren
# Entität: OpenAI (Organisation), 2015 (Zeit)
entities = self.kg_builder.extract_entities(doc)
# Beziehungen zwischen Entitäten aus Dokumenten extrahieren
# Zum Beispiel ein Beziehungstripel extrahieren: (OpenAI, gegründet in, 2015)
relations = self.kg_builder.extract_relations(doc)
# Entitäten und Beziehungen in den Wissensgraphen schreiben
self.kg_builder.add_entities(entities)
self.kg_builder.add_relations(relations)
# Gleichzeitig den Originaltext vektorisieren und in einer Vektordatenbank speichern, um die semantische Suche zu ergänzen
embedding = self.embedder.embed([doc])
self.vector_store.add(embedding, [doc])
def query(self, question):
"""
Abfragen verarbeiten: Wissensgraph-Reasoning und Vektor-Retrieval kombinieren
"""
# ==================== Wissensgraph-Retrieval ====================
# Zuerst die relevanten Entitäten identifizieren, die in der Frage erwähnt werden
# Zum Beispiel "Wer hat OpenAI gegründet?" -> die Entität "OpenAI" identifizieren
relevant_entities = self.kg_builder.query_entities(question)
# Basierend auf diesen Entitäten den direkt mit ihnen verbundenen Teilgraphen aus dem Graphen extrahieren
# Der Teilgraph enthält relevante Entitäten sowie ihre benachbarten Knoten und verbindenden Kanten
subgraph = self.kg_builder.get_subgraph(relevant_entities)
# ==================== Vektor-Retrieval ====================
# Als ergänzender Kanal semantisch ähnlichen Textinhalt abrufen
vector_results = self.vector_store.search(question, top_k=5)
# ==================== Ergebnis-Fusion ====================
# Die Ergebnisse des Graph-Reasonings und des Vektor-Retrievals zu einem einheitlichen Kontext fusionieren
combined_context = self.fuse_results(subgraph, vector_results)
# ==================== Generierung ====================
return self.generator.generate(combined_context, question)
def fuse_results(self, subgraph, vector_results):
"""
Den Wissensgraph-Teilgraphen und die Vektor-Retrieval-Ergebnisse zu einem für ein generatives Modell lesbaren Kontexttext fusionieren
"""
# Den Teilgraphen in natürlichsprachliche Beschreibungen umwandeln (z. B. "OpenAI -gegründet in-> 2015")
kg_text = subgraph.to_text()
# Die durch Vektor-Retrieval abgerufenen Originaltextsegmente verketten
vector_text = "\n".join([doc for doc, _ in vector_results])
return f"{kg_text}\n{vector_text}"
class KnowledgeGraphBuilder:
"""Wissensgraph-Builder (illustrative Implementierung; in realen Projekten werden üblicherweise spezialisierte NER-/Relationsextraktionsmodelle aufgerufen)"""
def extract_entities(self, text):
"""
# Benannte Entitäten aus Text extrahieren, üblicherweise mit NER-Modellen (Named Entity Recognition)
# Häufige Entitätstypen sind PERSON (Person), ORG (Organisation), TIME (Zeit), usw.
"""
entities = []
return entities
def extract_relations(self, text):
"""
# Beziehungen zwischen Entitäten aus Text extrahieren, üblicherweise mit Relationsextraktionsmodellen
# Beziehungen werden im Allgemeinen als Tripel dargestellt: (Kopf-Entität, Beziehungstyp, End-Entität)
"""
relations = []
return relations
def query_entities(self, question):
"""Basierend auf der Nutzerfrage die Entitäten identifizieren, die an der Frage beteiligt sind, für den anschließenden Subgraph-Abruf"""
return []
Anwendungsszenarien
GraphRAG eignet sich besonders für die folgenden Szenarien:
Fragebeantwortung, die das Verständnis von Entitätsbeziehungen erfordert, z. B. "Wer ist der CEO dieses Unternehmens?"
Fragen, die Multi-Hop-Reasoning erfordern, um zu einer Antwort zu gelangen, z. B. "Wer ist der Berater des Beraters von Zhang San?"
Szenarien, die das Erschließen impliziter Beziehungen in der Wissensbasis erfordern, die nicht explizit niedergeschrieben sind.
Es ist zu beachten, dass der Aufbau und die Pflege eines Wissensgraphen selbst einen gewissen Engineering-Aufwand verursachen (Genauigkeit der Entitäts-/Relationsextraktion, Graphaktualisierung und -wartung usw.), sodass er eher für Szenarien mit klaren Anforderungen an relationales Reasoning geeignet ist und nicht als Standardausgangspunkt für alle RAG empfohlen wird.
Lösungsvergleich und Empfehlungen
| Lösung | Kernansatz | Vorteile | Einschränkungen | Geeignete Szenarien |
|---|---|---|---|---|
| Basis-RAG | Vektorabruf + direkte Generierung | Einfach zu implementieren, geringe Kosten, schneller Einstieg | Begrenzte Abrufpräzision, neigt dazu, nicht ausreichend relevanten Inhalt abzurufen | Einfache Q&A, schnelle Prototypvalidierung |
| Erweitertes RAG (Reranking) | Vektor-Grobauswahl + Cross-Encoder-Feinranking | Verbessert die Präzision des Abruf-Rankings erheblich | Fügt einen zusätzlichen Modellinferenzdurchlauf hinzu, was Latenz und Kosten erhöht | Szenarien mit hohen Anforderungen an die Antwortgenauigkeit |
| Hybrides Retrieval | Fusion von Vektorabruf und Keyword-Abruf | Balanciert semantisches Matching und präzises Keyword-Matching | Erfordert die gleichzeitige Pflege zweier Abrufindizes | Wissensbasen mit vielen Eigennamen, Code und Bezeichnern |
| GraphRAG | Wissensgraph + Vektorabruf-Fusion | Unterstützt relationales Reasoning und Multi-Hop-Q&A | Hohe Kosten für Graphaufbau und -wartung | Szenarien mit klaren Anforderungen an relationales und Multi-Hop-Reasoning |
Kapitelzusammenfassung
Dieses Kapitel führt in die Kerntechnologien von RAG und Wissensabruf ein.
RAG-Grundprinzipien: Durch die Kombination von Abruf und Generierung können Agenten Fragen mithilfe externen Wissens beantworten, anstatt sich vollständig auf das in den Modellparametern gespeicherte Gedächtnis zu verlassen.
Erweitertes RAG: Durch Techniken wie Reranking und Hybrid-Retrieval verbessert es die Abrufqualität und Antwortgenauigkeit zusätzlich auf der Basis-Pipeline.
Vektordatenbank: Sie dient als Infrastruktur von RAG-Systemen und ist für die effiziente Speicherung und den Abruf von Vektordarstellungen von Dokumenten verantwortlich; die Wahl der Lösung sollte anhand von Datenumfang und Bereitstellungsmethode getroffen werden.
GraphRAG: In Kombination mit Wissensgraphen verbessert es relationales Reasoning und Multi-Hop-Q&A-Fähigkeiten, bringt aber auch zusätzliche Aufbau- und Wartungskosten mit sich.
Der Schlüssel zur Auswahl einer geeigneten RAG-Lösung liegt darin, zunächst die tatsächlichen Anforderungen des Geschäftsszenarios zu verstehen: Für einfache Frage-Antwort-Szenarien ist grundlegendes RAG oft ausreichend; wenn eine höhere Retrieval-Präzision erforderlich ist, kann Reranking eingeführt werden; wenn die Wissensbasis viele Eigennamen/Schlüsselwörter enthält, ist Hybrid-Retrieval geeignet; wenn ein hohes Maß an relationalem Reasoning und Multi-Hop-Q&A involviert ist, sollte die Einführung von GraphRAG in Betracht gezogen werden. Je komplexer die Technologie, desto höher die Engineering- und Wartungskosten, daher ist die Auswahl basierend auf tatsächlichen Anforderungen wichtiger als das blinde Streben nach "fortschrittlicheren Lösungen".
Andere Erweiterungen