Mittelstufe 11 Min.Stack

RAG mit ChromaDB und Mistral

Direkte Antwort

Für ein lokales RAG mit Mistral besteht der einfachste technische Stack aus Ollama (Generierung und Embeddings mit bge-m3) und ChromaDB im Dateimodus, ohne Server oder PyTorch. Bei den Modellen ist ministral-3:8b (6,0 GB, Apache-2.0-Lizenz, angekündigter Kontext von 256K) eine gute Standardwahl für eine Grafikkarte mit 8 bis 12 GB, ministral-3:14b für 16 GB und mistral-small3.2:24b (15 GB) für mehr Speicher. Eine Einstellung sollten Sie nicht vergessen: das Kontextfenster von Ollama vergrößern, damit die Textpassagen in den Prompt passen.

Dieser Leitfaden zeigt, wie Sie mit zwei Python-Skripten und einem auf Ihrem Rechner ausgeführten Mistral-Modell einen vollständigen Dokumentenassistenten erstellen: Ihre PDF- und Textdateien werden in Abschnitte zerlegt und in ChromaDB indiziert. Anschließend werden die gefundenen Passagen an das Modell übergeben, das unter Angabe seiner Quellen antwortet. Der Leitfaden erläutert auch, welches Mistral-Modell Sie je nach verfügbarem Grafikspeicher wählen sollten und welche Fallstricke dazu führen, dass ein RAG an der Frage vorbe antwortet.

Von Mohamed Meguedmi·Aktualisierung 2026-09-30·Unter Windows, macOS und Linux getestet

#Was wir bauen: ein vollständig lokales RAG-System mit Mistral

RAG (durch Retrieval erweiterte Generierung) besteht darin, die Passagen Ihrer Dokumente zu finden, die zur Frage passen, und sie anschließend in den Prompt des Modells einzufügen, damit es auf ihrer Grundlage antwortet. Das angestrebte Ergebnis ist hier ein kleines Kommandozeilenwerkzeug: Ein Skript indexiert einen Dokumentenordner; ein zweites liest eine Frage ein, findet die fünf ähnlichsten Passagen in ChromaDB, übergibt sie zusammen mit der Frage über Ollama an ein Mistral-Modell und zeigt die Antwort an, gefolgt von den herangezogenen Dateien. Nichts verlässt den Rechner: Ollama stellt das Generierungsmodell und das Embedding-Modell bereit, ChromaDB speichert die Vektoren in einem lokalen Ordner.

Der Begriff „Mistral“ wird in zwei Bedeutungen verwendet: für die Modelle von Mistral AI mit offen verfügbaren Gewichten, die man selbst herunterlädt und ausführt (Gegenstand dieses Leitfadens), und für die gehosteten APIs des Unternehmens, die Ihre Textpassagen an dessen Server senden. Bei vertraulichen Dokumenten erfüllt nur die erste Variante die Anforderung „100 % lokal“.

#Welches Mistral-Modell für RAG wählen?

Das RAG-Local-Kit

Ihre Dokumente, Ihre KI: ein zuverlässiges lokales RAG für Ihre PDFs, Notizen und E-Mails – ohne Daten in die Cloud zu senden.

  • Lebenslanger Online-Zugang
  • PDF + Dateien
  • Erstattung binnen 30 Tagen

Ein RAG hat besondere Anforderungen: Das Modell muss eine strenge Anweisung befolgen („Antworte ausschließlich auf Grundlage der Textpassagen“), mehrere Textpassagen lesen, ohne den Überblick zu verlieren, und auf Französisch antworten. Die Größe spielt eine geringere Rolle als bei freier Konversation; dafür ist der für den Kontext verfügbare Speicher wichtiger. Die folgenden Größen entsprechen den Angaben in der Ollama-Modellbibliothek bei der Standardquantisierung.

