Wie man in 10 Zeilen Python ohne Frameworks ein RAG-System erstellt

Erschließen Sie die Kraft von RAG (Retrieval Augmented Generation) mit diesem 10-zeiligen Python-Code-Tutorial. Tauchen Sie in die Informationssuche ein, bauen Sie Ihr eigenes Dokumentensystem auf und nutzen Sie LLMs für robuste Antworten - alles ohne komplexe Frameworks. Optimieren Sie Ihren Inhalt für SEO und Engagement.

15. Februar 2025

party-gif

Erfahren Sie, wie Sie ein leistungsfähiges Retrieval-Augmented-Generation-(RAG)-System in nur 10 Zeilen Python-Code von Grund auf aufbauen können, ohne sich auf externe Frameworks zu verlassen. Dieser prägnante, aber umfassende Ansatz vermittelt Ihnen ein tiefes Verständnis der Kernkomponenten und befähigt Sie, robuste und anpassbare KI-gestützte Lösungen für Ihre dokumentenbasierten Anwendungen zu schaffen.

Verstehen Sie das Konzept der Retrieval Augmented Generation (RAG)

Retrieval Augmented Generation (RAG) ist eine Technik, die zur Informationssuche in Ihren eigenen Dokumenten verwendet wird. Sie beinhaltet einen zweistufigen Prozess:

  1. Erstellung einer Wissensbasis: Die Dokumente werden in kleinere Teilabschnitte unterteilt, und für jeden Teilabschnitt werden Einbettungen berechnet. Diese Einbettungen und die ursprünglichen Teilabschnitte werden in der Regel in einem Vektorenspeicher gespeichert.

  2. Suche und Generierung: Wenn eine neue Benutzerabfrage eingeht, wird eine Einbettung für die Abfrage berechnet. Die relevantesten Teilabschnitte werden durch Vergleich der Abfrageeinbettung mit den gespeicherten Teilabschnittseinbettungen abgerufen. Die abgerufenen Teilabschnitte werden dann an die ursprüngliche Abfrage angehängt und in ein Sprachmodell (LLM) eingegeben, um die endgültige Antwort zu generieren.

Diese Konfiguration wird als "retrieval-augmented generation"-Pipeline bezeichnet, da die Generierung des LLM durch die aus der Wissensbasis abgerufenen relevanten Informationen erweitert wird.

Obwohl es Frameworks wie Langchain und LlamaIndex gibt, die die Implementierung von RAG-Pipelines vereinfachen, können die Kernkomponenten allein mit Python, einem Einbettungsmodell und einem LLM aufgebaut werden. Dieser Ansatz bietet ein besseres Verständnis der zugrunde liegenden Konzepte und ermöglicht mehr Flexibilität bei der Anpassung der Pipeline.

Lernen Sie, Dokumente in Absätze zu unterteilen

Beim Aufbau eines retrieval-augmented generation (RAG)-Systems ist einer der Schlüsselschritte das Unterteilen der Eingabedokumente in kleinere, besser handhabbare Teile. In diesem Beispiel unterteilen wir das Eingabedokument (einen Wikipedia-Artikel) in Absätze.

Die Begründung für diesen Ansatz ist, dass Absätze oft logische Informationseinheiten darstellen, die effektiv abgerufen und zur Erweiterung der Benutzerabfrage verwendet werden können. Durch die Aufteilung des Dokuments auf diese Weise können wir die relevantesten Abschnitte für die endgültige Antwort besser identifizieren.

Der Code für diesen Schritt sieht wie folgt aus:

# Unterteile das Dokument in Absätze
chunks = [paragraph.strip() for paragraph in text.split('\n') if paragraph.strip()]

Hier verwenden wir einen einfachen Ansatz, bei dem wir den Eingabetext an Zeilenumbrüchen aufteilen, um die einzelnen Absätze zu extrahieren. Wir entfernen dann alle führenden oder nachfolgenden Leerzeichen aus jedem Absatz, um eine saubere Darstellung zu gewährleisten.

