Direkt zum Inhalt

DeepSeek R1 RAG Chatbot mit Chroma, Ollama und Gradio

Lerne, wie du einen lokalen RAG-Chatbot mit DeepSeek-R1, Ollama, LangChain und Chroma erstellst.
Aktualisierte 11. Feb. 2025  · 12 Min. Lesezeit

Retrieval-augmented Generation (RAG) hat sich als leistungsfähiger Ansatz für die Entwicklung von KI-Anwendungen erwiesen, die präzise, fundierte und kontextbezogene Antworten generieren, indem sie Wissen aus externen Quellen abrufen und zusammenführen.

In diesem Tutorial erkläre ich Schritt für Schritt, wie man einen RAG-basierten Chatbot mit DeepSeek-R1 und einem Buch über die Grundlagen der LLMs als Wissensbasis baut. Am Ende dieses Tutorials wirst du in der Lage sein, eine lokale RAG-Anwendung zu erstellen, die in der Lage ist, Fragen aus dem Buch zu beantworten und mit den Benutzern über eine Gradio-Schnittstelle zu interagieren.

Warum DeepSeek-R1 mit RAG verwenden?

DeepSeek-R1 ist aufgrund seiner optimierten Leistung, der erweiterten Vektorsuchfunktionen und der Flexibilität in verschiedenen Umgebungen - von lokalen Installationen bis hin zu skalierbaren Einsätzen - ideal für RAG-basierte Systeme geeignet. Hier sind einige Gründe, warum sie effektiv ist:

  1. Leistungsstarker Abruf: DeepSeek-R1 verarbeitet große Dokumentensammlungen mit geringer Latenzzeit.
  2. Feinkörniges Relevanz-Ranking: Durch die Berechnung der semantischen Ähnlichkeit wird das genaue Auffinden von Passagen sichergestellt.
  3. Kosten- und Datenschutzvorteile: Du kannst DeepSeek-R1 lokal ausführen ausführen, um API-Gebühren zu vermeiden und sensible Daten zu schützen.
  4. Einfache Integration: Es lässt sich leicht mit Vektordatenbanken wie Chroma.
  5. Offline-Fähigkeiten: Mit DeepSeek-R1 kannst du Retrievalsysteme aufbauen, die auch ohne Internetzugang funktionieren, sobald das Modell heruntergeladen ist.

Überblick: Bau eines RAG Chatbots mit DeepSeek-R1

Unser Demoprojekt konzentriert sich auf den Aufbau eines RAG-Chatbots mit DeepSeek-R1 und Gradio.

RAG Chatbot Pipeline

Der Prozess beginnt mit dem Laden und Zerlegen einer PDF-Datei in Textabschnitte, gefolgt von der Erzeugung von Einbettungen für diese Abschnitte. Diese Einbettungen werden in einer Chroma-Datenbank gespeichert, um sie effizient abrufen zu können. Wenn ein Nutzer eine Anfrage stellt, ruft das System die relevantesten Textabschnitte ab und verwendet DeepSeek-R1, um eine Antwort auf der Grundlage des abgerufenen Kontexts zu generieren.

Schritt 1: Voraussetzungen

Bevor wir beginnen, müssen wir sicherstellen, dass wir die folgenden Tools und Bibliotheken installiert haben:

  • Python 3.8+
  • Langchain
  • Chromadb
  • Gradio

Führe die folgenden Befehle aus, um die notwendigen Abhängigkeiten zu installieren:

!pip install langchain chromadb gradio ollama pymypdf
!pip install -U langchain-community

Sobald die oben genannten Abhängigkeiten installiert sind, führst du die folgenden Importbefehle aus:

import ollama
import re
import gradio as gr
from concurrent.futures import ThreadPoolExecutor
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.embeddings import OllamaEmbeddings
from chromadb.config import Settings
from chromadb import Client
from langchain.vectorstores import Chroma

Schritt 2: Das PDF mit PyMuPDFLoader laden

Wir werden LangChains PyMuPDFLoader nutzen, um den Text aus der PDF-Version des Buches Foundations of LLMs von Tong Xiao und Jingbo Zhu zu extrahieren - es ist ein mathematiklastiges Buch, was bedeutet, dass unser Chatbot in der Lage sein sollte, die Mathematik hinter LLMs gut zu erklären. Du kannst das Buch auf arXiv finden. 