Mistral-Modelle in der Ollama-Bibliothek (Stand September 2026)
ModellGröße in OllamaAngegebene KontextlängeFür wen
ministral-3:3b3,0 GB256KRechner ohne dedizierte GPU; einfache Antworten, komplexe Anweisungen werden nur eingeschränkt bewältigt
ministral-3:8b6,0 GB256KSinnvolle Standardwahl für eine Grafikkarte mit 8 bis 12 GB oder einen Laptop mit 16 GB Arbeitsspeicher
ministral-3:14b9,1 GB256KKarte mit 16 GB, oder 12 GB mit moderatem Kontext
mistral-nemo (12B)siehe die Ollama-Seite128KÄltere Alternative, noch weit verbreitet
mistral-small3.2:24b15 GB128KGrafikkarte mit 24 GB oder Unified Memory mit mindestens 32 GB; das zuverlässigste Modell bei der Einhaltung von Formatvorgaben
mistral (7B, Version 0.3)4,4 GB32KÄlteres Modell: nur für Rechner mit sehr begrenzten Ressourcen

Die Modellfamilie Ministral 3 (3B, 8B und 14B) wird unter der Apache-2.0-Lizenz veröffentlicht, wie die Ankündigung von Mistral 3 angibt. Die Ollama-Seite beschreibt sie als für den Einsatz am Netzwerkrand konzipiert und auf einer breiten Palette von Hardware lauffähig. Mistral Small 4, 2026 veröffentlicht und laut dem Namen auf seiner Hugging-Face-Modellseite mit insgesamt 119 Milliarden Parametern, ist auf Serverhardware ausgelegt: Für einen persönlichen Rechner kommt es nicht infrage. Für eine Größenordnung des benötigten Speichers gibt der VRAM-Rechner dieser Website den Speicherbedarf des Modells zuzüglich des Kontextcaches an.

i
Angegebene und nutzbare Kontextlänge
Ein Kontext von 128K oder 256K bezeichnet die maximale Kapazität des Modells, keine Einstellung: Ollama verwendet standardmäßig viel weniger, und ein großer Kontext benötigt zusätzlichen Speicher. Für ein RAG mit fünf Textpassagen reichen 8.000 Tokens aus.

#Der technische Stack

Generierung
Ein Mistral-Modell, das von Ollama über die lokale HTTP-API auf Port 11434 bereitgestellt wird.
Embeddings
bge-m3, über Ollama bereitgestellt: Die Seite in der Modellbibliothek beschreibt es als vielseitiges, mehrsprachiges Modell von BAAI mit unterschiedlichen Granularitätsstufen und 567 Millionen Parametern. Damit entfällt die Installation von PyTorch und sentence-transformers.
Vektordatenbank
ChromaDB im lokalen Modus (PersistentClient): ein Ordner, kein Server. Chroma stellt einen Wrapper namens OllamaEmbeddingFunction bereit, der die Embedding-API von Ollama aufruft.
Datei-Lesefunktion
pypdf für PDF-Dateien mit Text, direktes Einlesen für Markdown und Klartext. Eine gescannte PDF-Datei ist ein Bild: Zunächst ist eine Zeichenerkennung erforderlich.

#Umgebung vorbereiten

  1. 01
    Ollama installieren und die Modelle herunterladen
    Installieren Sie Ollama und laden Sie anschließend das Generationsmodell und das Embedding-Modell mit den beiden folgenden Befehlen herunter.
  2. 02
    Python-Umgebung erstellen
    Python 3.10 oder höher. Eine virtuelle Umgebung hält die Abhängigkeiten des Projekts getrennt.
  3. 03
    Dokumente platzieren
    Kopieren Sie Ihre PDF-, Markdown- und Textdateien in einen Ordner docs/ neben den Skripten.
Modelle und Abhängigkeiten
ollama pull ministral-3:8b
ollama pull bge-m3

mkdir mon-rag && cd mon-rag
python3 -m venv venv
source venv/bin/activate   # .\venv\Scripts\activate sous Windows
pip install chromadb pypdf requests

#2. Dokumente in ChromaDB indexieren

