Evaluierungen für Retrieval Augmented Generation: TruLens + Milvus
Dieser Artikel wurde ursprünglich in The New Stack veröffentlicht und wird hier mit Genehmigung erneut veröffentlicht.
Die zunehmende Popularität großer Sprachmodelle (LLMs) hat den Aufstieg von Vektorsuchtechnologien befeuert, darunter speziell entwickelte Vektordatenbanken wie Milvus und Zilliz Cloud, Vektorsuchbibliotheken wie FAISS und Vektorsuch-Plugins, die in traditionelle Datenbanken integriert sind.
Zunehmend ist die Vektorsuche zum wesentlichen Enterprise-Anwendungsfall für generative KI in Form von Retrieval Augmented Generation, oder RAGs, Frage-Antwort-Anwendungen, geworden. Diese Konstruktionsweise ermöglicht es den LLMs, einfachen Zugriff auf eine verifizierte Wissensbasis zu haben, die sie als Kontext zur Beantwortung von Fragen nutzen können. Milvus ist eine hochskalierbare Open-Source-Vektordatenbank speziell für diese Anwendung entwickelt.
Aufbau eines RAG
Beim Aufbau einer effektiven RAG-artigen LLM-Anwendung gibt es viele Konfigurationsentscheidungen, aus denen man wählen kann und die die Abrufqualität erheblich beeinflussen können. Einige dieser Entscheidungen umfassen:
Aufbau der Vektor-DB
- Datenauswahl
- Einbettungsmodell
- Indextyp
Das Finden hochwertiger Daten, die genau den Anforderungen Ihrer Anwendung entsprechen, ist entscheidend. Der Abrufprozess könnte irrelevante Ergebnisse liefern, wenn Sie nicht die richtigen Daten haben.
Nachdem Sie Ihre Daten ausgewählt haben, sollten Sie das von Ihnen verwendete Einbettungsmodell berücksichtigen, da es die Abrufqualität erheblich beeinflusst. Selbst wenn Ihre Wissensbasis die korrekten Informationen enthält, kann der Retriever falsche Ergebnisse liefern, wenn das Einbettungsmodell ein semantisches Verständnis Ihrer Domäne benötigt.
Kontextrelevanz ist eine hilfreiche Metrik zur Einschätzung der Abrufqualität, und diese Auswahlmöglichkeiten beeinflussen sie stark.
Schließlich kann der Indextyp einen erheblichen Einfluss auf die Effizienz der semantischen Suche haben. Dies gilt insbesondere für große Datensätze; diese Wahl ermöglicht es Ihnen, zwischen Trefferquote, Geschwindigkeit und Ressourcenanforderungen abzuwägen. Milvus unterstützt verschiedene Indextypen, wie Flat-Indizes, auf Produktquantisierung basierende Indizes und graphbasierte Indizes. Sie können mehr über verschiedene Indextypen lesen.
Abruf
- Menge des abgerufenen Kontexts (top k)
- Chunk-Größe
Wenn wir zum Abruf kommen, ist top k ein häufig diskutierter Parameter, der die Anzahl der abgerufenen Kontext-Chunks steuert. Ein höheres top k gibt uns eine höhere Chance, die benötigten Informationen abzurufen, und erhöht die Wahrscheinlichkeit, dass unser LLM irrelevante Informationen in seine Antwort einbezieht. Für einfache Fragen ist ein niedrigeres top k oft am leistungsfähigsten.
Die Chunk-Größe steuert die Größe jedes abgerufenen Kontexts. Eine größere Chunk-Größe kann bei komplexeren Fragen hilfreich sein, während kleinere Chunks für einfache Fragen ausreichen, die mit nur einer winzigen Menge an Informationen beantwortet werden können.
Für viele dieser Entscheidungen gibt es keine Einheitslösung. Die Leistung kann je nach Größe und Art der Daten, den verwendeten LLMs, Ihrer Anwendung und mehr stark variieren. Wir benötigen ein Evaluierungstool, um die Qualität dieser Abrufe für unseren spezifischen Anwendungsfall zu beurteilen. Hier kommt TruLens ins Spiel.
TruLens für LLM-Tracking und -Evaluierung
TruLens ist eine Open-Source-Bibliothek zur Evaluierung und Nachverfolgung der Leistung von LLM-Apps, wie RAGs. Mit TruLens erhalten wir außerdem die Möglichkeit, LLMs selbst zur Bewertung von Ausgabe, Abrufqualität und mehr zu verwenden.
Wenn wir LLM-Anwendungen erstellen, ist das wichtigste Thema, das viele Menschen beschäftigt, Halluzination. RAGs tragen wesentlich dazu bei, genaue Informationen sicherzustellen, indem sie dem LLM abgerufenen Kontext bereitstellen, aber sie können dies nicht garantieren. Evaluierungen sind hier unerlässlich, um die Abwesenheit von Halluzination in unserer App zu verifizieren. TruLens bietet drei Tests für diesen Bedarf: Kontextrelevanz, Fundiertheit und Antwortrelevanz. Sehen wir uns jeden davon an, um zu verstehen, wie sie uns nutzen können.
Kontextrelevanz
Der erste Schritt jeder RAG-Anwendung ist der Abruf; um die Qualität unseres Abrufs zu verifizieren, möchten wir sicherstellen, dass jeder Kontextabschnitt für die Eingabeabfrage relevant ist. Dies ist entscheidend, da das LLM diesen Kontext verwenden wird, um eine Antwort zu formulieren; daher könnten irrelevante Informationen im Kontext in eine Halluzination eingewoben werden.
Fundiertheit
Nachdem der Kontext abgerufen wurde, wird er anschließend von einem LLM zu einer Antwort geformt. LLMs weichen oft von den bereitgestellten Fakten ab, übertreiben oder erweitern sie zu einer korrekt klingenden Antwort. Um die Fundiertheit unserer Anwendung zu verifizieren, sollten wir die Antwort in einzelne Aussagen aufteilen und unabhängig nach Belegen suchen, die jede davon innerhalb des abgerufenen Kontexts stützen.
Antwortrelevanz
Zuletzt muss unsere Antwort immer noch hilfreich die ursprüngliche Frage beantworten. Wir können dies verifizieren, indem wir die Relevanz der finalen Antwort für die Benutzereingabe evaluieren.
Halluzinationsfreie RAGs
Indem wir zufriedenstellende Evaluierungen für diese Triade erreichen, können wir eine differenzierte Aussage über die Korrektheit unserer Anwendung treffen; sie ist bis zur Grenze ihrer Wissensbasis als halluzinationsfrei verifiziert. Mit anderen Worten: Wenn die Vektordatenbank nur genaue Informationen enthält, dann sind auch die vom RAG bereitgestellten Antworten genau.
Konkretisierung
Wie bereits erwähnt, können viele der Konfigurationsentscheidungen für unser RAG erhebliche Auswirkungen auf Halluzinationen haben. Um dies zu veranschaulichen, erstellen wir eine RAG-Frage-Antwort-Anwendung auf Basis von Wikipedia-Artikeln zu einer kleinen Gruppe von Städten. LlamaIndex wird als Framework für diese Anwendung dienen.
Folgen Sie diesem Beispiel in Google Colab.
Daten aus Wikipedia laden
Um unseren Vector Store zu erstellen, müssen wir zuerst Daten laden. Hier verwenden wir einen Data Loader von LlamaIndex, um Daten direkt aus Wikipedia zu laden.
from llama_index import WikipediaReader
cities = [
"Los Angeles", "Houston", "Honolulu", "Tucson", "Mexico City",
"Cincinatti", "Chicago"
]
wiki_docs = []
for city in cities:
try:
doc = WikipediaReader().load_data(pages=[city])
wiki_docs.extend(doc)
except Exception as e:
print(f"Error loading page for city {city}: {e}")
Evaluatoren einrichten
Als Nächstes möchten wir unsere Evaluatoren einrichten. Konkret verwenden wir die zuvor erwähnte Triade: Kontextrelevanz, Fundiertheit und Antwortrelevanz, um auf Halluzination zu testen.
TruLens stellt eine Reihe von Evaluatoren oder Feedback-Funktionen mit Prompts bereit, die für diese Evaluierung nützlich sind und einen bestimmten Modellanbieter verwenden, wie OpenAI, Anthropic oder HuggingFace.
# Initialize OpenAI-based feedback function collection class:
openai_gpt4 = feedback.OpenAI()
Nachdem wir unseren Modellanbieter festgelegt haben, wählen wir Frage-Aussage-Relevanz für unsere erste Evaluierung aus. Für jede Evaluierung in diesem Beispiel verwenden wir außerdem Chain-of-Thought-Begründungen, um die Evaluierungen besser zu verstehen. Dies wird durch das Suffix der Feedback-Funktion 1_with_cot_reason gekennzeichnet.
Wenn wir dies tun, müssen wir auch auswählen, welchen Text wir an unsere Feedback-Funktion übergeben. TruLens serialisiert die Anwendung, die dann durch eine JSON-ähnliche Struktur indiziert wird. Wir werden diesen Index für die Textauswahl verwenden. TruLens stellt eine Reihe von Hilfsfunktionen bereit, um dies zu erleichtern:
on_input()findet automatisch die Haupteingabe, die an unsere LlamaIndex-Anwendung übergeben wird, um sie als ersten Text zu verwenden, der an unsere Feedback-Funktion übergeben wird.TruLlama.select_source_nodes()identifiziert die Quellknoten, die in einem LlamaIndex-Retrieval verwendet werden.
Zuletzt müssen wir die Relevanz für jedes Kontextstück zu einer einzigen Bewertung aggregieren. Für dieses Beispiel verwenden wir das Maximum für die Aggregation, um die Relevanz des relevantesten Chunks zu messen. Andere Metriken wie Durchschnitt oder Minimum könnten ebenfalls verwendet werden.
# Question/statement relevance between question and each context chunk.
f_context_relevance = Feedback(openai.qs_relevance_with_cot_reason, name = "Context Relevance").on_input().on(
TruLlama.select_source_nodes().node.text
).aggregate(np.max)
Groundedness wird ähnlich eingerichtet, mit einer etwas anderen Aggregation. In diesem Fall nehmen wir die maximale Groundedness-Bewertung jeder Aussage und anschließend die durchschnittliche Groundedness-Bewertung über alle Aussagen hinweg.
grounded = Groundedness(groundedness_provider=openai_gpt4)
f_groundedness = Feedback(grounded.groundedness_measure_with_cot_reason, name = "Groundedness").on(
TruLlama.select_source_nodes().node.text # context
).on_output().aggregate(grounded.grounded_statements_aggregator)
Antwortrelevanz ist die einfachste Feedback-Funktion, die eingerichtet werden kann, da sie nur auf Eingabe/Ausgabe basiert. Wir können hierfür eine neue TruLens-Hilfsfunktion verwenden — .on_input_output().
# Question/answer relevance between overall question and answer.
f_qa_relevance = Feedback(openai.relevance_with_cot_reason,
name = "Answer Relevance").on_input_output()
Definieren des Konfigurationsraums
Nachdem wir unsere Daten geladen und unsere Evaluatoren eingerichtet haben, ist es an der Zeit, unser RAG zu erstellen. In diesem Prozess werden wir eine Reihe von RAGs mit unterschiedlichen Konfigurationen erstellen, jede bewerten und die beste optimale Wahl auswählen.
Wie bereits angedeutet, beschränken wir unseren Konfigurationsraum auf einige wirkungsvolle Entscheidungen für RAGs. Wir testen in diesem Beispiel Indextyp, Einbettungsmodell, Top k und Chunk-Größe; Sie werden jedoch ermutigt, auch andere Konfigurationen wie verschiedene Distanzmetriken und Suchparameter zu testen.
Iterieren durch unsere Auswahlmöglichkeiten
Nachdem wir den Konfigurationsraum definiert haben, verwenden wir itertools, um jede Kombination dieser Auswahlmöglichkeiten auszuprobieren und jede zu bewerten. Zusätzlich bietet uns Milvus einen schönen Vorteil durch den overwrite-Parameter. Dadurch können wir problemlos durch verschiedene Konfigurationen iterieren, ohne langsame Abbau- und Instanziierungsverfahren, die bei anderen Vektordatenbanken erforderlich sein können.
In jeder Iteration übergeben wir die Auswahl des Indexparameters an MilvusVectorStore und mithilfe des Speicherkontexts an unsere Anwendung. Wir übergeben unser Einbettungsmodell an den Servicekontext und erstellen dann unseren Index.
vector_store = MilvusVectorStore(index_params={
"index_type": index_param,
"metric_type": "L2"
},
search_params={"nprobe": 20},
overwrite=True)
llm = OpenAI(model="gpt-3.5-turbo")
storage_context = StorageContext.from_defaults(vector_store = vector_store)
service_context = ServiceContext.from_defaults(embed_model = embed_model, llm = llm, chunk_size = chunk_size)
index = VectorStoreIndex.from_documents(wiki_docs,
service_context=service_context,
storage_context=storage_context)
Dann können wir mit diesem Index eine Query Engine erstellen — wobei wir hier top_k definieren:
query_engine = index.as_query_engine(similarity_top_k = top_k)
Nach der Erstellung verwenden wir TruLens, um die Anwendung zu wrappen. Hier geben wir ihr einen leicht identifizierbaren Namen, zeichnen die Konfigurationen als App-Metadaten auf und definieren die Feedback-Funktionen für die Bewertung.
tru_query_engine = TruLlama(query_engine,
app_id=f"App-{index_param}-{embed_model_name}-{top_k}",
feedbacks=[f_groundedness, f_qa_relevance, f_context_relevance],
metadata={
'index_param':index_param,
'embed_model':embed_model_name,
'top_k':top_k
})
Diese tru_query_engine funktioniert genauso wie die ursprüngliche Query Engine.
Zuletzt verwenden wir eine kleine Menge von Test-Prompts zur Evaluierung, wobei wir die Anwendung aufrufen, um für jeden Prompt eine Antwort bereitzustellen. Da wir die OpenAI API in schneller Folge aufrufen, ist Tenacity hier nützlich, um uns durch exponentielles Backoff dabei zu helfen, Probleme mit Rate Limits zu vermeiden.
@retry(stop=stop_after_attempt(10), wait=wait_exponential(multiplier=1, min=4, max=10))
def call_tru_query_engine(prompt):
return tru_query_engine.query(prompt)
for prompt in test_prompts:
call_tru_query_engine(prompt)
Die Ergebnisse
Welche Konfiguration hat am besten abgeschnitten?
| Indextyp | Einbettungsmodell | Ähnlichkeit Top k | Chunk-Größe |
|---|---|---|---|
| IVF Flat | text-embedding-ada-002 | 3 | 200 |
Welche Konfiguration hat am schlechtesten abgeschnitten?
| Indextyp | Einbettungsmodell | Ähnlichkeit Top k | Chunk-Größe |
|---|---|---|---|
| IVF Flat | Multilingual MiniLM L12 v2 | 1 | 500 |
Welche Fehlermodi wurden identifiziert?
Ein Fehlermodus, den wir beobachtet haben, war das Abrufen von Informationen über die falsche Stadt. Unten sehen Sie ein Beispiel dafür mit dem Chain-of-Thought-Reasoning, bei dem Kontext zu Tucson statt zu Houston abgerufen wurde.
Ähnlich haben wir auch Probleme gesehen, bei denen wir Kontext zur richtigen Stadt abgerufen haben, der Kontext aber für die Eingabefrage irrelevant war.
Angesichts dieses irrelevanten Kontexts halluzinierte das Completion-Modell anschließend. Es ist wichtig zu beachten, dass Halluzination hier nicht unbedingt sachlich falsch ist; sie liegt einfach dann vor, wenn das Modell ohne stützende Belege antwortet.
Zusätzlich haben wir sogar Beispiele für irrelevante Antworten gefunden.
Leistung verstehen
Nach Indextyp
Der Indextyp hatte keinen bedeutenden Einfluss auf die Leistung in Bezug auf Geschwindigkeit, Token-Nutzung oder Evaluierungen. Dies ist wahrscheinlich auf die geringe Größe der für dieses Beispiel aufgenommenen Daten zurückzuführen, und der Indextyp kann bei größeren Korpora eine wichtigere Auswahl sein.
Nach Einbettungsmodell
Text-embedding-ada-002 übertraf das MiniLM-Einbettungsmodell bei der Groundedness (0,72 gegenüber durchschnittlich 0,60) und der Antwortrelevanz (0,82 gegenüber durchschnittlich 0,62). Die beiden Einbettungsmodelle schnitten bei der Kontextrelevanz gleich gut ab.
Diese verbesserten Evaluierungsergebnisse lassen sich darauf zurückführen, dass OpenAI-Einbettungen besser für Wikipedia-Informationen geeignet sind.
Ähnlichkeit Top K
Eine Erhöhung von top k führte zu einer leicht verbesserten maximalen Abrufqualität (gemessen anhand der Kontextrelevanz). Durch das Abrufen einer größeren Anzahl von Chunks hat der Retriever mehr Versuche, hochwertigen Kontext abzurufen.
Ein höheres top k verbesserte auch die Groundedness (0,71 gegenüber durchschnittlich 0,62) und die Antwortrelevanz (0,76 gegenüber durchschnittlich 0,68). Durch das Abrufen von mehr Kontext-Chunks stellen wir dem Completion-Modell mehr Belege zur Verfügung, um Aussagen zu treffen und zu stützen.
Wie erwartet gehen diese Verbesserungen mit deutlich höherer Token-Nutzung einher (durchschnittlich 590 zusätzliche Tokens pro Aufruf).
Chunk-Größe
Eine Erhöhung der Chunk-Größe verringerte die Groundedness unseres Retrievers, indem sie die Einbeziehung von umgebendem Text erzwang, der für die Eingabefrage irrelevant ist.
Positiv ist, dass eine höhere Chunk-Größe mehr Belege zum Abgleich bereitstellte. Wenn das LLM also Aussagen trifft, ist es wahrscheinlicher, dass sie durch abgerufenen Kontext gestützt werden.
Zuletzt erhöhte eine Erhöhung der Chunk-Größe die durchschnittliche Token-Nutzung um 400 Tokens pro Datensatz.
Ein besseres RAG mit TruLens und Milvus erstellen
In diesem Beitrag haben wir gelernt, wie man ein RAG mit verschiedenen Konfigurationen und Parametern erstellt, einschließlich Indextyp, Einbettungsmodell, Top k und Chunk-Größe. Die große Anzahl unterstützter Konfigurationen und die Unterstützung für Überschreibungen in Milvus ermöglichten dieses dynamische Experimentieren. Entscheidend ist, dass wir auch TruLens verwendet haben, um jedes Experiment zu verfolgen und zu bewerten, neue Fehlermodi zu identifizieren und zu erklären und schnell die leistungsfähigste Kombination zu finden.
Um es selbst auszuprobieren. Du kannst dir Open Source TruLens ansehen und Open Source Milvus oder Zilliz Cloud installieren.
Weiterlesen

Top 10 Context Engineering Techniques You Should Know for Production RAG
A practical guide to context engineering for production LLM systems, covering RAG, context processing, memory, agents, and multimodal context.

Expanding Our Global Reach: Zilliz Cloud Launches in Azure Central India
Zilliz Cloud expands to Azure Central India. This new region helps customers meet compliance, reduce latency, and optimize cloud costs when building AI applications.

DeepSeek-VL2: Mixture-of-Experts Vision-Language Models for Advanced Multimodal Understanding
Explore DeepSeek-VL2, the open-source MoE vision-language model. Discover its architecture, efficient training pipeline, and top-tier performance.