Die resultierende chunks-Liste enthält die einzelnen Absätze, die dann eingebettet und im Vektorenspeicher gespeichert werden können. Dies ermöglicht es uns, die relevantesten Absätze basierend auf der Benutzerabfrage effizient abzurufen und in die endgültige, vom Sprachmodell generierte Antwort aufzunehmen.

Betten Sie Dokumente und Benutzerabfragen mit Sentence Transformers ein

Um die Dokumentenabschnitte und die Benutzerabfrage einzubetten, verwenden wir die Sentence Transformers-Bibliothek. Diese Bibliothek stellt vorgefertigte Modelle bereit, die hochwertige Einbettungen für Text generieren können.

Zuerst laden wir das Einbettungsmodell:

from sentence_transformers import SentenceTransformer
embedding_model = SentenceTransformer('all-mpnet-base-v2')

Als Nächstes berechnen wir die Einbettungen für die Dokumentenabschnitte:

chunk_embeddings = embedding_model.encode(chunks, normalize_embeddings=True)

Hier ist chunks eine Liste der Textabschnitte aus dem Dokument. Die Option normalize_embeddings=True stellt sicher, dass die Einbettungen normalisiert werden, was für die spätere Ähnlichkeitsberechnung wichtig ist.

Um die Einbettung für die Benutzerabfrage zu erhalten, führen wir einfach aus:

query_embedding = embedding_model.encode([user_query], normalize_embeddings=True)[0]

Nun haben wir die Einbettungen für die Dokumentenabschnitte und die Benutzerabfrage, die wir für den Abrufschritt verwenden können.

Rufen Sie relevante Chunks basierend auf der Kosinusähnlichkeit ab

Um die relevantesten Abschnitte basierend auf der Benutzerabfrage abzurufen, müssen wir zunächst den Kosinusähnlichkeitswert zwischen der Abfrageeinbettung und den Einbettungen der Dokumentenabschnitte berechnen. Hier ist, wie wir das machen können:

  1. Berechnen Sie die Einbettung der Benutzerabfrage mit demselben Einbettungsmodell wie die Dokumentenabschnitte.
  2. Berechnen Sie das Skalarprodukt zwischen der Abfrageeinbettung und jeder der Dokumentenabschnittseinbettungen. Dies ergibt die Kosinusähnlichkeitswerte.
  3. Sortieren Sie die Dokumentenabschnitte basierend auf den Kosinusähnlichkeitswerten und wählen Sie die K relevantesten Abschnitte aus.
  4. Rufen Sie den Textinhalt der ausgewählten Abschnitte ab, um ihn als Kontext für das Sprachmodell zu verwenden.

Die Schlüsselschritte sind:

  1. Berechnen der Abfrageeinbettung
  2. Berechnen der Kosinusähnlichkeitswerte
  3. Sortieren und Auswählen der Top-K-Abschnitte
  4. Abrufen des Textinhalts der Abschnitte

Dieser einfache Ansatz ermöglicht es uns, die relevantesten Informationen aus der Dokumentensammlung effizient abzurufen, um die Benutzerabfrage zu erweitern, bevor sie an das Sprachmodell für die Generierung weitergegeben wird.

Erweitern Sie die Benutzerabfrage mit abgerufenen Chunks und generieren Sie eine Antwort mit OpenAI GPT-4

Um eine Antwort unter Verwendung der abgerufenen Abschnitte und der Benutzerabfrage zu generieren, erstellen wir zunächst eine Eingabeaufforderung, die die relevanten Abschnitte und die Benutzerabfrage enthält. Anschließend verwenden wir das OpenAI GPT-4-Modell, um eine Antwort basierend auf dieser Eingabeaufforderung zu generieren.

Hier ist der Code:

# Holen Sie sich die Benutzerabfrage
user_query = "Was ist die Hauptstadt von Frankreich?"

