- envs/environment-docling.yml (arm64, Python 3.12) and envs/environment-local_rag.yml (x86_64, Python 3.10) with exact pip versions and create commands in the header - README and EINSTIEG: how to create the environments; x86 tested for chat, search and indexing (search about 2.3 s under Rosetta) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.7 KiB
Kafka-Chat: Einstieg
Stand: Oktober 2026. Ausführliche englische Referenz: README.md.
Starten
Conda-Umgebungen
| Umgebung | Architektur | Einsatz |
|---|---|---|
docling |
arm64 (Apple Silicon), Python 3.12, torch 2.14 | Standard. Auch für Docling (prepare_corpus.py). |
local_rag |
x86_64 (Intel-Mac oder Rosetta), Python 3.10, torch 2.2.2 | Alternative für x86. Chat, Suche und Indexbau getestet; Docling läuft hier nicht. |
- Warum bge-m3 auf x86 nur über safetensors lädt: Für macOS x86 gibt es höchstens torch 2.2.2.
transformerslädt.bin-Gewichte aber erst ab torch 2.6, und bge-m3 hat im Hauptzweig nurpytorch_model.bin. - Die Lösung im Code:
local_rag.pylädt deshalb die offizielle safetensors-Konvertierung (EMBEDDING_REVISION). Die Gewichte sind dieselben, die Vektoren in beiden Umgebungen identisch, und dieselben Indizes funktionieren in beiden. - Neu anlegen: Mit festen Versionen aus
envs/environment-docling.ymlbzw.envs/environment-local_rag.yml. Die Befehle stehen jeweils im Kopf der Datei. - Geschwindigkeit unter Rosetta: Spürbar langsamer. Im Test brauchte die Suche etwa 2,3 statt gut 1 Sekunde, der Serverstart war dagegen nicht langsamer.
Voraussetzungen (einmalig)
- Ollama mit dem Modell:
ollama pull gemma4:12b. - Für ChatGPT und Realtime eine Datei
.envim Projektordner mitOPENAI_API_KEY=sk-.... Sie ist git-ignoriert, am besten mitchmod 600schützen. Ohne Key sind beide Einträge im Menü ausgegraut. - Die beiden Vektorspeicher
vectorstore_pypdf_2000/undvectorstore_primary_pypdf_2000/. Sie sind nicht im Git; Neubau siehe unten.
Server starten
cd /Users/Philipp/Desktop/workspace/diverses/local_rag
conda activate docling # auf x86: conda activate local_rag
uvicorn server:app --port 8010
Dann im Browser http://localhost:8010 öffnen. Der Start dauert etwa eine Minute: Embedding-Modell, Reranker und Stichwortindex werden geladen. Danach ist auch die erste Frage schnell.
- Optionen:
--reloadstartet den Server bei Codeänderungen neu,--log-level debugzeigt mehr Details. - Ohne
--reloadbraucht jede Änderung an.py-Dateien einen Neustart. Beitemplates/chat.htmlreicht es, die Seite neu zu laden.
Was im Terminal steht, pro Frage
[RAG] User question: '…'
[Zeit] LLM-Aufruf 1: 6.86s (Suche angefordert)
[RAG] Tool search '…': 5 chunk(s)
[Zeit] Suche 1.37s (davon Reranker 1.21s)
[Zeit] LLM-Aufruf 2: 15.31s (Antwort)
[Zeit] Gesamt 23.54s
Danach folgen die gefundenen Chunks mit Quelle, Seite und Score sowie die vollständige Antwort.
Aufbau
Browser (templates/chat.html)
│ POST /api/chat {message, history, persona, llm_provider}
▼
server.py ── wählt Figur (personas.py) und RAG-Instanz (Anbieter × Vektorspeicher)
▼
local_rag.py · LocalRAG.query_with_history
│ Systemprompt + Verlauf + Frage an das LLM, Such-Werkzeug angeboten
│ ┌─ LLM ruft search_kafka_corpus(query) auf (höchstens 3 Runden)
│ │ retrieve(): FAISS (bge-m3) + BM25 → Fusion (RRF) → 25 Kandidaten
│ │ → Reranker (bge-reranker-v2-m3) → beste 5 Chunks
│ └─ LLM antwortet mit den Auszügen
▼
Antwort + gefundene Chunks + Suchanfragen → Browser
Dateien
| Datei | Inhalt |
|---|---|
| server.py | FastAPI-Server, Modellwahl, eine RAG-Instanz pro Anbieter und Vektorspeicher, Konsolenausgabe |
| personas.py | die drei Figuren: Texte, Bilder, Systemprompts, Werkzeugbeschreibung, Vektorspeicher |
| local_rag.py | Laden der Modelle, Suche (retrieve), Werkzeugschleife (query_with_history), Indexbau |
| realtime_chat.py | Anbindung der OpenAI Realtime API (WebSocket, nur Text) als LangChain-Modell |
| templates/chat.html | Oberfläche, Menüs für Figur und Modell |
templates/visuals/ |
Bilder (*.png Vorlagen, *.jpg Webversionen, *_avatar.jpg) |
| add_pdfs.py | Vektorspeicher aus einem PDF-Ordner bauen |
| eval_retrieval.py | Suchqualität auf einem Goldset messen (Recall@k, MRR, optional LLM-Judge) |
Stellschrauben
Verhalten der Figuren: personas.py
Pro Figur ein Eintrag in PERSONAS; eine neue Figur ist ein neuer Eintrag mit eigenem Bild.
| Feld | wirkt auf |
|---|---|
system_prompt |
Ton, Rolle, Regeln für Suche und Belege |
tool_description |
was das Modell über das Such-Werkzeug erfährt (ohne Feld gilt SEARCH_TOOL_DESCRIPTION) |
vectorstore |
welcher Index durchsucht wird |
label, topic, name, role, place, status, greeting, suggestions |
Texte in der Oberfläche |
image, image_position, image_alt, avatar |
Bilder |
- Die Regeln zur Suche für Zeitgenosse und Autor stehen gemeinsam in
_TOOL_RULES. - Der Prompt der Forscherin ist
SYSTEM_PROMPTinlocal_rag.py.
Sprachmodelle: server.py und local_rag.py
| Einstellung | aktuell | wo |
|---|---|---|
| lokales Modell (Ollama) | gemma4:12b, denkt standardmäßig (reasoning nicht gesetzt) |
OLLAMA_MODEL in server.py, ChatOllama(...) in local_rag.py |
| ChatGPT | gpt-6.1-sol |
OPENAI_MODEL in server.py |
| Denkaufwand ChatGPT | low, über die Responses API |
ChatOpenAI(...) in local_rag.py |
| Realtime | gpt-realtime-2.1-mini |
REALTIME_MODEL in server.py |
| Voreinstellung im Menü | ChatGPT | selected in chat.html |
- gpt-6.1-sol: kennt keinen Denkaufwand
none. Werkzeuge funktionieren nur über die Responses API, deshalb stehtuse_responses_api=True. - Realtime: öffnet pro Modellaufruf eine eigene WebSocket-Sitzung.
Suche: Konstanten oben in local_rag.py
| Konstante | aktuell | Bedeutung |
|---|---|---|
RETRIEVAL_K |
5 | Chunks pro Suche an das LLM |
RETRIEVAL_MODE |
hybrid |
dense (nur Vektoren), hybrid (Vektoren + BM25, RRF), union |
CANDIDATES_K |
25 | Kandidaten vor dem Reranker. 25 war schneller und nicht schlechter als 50. |
RERANKER_MODEL |
BAAI/bge-reranker-v2-m3 |
None schaltet das Reranking ab. Läuft auf der GPU in fp16. |
MAX_TOOL_ROUNDS |
3 | Suchen pro Nachricht, danach muss das Modell antworten |
SEARCH_TOOL_DESCRIPTION |
Text | Werkzeugbeschreibung für die Forscherin |
Die Suche dauert etwa 1 bis 1,5 Sekunden pro Aufruf, fast nur der Reranker. Den größten Teil der Antwortzeit brauchen die Modellaufrufe.
Index: Neubau nötig bei Änderung
| Konstante | aktuell |
|---|---|
EMBEDDING_MODEL |
BAAI/bge-m3 (normalisierte Vektoren) |
CHUNK_SIZE / CHUNK_OVERLAP |
2000 / 400 Zeichen |
PDF_LOADER |
pypdf; docling war im Goldset-Vergleich nicht besser |
CHUNK_HEADERS |
False |
Neubau der beiden Indizes. data_primary/ enthält nur einen Verweis auf data/Kafka_Die_Verwandlung_.pdf.
python add_pdfs.py --data data --index ./vectorstore_pypdf_2000 --loader pypdf --no-headers
python add_pdfs.py --data data_primary --index ./vectorstore_primary_pypdf_2000 --loader pypdf --no-headers
Jeder Index speichert seine Einstellungen in index_info.json. Passt das Embedding-Modell nicht zum Code, verweigert LocalRAG das Laden.
Suchqualität messen
python eval_retrieval.py --goldset eval/goldset_kafka_human.csv --report eval/mein_bericht.txt
--judge gpt-6-lunalässt zusätzlich ein LLM prüfen, ob die Antwort in den Top 5 steht; das braucht den Key.- Stand auf dem menschlichen Goldset (30 Fragen): R@5 0,73, MRR 0,68.
Gut zu wissen
- Speicher: Embedding-Modell, Reranker und BM25-Index gibt es pro Prozess nur einmal, auch bei mehreren Anbietern. Ollama braucht zusätzlich etwa 8 bis 10 GB für gemma4:12b.
- Nicht im Git: Korpus-PDFs (
data/), Vektorspeicher,.envund Schlüsseldateien.