M1+M2: RAG-Pipeline mit verbindlichem Grounding
agent/-Paket: Ingest (601 Layer-2-Eintraege -> 3005 Chunks, FTS5-BM25 + Vektoren-Cache), Hybrid-Retrieval (RRF, Stand-Boost, cross_ref-Erweiterung), Ollama-Client (embed/chat, think-Flag-Fallback, kurzes Connect-Budget), Systemprompt mit Zitierpflicht, Post-Validierung (zitierte IDs gemaess Retrieved-Set, 1x Regenerierung, dann Verweigerung), FastAPI (/ask, /health, /reindex), CLI, Goldset (31 Fragen, IDs gegen kb.json verifiziert, inkl. ATZ-Konfliktfall + 4 Verweigerungsfaelle), Eval-Suite, Test-Chat. 41 Offline-Tests gruen. Baseline BM25-only: Hit-Rate 0,871 / Recall@8 0,855 / MRR 0,476. Hybrid-Messung, Antwortmodus-Eval und Modell-Bake-off (M3) auf dem Host ausstaendig (Ollama aus der Zed-Sandbox nicht erreichbar). MEMORY.md und planung.md Umsetzungsstand aktualisiert.
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
# PV RAG Agent
|
||||
|
||||
Lokaler RAG-Agent für österreichische Personalverrechnung: beantwortet
|
||||
Fragen **ausschließlich** aus der kuratierten Wissensbasis (Layer 2,
|
||||
`wissensbasis/`, 601 Einträge) — mit ID- und Stand-Beleg, ohne
|
||||
Trainingswissen, ohne Web-Zugriff. Verbindliche Regeln:
|
||||
`.agents/skills/pv-rag-agent/SKILL.md`, Plan: `planung.md`.
|
||||
|
||||
## Architektur (Kurzfassung)
|
||||
|
||||
```
|
||||
wissensbasis/dokumente/*.md ──ingest──▶ data/index.db
|
||||
├─ chunks (FTS5, BM25, Umlaut-Folding)
|
||||
├─ vectors (bge-m3, Content-Hash-Cache)
|
||||
└─ Metadaten (stand, topic, tags, …)
|
||||
Frage ──retrieve──▶ Hybrid BM25+Dense (RRF) + cross_ref-Erweiterung
|
||||
──generate──▶ Ollama (Systemprompt, Zitierpflicht)
|
||||
──validate──▶ zitierte IDs ⊆ Retrieved-Set? sonst 1× regenerieren, dann verweigern
|
||||
```
|
||||
|
||||
- **Nur Layer 2** als Korpus (kuratiert, lizenzkonform). Layer-1-Volltexte
|
||||
(`.lexis360/`, `.wiku/`) bleiben außen vor (offener Lizenzpunkt).
|
||||
- **Kein Ausweg nach außen:** keine Tools, kein Browsing — der einzige
|
||||
HTTP-Client spricht mit Ollama.
|
||||
- **Verweigerungspflicht:** leeres/schwaches Retrieval → deterministische
|
||||
Antwort „Dazu enthält die Wissensbasis keine Aussage." (kein LLM-Call).
|
||||
|
||||
## Schnellstart
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 1) Index bauen (mit Embeddings, wenn Ollama erreichbar)
|
||||
python -m agent.cli ingest # --no-embed erzwingt BM25-only
|
||||
|
||||
# 2) Frage im Terminal
|
||||
python -m agent.cli ask "Wie hoch ist die AMS-Ersatzquote bei geblockter Altersteilzeit?"
|
||||
|
||||
# 3) Goldset-Evaluation (offline: Retrieval-Metriken)
|
||||
python -m agent.cli eval
|
||||
# inkl. Antworten + Verweigerungsfälle (benötigt Ollama):
|
||||
python -m agent.cli eval --answers --json-out data/eval-report.json
|
||||
|
||||
# 4) HTTP-API + Test-Chat
|
||||
python -m agent.cli serve # http://127.0.0.1:8080 (/ask, /health, /reindex)
|
||||
```
|
||||
|
||||
## Konfiguration (Umgebungsvariablen)
|
||||
|
||||
| Variable | Default | Bedeutung |
|
||||
|---|---|---|
|
||||
| `OLLAMA_URL` | `http://100.183.83.12:11435` | Ollama-Server (Custom-Port!) |
|
||||
| `PV_ANSWER_MODEL` | `qwen3.8:27b` | Antwortmodell (provisorisch bis Bake-off M3) |
|
||||
| `PV_EMBED_MODEL` | `bge-m3` | Embedding-Modell |
|
||||
| `PV_DB_PATH` | `data/index.db` | SQLite-Index |
|
||||
| `PV_KB_DIR` | `wissensbasis` | Wissensbasis-Verzeichnis |
|
||||
| `PV_THINK` | `false` | Thinking per Request (qwen3.8: default an) |
|
||||
| `PV_EMBED_OFF` | `false` | `true` = BM25-only |
|
||||
| `PV_CONTEXT_BLOCKS` | `8` | Kontextblöcke im Prompt |
|
||||
| `PV_PORT` | `8080` | API-Port |
|
||||
|
||||
## Deployment auf dem Host (Ollama-Maschine)
|
||||
|
||||
```bash
|
||||
# Modelle einmalig pullen
|
||||
ollama pull qwen3.8:27b
|
||||
ollama pull bge-m3
|
||||
curl http://100.183.83.12:11435/api/tags # Erreichbarkeit + Modelle
|
||||
|
||||
# Vollständiger Index (BM25 + Dense) — danach eval, Ziel: Recall@8 > 0,9
|
||||
python -m agent.cli ingest
|
||||
python -m agent.cli eval
|
||||
python -m agent.cli eval --answers # Zitier-Präzision, Verweigerungen, Latenz
|
||||
```
|
||||
|
||||
## Baseline (2026-09-14, BM25-only, ohne Dense)
|
||||
|
||||
Goldset (31 Fragen, `agent/eval/goldset.yaml`): Hit-Rate 0,871 ·
|
||||
Recall@8 0,855 · MRR 0,476. Die vier Fehltreffer sind klassische
|
||||
BM25-Schwächen (Komposita: „aliquotiert"↔„Aliquotierung";
|
||||
„Mindestlohngesetz") — genau die Fälle, die die Dense-Suche abdecken soll.
|
||||
Hybrid-Messung auf dem Host aussteht (Ollama aus der Zed-Sandbox nicht
|
||||
erreichbar).
|
||||
|
||||
## Dateien
|
||||
|
||||
```
|
||||
agent/
|
||||
config.py Env-Konfiguration
|
||||
kb.py Layer-2-Parsing + kb.json-Gate
|
||||
normalize.py Umlaut-Folding, FTS-Query-Bau
|
||||
ingest.py Index-Bau (chunks + FTS5 + Vektoren-Cache)
|
||||
retrieve.py Hybrid-Retrieval (BM25 + Dense, RRF, cross_refs)
|
||||
ollama_client.py Ollama-HTTP (embed + chat, think-Fallback)
|
||||
generate.py Systemprompt, Post-Validierung, Verweigerung
|
||||
api.py FastAPI (/ask, /health, /reindex)
|
||||
cli.py ingest | ask | eval | serve
|
||||
eval/ goldset.yaml + evaluate.py
|
||||
web/index.html Minimaler Test-Chat
|
||||
tests/ 41 Tests (offline, Fake-Ollama)
|
||||
data/ index.db (gitignored)
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
python -m pytest -q # 41 Tests, alle offline
|
||||
```
|
||||
|
||||
## Lizenz-Disziplin
|
||||
|
||||
`.lexis360/`, `.wiku/`, `.firecrawl/`, `.ris/` sind lokal und unversioniert
|
||||
(`.gitignore`). Der Index enthält ausschließlich Layer-2-Kuratierung;
|
||||
Layer-1-Prompts wären ein Lizenzverstoß und sind im Code nicht vorgesehen.
|
||||
Reference in New Issue
Block a user