# Load the document using PyMuPDFLoader
loader = PyMuPDFLoader("/path/to/Foundations_of_llms.pdf")

documents = loader.load()

Sobald das Dokument geladen ist, können wir damit beginnen, den Text für die weitere Verarbeitung in Abschnitte zu unterteilen.

Schritt 3: Das Dokument in kleinere Teile aufteilen

Wir teilen den extrahierten Text in kleinere, sich überschneidende Abschnitte auf, um den Kontext besser wiederzufinden. Du kannst die Größe des Chunks und die Überlappung der Chunks in der Funktion RecursiveCharacterTextSpilitter() je nach deinem System variieren.

# Split the document into smaller chunks
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)

chunks = text_splitter.split_documents(documents)

Jetzt haben wir die extrahierten Textabschnitte, die in Einbettungen umgewandelt werden können.

Schritt 4: Einbettungen mit DeepSeek-R1 generieren

Wir verwenden Ollama Embeddings, die auf DeepSeek-R1 basieren, um die Dokumenteneinbettungen zu erzeugen. Je nach Größe des Dokuments kann die Erstellung der Einbettung einige Zeit in Anspruch nehmen, daher ist es besser, sie zu parallelisieren, um die Verarbeitung zu beschleunigen.

Hinweis: model="deepseek-r1" berücksichtigt standardmäßig das 7B-Parameter-Modell. Du kannst ihn je nach Bedarf in 8B, 14B, 32B, 70B oder 671B ändern. Ersetze X im folgenden Modellnamen durch die Modellgröße: model="deepseek-r1:X"

# Initialize Ollama embeddings using DeepSeek-R1
embedding_function = OllamaEmbeddings(model="deepseek-r1")
# Parallelize embedding generation
def generate_embedding(chunk):
    return embedding_function.embed_query(chunk.page_content)
with ThreadPoolExecutor() as executor:
    embeddings = list(executor.map(generate_embedding, chunks))

Die obige Funktion initialisiert DeepSeek-R1 über Ollama, um hochdimensionale semantische Einbettungen zu generieren, die später für die ähnlichheitsorientierte Dokumentensuche verwendet werden.

Die Funktion generate_embedding() nimmt den Text eines Dokumentenstücks und erzeugt dessen Einbettung. Schließlich wendet ThreadPoolExecutor() generate_embedding() auf jeden Chunk gleichzeitig an und sammelt die Einbettungen in einer Liste, um sie schneller zu verarbeiten als bei der sequentiellen Ausführung.

Schritt 5: Einbettungen im Chroma-Vektor-Speicher speichern

Wir speichern die Einbettungen und die entsprechenden Textabschnitte in einer leistungsstarken Vektordatenbank, Chroma.

# Initialize Chroma client and create/reset the collection
client = Client(Settings())
client.delete_collection(name="foundations_of_llms")  # Delete existing collection (if any)
collection = client.create_collection(name="foundations_of_llms")
# Add documents and embeddings to Chroma
for idx, chunk in enumerate(chunks):
    collection.add(
        documents=[chunk.page_content], 
        metadatas=[{'id': idx}], 
        embeddings=[embeddings[idx]], 
        ids=[str(idx)]  # Ensure IDs are strings
    )

Wir beginnen mit den folgenden Schritten, um Einbettungen zu speichern:

1. Initialisiere den Chroma-Client und setze die Sammlung zurück:

  • Die Client(Settings()) initialisiert den Chroma-Client, um den Vektorspeicher zu verwalten. 
  • Lösche alle bestehenden Sammlungen, die deinem Sammlungsnamen ähneln, mit client.delete_collection(), um Fehler zu vermeiden. Zum Schluss erstellst du mit client.create_collection() eine neue Sammlung, in der du die Dokumentenstücke und ihre Einbettungen speicherst.

2. Iteriere durch Dokumentenabschnitte:

  • Iteriere über jedes Dokumentenstück und die dazugehörige Einbettung mit Hilfe seiner eindeutigen String-ID.