# Rufen Sie die relevantesten Abschnitte basierend auf der Benutzerabfrage ab
top_chunk_ids = [6, 8, 5]
top_chunks = [chunks[i] for i in top_chunk_ids]

# Erstellen Sie die Eingabeaufforderung
prompt = "Verwenden Sie den folgenden Kontext, um die Frage am Ende zu beantworten. Wenn Sie die Antwort nicht wissen, sagen Sie das und versuchen Sie nicht, eine Antwort zu erfinden.\n\nKontext:\n"
for chunk in top_chunks:
    prompt += chunk + "\n\n"
prompt += f"\nFrage: {user_query}"

# Generieren Sie die Antwort mit OpenAI GPT-4
response = openai_client.chat.create(
    model="gpt-4",
    messages=[
        {"role": "user", "content": prompt}
    ],
    max_tokens=1024,
    n=1,
    stop=None,
    temperature=0.7,
).choices[0].message.content

print(response)

In diesem Code rufen wir zunächst die relevantesten Abschnitte basierend auf der Benutzerabfrage ab, indem wir die 3 Abschnitte mit den höchsten Ähnlichkeitswerten finden. Wir erstellen dann eine Eingabeaufforderung, die diese Abschnitte und die Benutzerabfrage enthält. Schließlich verwenden wir das OpenAI GPT-4-Modell, um eine Antwort basierend auf dieser Eingabeaufforderung zu generieren.

Die generierte Antwort wird eine prägnante und relevante Antwort auf die Benutzerabfrage sein, die die Informationen aus den abgerufenen Abschnitten nutzt.

Schlussfolgerung

In diesem Tutorial haben wir gelernt, wie man mit nur 10 Zeilen Python-Code ein vollständiges Chatsystem mit einem Dokumentenabfragesystem aufbaut. Wir haben die Schlüsselkomponenten einer Retrieval Augmented Generation (RAG)-Pipeline behandelt, einschließlich der Erstellung einer Wissensbasis, der Dokumentenunterteilung, der Einbettung, des Abrufs und der Generierung unter Verwendung eines großen Sprachmodells.

Die wichtigsten Erkenntnisse sind:

  1. Sie können eine grundlegende RAG-Pipeline implementieren, ohne auf komplexe Frameworks wie Langchain oder LlamaIndex angewiesen zu sein. Reines Python und einige Bibliotheken reichen aus.
  2. Das Unterteilen Ihrer Dokumente basierend auf der Struktur (z.B. Absätze) ist eine einfache, aber effektive Strategie für die meisten Anwendungsfälle.
  3. Das Einbetten der Dokumentenabschnitte und der Benutzerabfrage und anschließende Berechnen der Ähnlichkeitswerte ermöglicht es Ihnen, die relevantesten Informationen zur Erweiterung der Benutzerabfrage abzurufen.
  4. Das Integrieren der abgerufenen Abschnitte mit der Benutzerabfrage und das Einspeisen in ein großes Sprachmodell ermöglicht die Generierung einer relevanten und informativ en Antwort.

Während dieses Beispiel eine solide Grundlage bietet, gibt es viele Möglichkeiten, robustere und fortgeschrittenere RAG-Systeme aufzubauen. Frameworks wie Langchain und LlamaIndex können hilfreich sein, wenn es um die Integration mit verschiedenen Vektorenspeichern und Sprachmodellen geht. Ein Einstieg mit einer reinen Python-Implementierung kann Ihnen jedoch ein besseres Verständnis der Kernkonzepte und -komponenten einer RAG-Pipeline vermitteln.

Wenn Sie an der Erkundung fortgeschrittener RAG-Techniken interessiert sind, empfehle ich Ihnen, einen Blick auf meinen Kurs "RAG Beyond Basics" zu werfen, der sich eingehender mit dem Aufbau komplexer, produktionsreifer RAG-Systeme befasst.

FAQ