Das Skript liest jede Datei, teilt den Text an Absatzgrenzen in Abschnitte von etwa 1.800 Zeichen auf und übergibt sie anschließend an Chroma, das bge-m3 über Ollama aufruft, um die Vektoren zu berechnen. Zwei Details sind wichtig: Jeder Abschnitt behält den Dateinamen als Metadatum bei (um die Quelle zitieren zu können), und die Abschnitte werden stapelweise statt einzeln hinzugefügt.

index.py
from pathlib import Path
import chromadb
from chromadb.utils.embedding_functions.ollama_embedding_function import OllamaEmbeddingFunction
from pypdf import PdfReader

ef = OllamaEmbeddingFunction(url="http://localhost:11434", model_name="bge-m3")
coll = chromadb.PersistentClient(path="./chroma_db").get_or_create_collection("mes_docs", embedding_function=ef)

def lire(path: Path) -> str:
    if path.suffix.lower() == ".pdf":
        return "\n\n".join(p.extract_text() or "" for p in PdfReader(str(path)).pages)
    return path.read_text(encoding="utf-8", errors="ignore")

def decouper(texte: str, max_chars=1800):
    """Regroupe des paragraphes entiers jusqu'à max_chars ; un paragraphe trop long est coupé."""
    chunks, courant = [], ""
    for para in (p.strip() for p in texte.split("\n\n")):
        if not para:
            continue
        while len(para) > max_chars:
            if courant:
                chunks.append(courant); courant = ""
            chunks.append(para[:max_chars]); para = para[max_chars:]
        if len(courant) + len(para) + 2 > max_chars and courant:
            chunks.append(courant); courant = ""
        courant = (courant + "\n\n" + para).strip()
    if courant:
        chunks.append(courant)
    return chunks

n = 0
for path in sorted(Path("docs").rglob("*")):
    if path.suffix.lower() not in {".pdf", ".md", ".txt"}:
        continue
    chunks = decouper(lire(path))
    if not chunks:
        print(f"  ! {path.name} : aucun texte extrait (PDF scanné ?)")
        continue
    for i in range(0, len(chunks), 32):  # par lots de 32
        lot = chunks[i:i + 32]
        coll.upsert(
            ids=[f"{path.name}-{i + j}" for j in range(len(lot))],
            documents=lot,
            metadatas=[{"source": path.name}] * len(lot),
        )
    n += len(chunks)
    print(f"  + {path.name} : {len(chunks)} passages")
print(f"Terminé : {n} passages indexés")

Die Verwendung von upsert mit Kennungen, die aus dem Dateinamen und der Nummer des Textabschnitts gebildet werden, ermöglicht es, das Skript erneut auszuführen: Bei einer erneuten Indexierung desselben Ordners werden die Textabschnitte aktualisiert, statt dupliziert zu werden. Achtung: Wird ein Dokument kürzer, bleiben die alten, überzähligen Textabschnitte in der Datenbank; löschen Sie bei einer größeren Änderung den Ordner chroma_db und indexieren Sie erneut. Die Wahl der Größe der Textabschnitte wird im Leitfaden zu Chunking-Strategien ausführlich erläutert.

#3. Abfragen: Suche, dann Generierung

Das zweite Skript bettet die Frage ein, ruft die fünf ähnlichsten Textpassagen ab und stellt den Prompt zusammen. Die Anweisung ist entscheidend: Sie verlangt, ausschließlich auf Grundlage der Passagen zu antworten, offen zuzugeben, wenn Informationen fehlen, und die Datei als Quelle zu nennen. Der Parameter num_ctx vergrößert das Kontextfenster: Laut Ollama-Dokumentation beträgt das Standardfenster 4.096 Tokens und lässt sich über die Variable OLLAMA_CONTEXT_LENGTH oder den Parameter num_ctx ändern. Mit fünf Passagen von jeweils 400 bis 500 Tokens, der Anweisung und der Antwort sind 4.096 Tokens knapp bemessen: Ein zu kurzes Kontextfenster führt dazu, dass der Kontext ohne Hinweis abgeschnitten wird und das Modell antwortet, ohne das Ende Ihrer Passagen gelesen zu haben.