3. Füge Chunks und Embeddings zu Chroma hinzu:

  • Für jeden Chunk speichert collection.add():
    • Die Inhalt des Dokuments (chunk.page_content)
    • Metadaten ({'id': idx}), um den Chunk zu referenzieren
    • Sein entsprechender Einbettungsvektor für den Abruf
    • A einzigartige ID String zur Identifizierung des Eintrags

Auf diese Weise wird sichergestellt, dass jedes Dokument korrekt indiziert wird, um eine effiziente vektorbasierte Suche zu ermöglichen.

Schritt 6: Initialisiere den Retriever

Wir initialisieren den Chroma-Retriever und stellen sicher, dass er die gleichen DeepSeek-R1-Einbettungen für Abfragen verwendet.

# Initialize retriever using Ollama embeddings for queries
retriever = Chroma(collection_name="foundations_of_llms", client=client, embedding_function=embedding_function).as_retriever()

Der Chroma-Retriever verbindet sich mit der Sammlung "foundations_of_llms" und verwendet DeepSeek-R1-Einbettungen über Ollama, um Benutzeranfragen einzubetten. Es findet die relevantesten Dokumententeile auf der Basis von Vektorähnlichkeit für kontextbezogene Antworten.

Schritt 7: Definiere die RAG-Pipeline

Als Nächstes suchen wir die wichtigsten Textabschnitte heraus und formatieren sie für DeepSeek-R1, um Antworten zu generieren.

def retrieve_context(question):
    # Retrieve relevant documents
    results = retriever.invoke(question)
    # Combine the retrieved content
    context = "\n\n".join([doc.page_content for doc in results])
    return context

Die Funktion retrieve_context bettet die Benutzerabfrage mit DeepSeek-R1 ein und ruft die wichtigsten relevanten Dokumentenstücke über den Chroma Retriever ab. Dann kombiniert es den Inhalt der abgerufenen Chunks zu einem einzigen Kontextstring für die weitere Verarbeitung.

Schritt 8: Abfrage von DeepSeek-R1 für kontextbezogene Antworten

Jetzt haben wir die Frage und den gesuchten Kontext. Als Nächstes schickst du sie über Ollama an DeepSeek-R1, um unsere endgültige Antwort zu erhalten.

def query_deepseek(question, context):
    # Format the input prompt
    formatted_prompt = f"Question: {question}\n\nContext: {context}"
    # Query DeepSeek-R1 using Ollama
    response = embedding_function.chat(
        model="deepseek-r1",
        messages=[{'role': 'user', 'content': formatted_prompt}]
    )
    # Clean and return the response
    response_content = response['message']['content']
    final_answer = re.sub(r'<think>.*?</think>', '', response_content, flags=re.DOTALL).strip()
    return final_answer

Um die endgültige Antwort zu erhalten, kombinieren wir zunächst die Frage des Nutzers und den ermittelten Kontext zu einer strukturierten Aufforderung. Sende diese Aufforderung dann über Ollama an das DeepSeek-R1-Modell, um eine Antwort zu erhalten. Um die endgültige Ausgabe vorzeigbar zu machen, entfernen wir unnötige Tags und geben die endgültige Antwort zurück.

Schritt 9: Erstelle die Gradio-Schnittstelle

Wir haben unsere RAG-Pipeline eingerichtet. Jetzt werden wir Gradio nutzen, um eine interaktive Schnittstelle zu schaffen, über die Nutzer Fragen zur Wissensbasis stellen können (in diesem Fall zu den Grundlagen des LLM).

def ask_question(question):
    # Retrieve context and generate an answer using RAG
    context = retrieve_context(question)
    answer = query_deepseek(question, context)
    return answer
# Set up the Gradio interface
interface = gr.Interface(
    fn=ask_question,
    inputs="text",
    outputs="text",
    title="RAG Chatbot: Foundations of LLMs",
    description="Ask any question about the Foundations of LLMs book. Powered by DeepSeek-R1."
)
interface.launch()

Die Funktion ask_question() ruft mithilfe des Chroma-Retrievers den relevanten Kontext ab und generiert die endgültige Antwort über DeepSeek-R1. Die Gradio-Benutzeroberfläche, die mit gr.Interface() erstellt wurde, ermöglicht es den Nutzern, interaktiv Fragen zu stellen und kontextgenaue, fundierte Antworten zu erhalten.

