Vollständig lokales PDF-RAG mit LangChain, Ollama und Chroma
Eine vollständig lokale PDF-RAG-App: LangChain verarbeitet die Dateien, Ollama integriert sie, Chroma speichert die Vektoren, und die Antworten funktionieren offline ohne jegliche Cloud-API-Schlüssel.
Quellen: Implement RAG with Langchain and local Ollama models
Bauen Sie einen vollständig lokalen RAG-Pipeline für PDFs mit LangChain, Ollama und ChromaDB – chatten Sie mit Dokumenten, ohne Cloud-APIs oder Datenverkehr außerhalb des Geräts.
Warum Lokalität wichtig ist
Cloud-basierte LLMs sind zwar praktisch, doch jede Abfrage überträgt Text über das Netzwerk eines Dritten. Rechtverträge, medizinische Unterlagen und interne Berichte können oft diesen Kompromiss nicht akzeptieren. Ein lokaler Stack verhindert Datenlecks und ermöglicht gleichzeitig natursprachige Fragen und Antworten anhand eigener Dateien.
Was Sie erhalten
- Einlesen von PDF-Dateien in einen persistenten Vektor-Speicher
- Antworten auf natursprachige Fragen, die auf diesen Dokumenten beruhen
- Ausführen im Offline-Modus zur Inferenz – keine API-Schlüssel mehr erforderlich, sobald die Modelle heruntergeladen wurden
Stack: LangChain zur Orchestrierung, Ollama für den lokalen Chat und Embeddings, ChromaDB als Vektordatenspeicher.
Architektur
Eingabepfad: PDF-Bytes gelangen in einen auf PyPDF basierenden Lader, werden in Blöcke unterteilt, anschließend embeddet Ollama jeden Block in Chroma.
Fragepfad: Eine Frage wird an den Retriever gesendet, passende Blöcke werden zum Kontext, und das lokale Ollama-Chat-Modell schreibt die Antwort.
Zwei Phasen sorgen für transparente Kosten:
- Eingabe — einmal (oder wenn Dokumente sich ändern): Parsen, in Blöcke unterteilen, embedden, speichern
- Auswertung — bei jeder Anfrage: Die relevantesten Blöcke werden zum Kontext für das LLM
Man zahlt nur bei Inhaltsschwankungen für Embeddings; interaktive Anfragen bleiben schnell.
Voraussetzungen
Installieren Sie Ollama und laden Sie die hier verwendeten Modelle herunter:
ollama pull embeddinggemma:latest # embedding model
ollama pull gemma4:latest # chat model
gemma4 ist auf spezifische Anweisungen abgestimmt. Für einen geringeren Ressourcenverbrauch versuchen Sie gemma2:2b; für stärkere Schlussfolgerungen verwenden Sie gemma4:27b – passen Sie die Konfigurationsstrings entsprechend an.
Projekteinrichtung
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Wichtige Pakete aus den Anforderungen:
langchain
langchain-chroma
langchain-community
langchain-ollama
langchain-text-splitters
pypdf
chromadb
python-dotenv
Überschreiben Sie die Standardwerte über die Umgebung, ohne den Code zu ändern:
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=gemma4:latest
OLLAMA_EMBED_MODEL=embeddinggemma:latest
Eingabepipeline (src/ingest.py)
Dokumente laden
def load_documents(pdf_path: str | Path) -> list:
path = Path(pdf_path)
loader = (
PyPDFLoader(str(path))
if path.is_file()
else PyPDFDirectoryLoader(str(path))
)
return loader.load()py
Ein Hilfsprogramm kümmert sich entweder um eine einzelne Datei oder einen Ordner. Die LangChain-Lader liefern in jedem Fall immer eine einheitliche Liste von Document-Objekten.
In Blöcke aufteilen
def split_documents(documents: list) -> list:
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
)
return splitter.split_documents(documents)
RecursiveCharacterTextSplitter bevorzugt zunächst Absätze, anschließend Sätze und danach Worttrennungen, damit semantische Grenzen erhalten bleiben. Eine Überschneidung von 200 Zeichen sorgt dafür, dass ein Satz, der eine Grenze überschreitet, in beiden benachbarten Blöcken vorhanden bleibt, was Fehlerraten an den Rändern verringert.
Einfügen und speichern
def build_vector_store(chunks: list) -> Chroma:
embeddings = OllamaEmbeddings(model=EMBEDDING_MODEL, base_url=OLLAMA_BASE_URL)
VECTOR_STORE_DIR.mkdir(parents=True, exist_ok=True)
return Chroma.from_documents(
documents=chunks,
embedding=embeddings,
collection_name=COLLECTION_NAME,
persist_directory=str(VECTOR_STORE_DIR),
)
Ollama stellt Embeddings lokal über Port 11434 zur Verfügung. Chroma speichert sie unter vector_store/ (SQLite zusammen mit HNSW-Indexdateien), sodass ein Neustart des Prozesses keine vollständige Neu-Einbettung erfordert.
Auffindungskette (src/retrieval.py)
LangChain LCEL kombiniert Schritte mithilfe des Operators |.
Prompt-Vorlage
RAG_PROMPT = ChatPromptTemplate.from_messages([
(
"system",
"You are an assistant that answers questions based on the provided context "
"from PDF documents. Use only the information in the context to answer. "
"If the answer is not in the context, say so clearly.\n\nContext:\n{context}"
),
("human", "{question}"),
])
Die Systemnachricht verankert die Antworten im abgerufenen Kontext – dies ist der wesentliche Schutzmechanismus von RAG gegen willkürliche Halluzinationen, die allein durch das Vortrainieren entstehen könnten.
Die Kette erstellen
def build_rag_chain(k: int = 4):
store = load_vector_store()
retriever = store.as_retriever(search_kwargs={"k": k})
llm = ChatOllama(model=MODEL_NAME, base_url=OLLAMA_BASE_URL)
return (
{"context": retriever | _format_docs, "question": RunnablePassthrough()}
| RAG_PROMPT
| llm
| StrOutputParser()
)
Von links nach rechts: Der Retriever wählt die k am ähnlichstenen Fragmente mithilfe der Embedding-Ähnlichkeit aus, Hilfsprogramme formatieren diese Fragmente zu einem einzigen Kontextstring, das Prompt-Muster fügt den Kontext zur Benutzerfrage hinzu, und ChatOllama erzeugt die Antwort.
Interaktiver Abfrageschleifenmodus
# Index a directory of PDFs (default: data/pdfs/)
python main.py ingest
Eine einfache CLI (oder HTTP-Wrapper) lädt die Kette einmal hoch und beantwortet Fragen, bis Sie beenden. Halten Sie k anpassbar, damit Sie zwischen Größe des Kontexts und Latenzzeit abwägen können.
Vollständige Ausführung vom Anfang bis zum Ende
# Index a specific file
python main.py ingest --path /path/to/contract.pdf
# Ask a one-off question
python main.py query "What are the payment terms?"
# Interactive session
python main.py chat
Weisen Sie die Eingabe auf Ihren PDF-Ordner hin, überprüfen Sie, dass der Verzeichnis für den Vektorstore gefüllt ist, und führen Sie anschließend Abfragen durch. Alles – Embeddings, Vektoren, Generierung – bleibt auf dem lokalen Rechner.
Fazit
Lokaler RAG ist nicht mehr etwas Exotisches. Ollama stellt die Modelle bereit, Chroma speichert Vektoren, und LangChain verbindet das Graphen. Für private Korpora reicht dieses Trio aus, um einen nützlichen Dokumentenassistenten ohne Cloud-Kosten oder externe Datenschnittstellen bereitzustellen.