ask.py
import sys, requests
import chromadb
from chromadb.utils.embedding_functions.ollama_embedding_function import OllamaEmbeddingFunction

MODELE = "ministral-3:8b"
ef = OllamaEmbeddingFunction(url="http://localhost:11434", model_name="bge-m3")
coll = chromadb.PersistentClient(path="./chroma_db").get_collection("mes_docs", embedding_function=ef)

SYSTEME = (
    "Tu réponds en français, uniquement à partir des passages fournis. "
    "Si la réponse n'y figure pas, dis-le clairement au lieu de deviner. "
    "Termine chaque affirmation par le nom du fichier source entre crochets."
)

def repondre(question: str, k: int = 5):
    res = coll.query(query_texts=[question], n_results=k)
    passages = list(zip(res["documents"][0], res["metadatas"][0]))
    contexte = "\n\n---\n\n".join(f"[{m['source']}]\n{p}" for p, m in passages)
    r = requests.post("http://localhost:11434/api/chat", json={
        "model": MODELE,
        "stream": False,
        "options": {"temperature": 0.2, "num_ctx": 8192},
        "messages": [
            {"role": "system", "content": SYSTEME},
            {"role": "user", "content": f"PASSAGES :\n{contexte}\n\nQUESTION : {question}"},
        ],
    }, timeout=300)
    r.raise_for_status()
    return r.json()["message"]["content"], sorted({m["source"] for _, m in passages})

if __name__ == "__main__":
    q = " ".join(sys.argv[1:]) or input("Question : ")
    reponse, sources = repondre(q)
    print("\n" + reponse)
    print("\nSources consultées :", ", ".join(sources))
Starten
python index.py
python ask.py "Quel est le délai de préavis prévu au contrat ?"

#Prüfen, was ChromaDB zurückgibt, bevor man das Modell verantwortlich macht

Wenn eine Antwort schlecht ist, gibt es dafür zwei mögliche Ursachen: Die Suche hat nicht die richtige Textpassage geliefert, oder das Modell hat sie falsch verwendet. Die beiden Fälle lassen sich unterscheiden, indem Sie die gefundenen Textpassagen mit ihren Distanzwerten anzeigen, ohne das Modell aufzurufen. Wenn die richtige Textpassage nicht unter den ersten fünf Ergebnissen ist, ändern Sie die Aufteilung des Textes, ergänzen Sie eine Stichwortsuche oder einen Reranker. Ist sie vorhanden und die Antwort trotzdem falsch, liegt das Problem am Prompt, am abgeschnittenen Kontext oder am Modell: Probieren Sie das nächstgrößere Modell aus, bevor Sie eine Schlussfolgerung ziehen.

debug.py: Textpassagen und ihre Distanz anzeigen
import sys
import chromadb
from chromadb.utils.embedding_functions.ollama_embedding_function import OllamaEmbeddingFunction

ef = OllamaEmbeddingFunction(url="http://localhost:11434", model_name="bge-m3")
coll = chromadb.PersistentClient(path="./chroma_db").get_collection("mes_docs", embedding_function=ef)
res = coll.query(query_texts=[" ".join(sys.argv[1:])], n_results=8)
for doc, meta, dist in zip(res["documents"][0], res["metadatas"][0], res["distances"][0]):
    print(f"{dist:.3f}  {meta['source']}  {doc[:120]!r}")

#Speicherbudget: Was gleichzeitig in den Speicher passen muss

Bei RAG kommen zwei Modelle zum Einsatz: eines für die Generierung und eines für die Berechnung der Vektoren, dazu der Kontextcache des ersten Modells. Ollama lädt jedes Modell bei Bedarf und kann eines aus dem Speicher entfernen, um Platz für das andere zu schaffen. Bei knappem Speicher entsteht dadurch bei jedem Wechsel eine Verzögerung. Die Tabelle zeigt die Größenordnung für drei Konfigurationen; die Modellgröße stammt aus der Ollama-Bibliothek, der Rest beruht auf einer Berechnung, die sich mit dem VRAM-Rechner dieser Website verfeinern lässt.