Herzlichen Glückwunsch! Du hast jetzt einen lokal laufenden Chatbot, mit dem du über alles diskutieren kannst, was mit LLMs zu tun hat.

RAG-Anwendung mit DeekSeek-R1 und Gradio

Optimierungen

Die obige Demo zeigt eine sehr einfache Implementierung von RAG, die weiter optimiert werden kann, um effizienter zu werden. Hier sind ein paar Dinge zum Ausprobieren:

  • Anpassung der Brockengröße: Passe die Parameter chunk_size und chunk_overlap an, um ein Gleichgewicht zwischen Leistung und Abrufqualität herzustellen.
  • Kleinere Modellversionen: Wenn DeepSeek-R1 zu ressourcenintensiv ist, kannst du andere Versionen (deepseek-r1:7b oder deepseek-r1:8b oder deepseek-r1:14b) über Ollama verwenden.
  • Skaliere mit Faiss: Für größere Dokumente solltest du die Integration von Faiss für ein schnelleres Auffinden.
  • Stapelverarbeitung: Wenn das Einbetten langsam ist, kannst du die Chunks stapeln, um die Effizienz zu verbessern.

Fazit

In diesem Tutorial haben wir einen RAG-basierten lokalen Chatbot gebaut, der DeepSeek-R1 und Chroma für das Retrieval nutzt und auf der Grundlage einer großen Wissensdatenbank genaue, kontextreiche Antworten auf Fragen gibt.

Um mehr über DeepSeek zu erfahren, empfehle ich diese Blogs:


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Ich bin ein Google Developers Expert in ML (Gen AI), ein Kaggle 3x Expert und ein Women Techmakers Ambassador mit mehr als 3 Jahren Erfahrung im Tech-Bereich. Ich habe 2020 ein Startup im Bereich Gesundheitstechnologie mitbegründet und mache einen Master in Informatik an der Georgia Tech, der sich auf maschinelles Lernen spezialisiert.

Themen

Lerne KI mit diesen Kursen!

Kurs

Retrieval Augmented Generation (RAG) with LangChain

3 hr
2.9K
Learn cutting-edge methods for integrating external data with LLMs using Retrieval Augmented Generation (RAG) with LangChain.
Siehe DetailsRight Arrow
Kurs starten
Mehr anzeigenRight Arrow
Verwandt

Der Blog

Top 30 Generative KI Interview Fragen und Antworten für 2024

Dieser Blog bietet eine umfassende Sammlung von Fragen und Antworten zu generativen KI-Interviews, die von grundlegenden Konzepten bis hin zu fortgeschrittenen Themen reichen.
Hesam Sheikh Hassani's photo

Hesam Sheikh Hassani

15 Min.

Der Blog

Die 20 besten Snowflake-Interview-Fragen für alle Niveaus

Bist du gerade auf der Suche nach einem Job, der Snowflake nutzt? Bereite dich mit diesen 20 besten Snowflake-Interview-Fragen vor, damit du den Job bekommst!
Nisha Arya Ahmed's photo

Nisha Arya Ahmed

20 Min.

Der Blog

Lehrer/innen und Schüler/innen erhalten das Premium DataCamp kostenlos für ihre gesamte akademische Laufbahn

Keine Hacks, keine Tricks. Schüler/innen und Lehrer/innen, lest weiter, um zu erfahren, wie ihr die Datenerziehung, die euch zusteht, kostenlos bekommen könnt.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

4 Min.

Der Blog

Q2 2023 DataCamp Donates Digest

DataCamp Donates hat im zweiten Quartal 2023 über 20.000 Stipendien an unsere gemeinnützigen Partner vergeben. Erfahre, wie fleißige benachteiligte Lernende diese Chancen in lebensverändernde berufliche Erfolge verwandelt haben.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

Der Blog

2022-2023 DataCamp Classrooms Jahresbericht

Zu Beginn des neuen Schuljahres ist DataCamp Classrooms motivierter denn je, das Lernen mit Daten zu demokratisieren. In den letzten 12 Monaten sind über 7.650 neue Klassenzimmer hinzugekommen.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

8 Min.

Mehr anzeigenMehr anzeigen