- 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>
158 lines
7.7 KiB
Markdown
158 lines
7.7 KiB
Markdown
# Kafka-Chat: Einstieg
|
||
|
||
Stand: Oktober 2026. Ausführliche englische Referenz: [README.md](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. `transformers` lädt `.bin`-Gewichte aber erst ab torch 2.6, und bge-m3 hat im Hauptzweig nur `pytorch_model.bin`.
|
||
- **Die Lösung im Code:** `local_rag.py` lä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.yml` bzw. `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 `.env` im Projektordner mit `OPENAI_API_KEY=sk-...`. Sie ist git-ignoriert, am besten mit `chmod 600` schützen. Ohne Key sind beide Einträge im Menü ausgegraut.
|
||
- Die beiden Vektorspeicher `vectorstore_pypdf_2000/` und `vectorstore_primary_pypdf_2000/`. Sie sind nicht im Git; Neubau siehe unten.
|
||
|
||
**Server starten**
|
||
|
||
```bash
|
||
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: `--reload` startet den Server bei Codeänderungen neu, `--log-level debug` zeigt mehr Details.
|
||
- Ohne `--reload` braucht jede Änderung an `.py`-Dateien einen Neustart. Bei `templates/chat.html` reicht 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](server.py) | FastAPI-Server, Modellwahl, eine RAG-Instanz pro Anbieter und Vektorspeicher, Konsolenausgabe |
|
||
| [personas.py](personas.py) | die drei Figuren: Texte, Bilder, Systemprompts, Werkzeugbeschreibung, Vektorspeicher |
|
||
| [local_rag.py](local_rag.py) | Laden der Modelle, Suche (`retrieve`), Werkzeugschleife (`query_with_history`), Indexbau |
|
||
| [realtime_chat.py](realtime_chat.py) | Anbindung der OpenAI Realtime API (WebSocket, nur Text) als LangChain-Modell |
|
||
| [templates/chat.html](templates/chat.html) | Oberfläche, Menüs für Figur und Modell |
|
||
| `templates/visuals/` | Bilder (`*.png` Vorlagen, `*.jpg` Webversionen, `*_avatar.jpg`) |
|
||
| [add_pdfs.py](add_pdfs.py) | Vektorspeicher aus einem PDF-Ordner bauen |
|
||
| [eval_retrieval.py](eval_retrieval.py) | Suchqualität auf einem Goldset messen (Recall@k, MRR, optional LLM-Judge) |
|
||
|
||
## Stellschrauben
|
||
|
||
### Verhalten der Figuren: [personas.py](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_PROMPT` in `local_rag.py`.
|
||
|
||
### Sprachmodelle: [server.py](server.py) und [local_rag.py](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 steht `use_responses_api=True`.
|
||
- **Realtime:** öffnet pro Modellaufruf eine eigene WebSocket-Sitzung.
|
||
|
||
### Suche: Konstanten oben in [local_rag.py](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`.
|
||
|
||
```bash
|
||
python add_pdfs.py --data data --index ./vectorstore_pypdf_2000 --loader pypdf --no-headers
|
||
```
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
python eval_retrieval.py --goldset eval/goldset_kafka_human.csv --report eval/mein_bericht.txt
|
||
```
|
||
|
||
- `--judge gpt-6-luna` lä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, `.env` und Schlüsseldateien.
|