Einzuplanender Speicherbedarf (Ollama-Modellgewichte, Kontext mit 8.192 Tokens)
KonfigurationSpeicherbedarf des GenerationsmodellsZu ergänzenVorgesehene Grafikkarte
ministral-3:8b + bge-m36,0 GBKontextcache, Embedding-Modell (567 Millionen Parameter, kaum mehr als ein GB in Halbpräzision), Speicherreserve für das System8 bis 12 GB
ministral-3:14b + bge-m39,1 GBEbenso; bei 12 GB wird ein langer Kontext zum begrenzenden Faktor12 bis 16 GB
mistral-small3.2:24b + bge-m315 GBDasselbe gilt; eine großzügige Reserve einplanen24 GB oder mehr
→
Wenn der Arbeitsspeicher nicht ausreicht
Reduzieren Sie zuerst num_ctx (8.192 ist für fünf Textpassagen bereits großzügig bemessen), und wechseln Sie dann zum kleineren Modell. Vermeiden Sie auch, die Anzahl der Textpassagen zu erhöhen: Zu viele Passagen verwässern die Antwort ebenso, wie sie den Speicher füllen.

#Die Fallstricke, die zu Antworten an der Frage vorbei führen

Der Standardkontext ist zu kurz
Siehe oben: Wird num_ctx nicht erhöht, werden die letzten Passagen abgeschnitten. Typisches Symptom: ChromaDB ruft die richtige Antwort ab, aber das Modell sagt, dass es sie nicht findet.
Gescannte PDF-Dateien
pypdf liest nur bereits vorhandenen Text. Bei einem Scan wird kein Text zurückgegeben: Das Skript zeigt dies an. Lassen Sie das Dokument zuerst durch eine OCR-Texterkennung laufen, wie im Leitfaden zu Tesseract beschrieben.
Passagen ohne Kontext
Eine aus ihrem Dokument herausgerissene Passage („die Frist beträgt 30 Tage“) lässt nicht erkennen, worauf sie sich bezieht. Stellen Sie jeder Passage den Titel des Dokuments oder des Abschnitts voran.
Frage ohne Antwort in den Dokumenten
Ohne die Anweisung „sag es klar“ füllt ein Modell die Lücke mit dem, was es weiß. Testen Sie immer eine Frage, deren Antwort nicht in Ihren Dateien enthalten ist.
Kennungen und exakte Begriffe
Eine Vertrags- oder Aktennummer lässt sich mithilfe von Embeddings nicht gut finden: Ergänzen Sie eine Stichwortsuche, wie im Leitfaden zur hybriden Suche beschrieben.
!
Prüfen, bevor Sie vertrauen
Ein RAG zitiert seine Quellen, doch das beweist nicht, dass die Antwort richtig ist: Öffnen Sie bei wichtigen Entscheidungen (Verträge, Zahlen, Fristen) die zitierte Datei.

#Weiterführende Informationen

Verbesserungen nach dem Verhältnis von Aufwand zu Wirkung sortiert
VerbesserungAufwandSinnvoll, wenn
Anzahl der Passagen (k) von 5 auf 8 erhöhenEine ZeileDie Antwort ist auf mehrere Abschnitte verteilt
Chunking nach Überschriften statt nach AbsätzenMittelStrukturierte Dokumente (Dokumentation, Verträge mit Artikeln)
Hybride Suche: BM25 + VektorsucheMittelFragen nach Kennungen, Abkürzungen oder Eigennamen
Reranker (bge-reranker-v2-m3)MittelDie richtige Antwort wird abgerufen, landet aber hinter Rang 5
Chat-Interface (Open WebUI, FastAPI-API)VariabelAndere Personen müssen das Tool verwenden.
Geplante Datensicherung und NeuindexierungGeringDer Dokumentenordner verändert sich jede Woche

Jede Verbesserung hat einen eigenen Leitfaden: Messen Sie den Recall anhand von 30 bis 50 echten Fragen vor und nach der Änderung, statt Techniken aufeinanderzuschichten. Wenn Sie eine fertige Benutzeroberfläche bevorzugen, ohne Code schreiben zu müssen, stellt der Leitfaden zu RAG ohne Programmieren Open WebUI und AnythingLLM vor.

#Häufige Fragen zum RAG mit Mistral

FAQ
Welches Mistral-Modell für lokales RAG?+
Für eine Grafikkarte mit 8 bis 12 GB ist ministral-3:8b (6,0 GB in Ollama, Apache-2.0-Lizenz) ein guter Ausgangspunkt; ministral-3:14b (9,1 GB) für 16 GB; mistral-small3.2:24b (15 GB) für 24 GB und mehr. Entscheiden Sie danach, wie viel Speicher nach dem Laden des Modells noch frei bleibt: Auch für den Kontext muss Platz vorhanden sein.
Kann Ollama die Embeddings anstelle von sentence-transformers berechnen?+
Ja: Ollama bietet eine Embedding-API an und stellt bge-m3 bereit, ein mehrsprachiges Modell mit 567 Millionen Parametern. ChromaDB stellt den Wrapper OllamaEmbeddingFunction bereit, um diese API aufzurufen. Der Vorteil ist, dass nur eine einzige Engine installiert werden muss, ohne PyTorch; der Nachteil ist, dass Ollama während der Indexierung laufen muss.
Warum sagt das Modell, dass es die Antwort nicht findet, obwohl sie in meinen Dokumenten enthalten ist?+
Zwei häufige Ursachen. Entweder ist das Kontextfenster von Ollama zu kurz und die Textabschnitte werden abgeschnitten: Erhöhen Sie num_ctx auf 8.192. Oder die gefundenen Textabschnitte enthalten die Antwort nicht: Prüfen Sie vor der Generierung, was ChromaDB zurückgibt, und passen Sie anschließend die Aufteilung oder die Suche an.
Kann die Mistral-API anstelle von Ollama verwendet werden?+
Technisch gesehen ja, aber Ihre Textpassagen würden dann an die Server des Unternehmens gesendet, was den Vertraulichkeitsanforderungen für sensible Dokumente widerspricht. Bleiben Sie bei Open-Weight-Modellen, die mit Ollama ausgeführt werden, um den Betrieb lokal zu halten. Für nicht sensible Dokumente ist die API möglich; lesen Sie dann beim Anbieter die Bedingungen für die Datenverarbeitung.
Wie lassen sich neue Dokumente hinzufügen, ohne alles neu zu indexieren?+
Kopieren Sie sie nach docs/ und starten Sie index.py erneut: Dank upsert und stabilen Identifikatoren werden bestehende Abschnitte aktualisiert und neue hinzugefügt. Wenn Sie die Größe der Abschnitte oder das Embedding-Modell ändern, löschen Sie den Ordner chroma_db und indexieren Sie alles neu: Die alten Vektoren sind nicht mehr vergleichbar.
Ist eine GPU für dieses RAG erforderlich?+
Nicht unbedingt: ministral-3:3b läuft auf einem aktuellen Prozessor mit 8 GB Speicher, antwortet allerdings langsam. Die Indexierung hingegen wird nur einmal ausgeführt. Eine GPU verbessert vor allem die Antworten: Sie ermöglicht mehr Geschwindigkeit und die Nutzung eines größeren Modells. Messen Sie die Antwortzeit auf Ihrem Rechner, bevor Sie sich für eine Investition entscheiden.
Hat Ihnen dieser Guide geholfen?

Haben Sie Feedback, einen Fehler entdeckt oder möchten Sie etwas präzisieren? Geben Sie uns Bescheid – so wird der Guide für alle besser.