Files
pv-agent/planung.md
T
fegger 2cba72aeb0 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.
2026-09-14 16:53:04 +02:00

249 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## User
bitte plane das folgende:
Eine Wissensdatenbank und ein Agent der Fragen zu Oesterreichischer Personalverrechnung mit dem Wissen aus der Wissensdatenbank beantwortet.
Entwicklungsumgebung-GPU: AMD Radeon Ai Pro r9700 - 32GB vram
Ollama-Server: http://100.183.83.12:11435
Agent: lokales Modell (ollama): empfehle ein Modell. der Agent darf nur wissen aus der wissensbasis verwenden, kein trainingswissen, kein web search
Wissensdatenbank: bitte um einen Vorschlag fuer die Umsetzung. bereits vorhanden: [@wissensbasis](file:///home/fegger/Projects/odoo-projects/pv-agent/wissensbasis/)
in weiterer Folge soll der Agent in Odoo-Enterprise verwendet werden.
---
# Plan: Wissensbasis-Agent Österreichische Personalverrechnung
*Stand: 2026-09-14 — Planung, noch keine Implementierung.*
## 1. Ist-Zustand (verifiziert)
- **Wissensbasis** (`wissensbasis/`): **601 kuratierte Layer-2-Einträge**
(571 Lexis Briefings Personalrecht, 30 WIKU), verteilt über 69 Cluster
(`INDEX.md`). Median ~7,3 KB, max ~31 KB, gesamt ~4,8 MB — ideale
Größe für vollständige Indizierung.
- **Struktur retrieval-gerecht:** verbindliches Frontmatter-Schema
(`id`, `stand`, `topic`, `tags`, `legal_bases`, `cross_refs`, …),
`kb.json` als generierte/validierte Registry, fixer Dokumentaufbau
(`## Zusammenfassung``## Kernwerte & Fristen``## Rechtsgrundlagen`
`## Payroll-Relevanz (Odoo)``## Verweise`).
- **Layer-1-Volltexte:** `.lexis360/md/` (572 Dateien) lokal vorhanden,
lizenzbeschränkt + unversioniert. `.wiku/` fehlt in diesem Checkout —
für Phase A irrelevant (Retrieval läuft auf Layer 2).
- **Ollama-Server** `http://100.183.83.12:11435` (Custom-Port): aus der
Zed-Sandbox **nicht erreichbar** (Netzwerkrestriktion, Timeout) — ist
vom Host aus zu verifizieren (`curl http://100.183.83.12:11435/api/tags`).
- **GPU:** Radeon AI Pro R9700, 32 GB (Strix Halo / RDNA 5) — budgetiert
Modellwahl auf ~2628 GB nutzbar.
## 2. Zielbild
Ein RAG-Agent, der Fragen zur österreichischen Personalverrechnung
**ausschließlich** aus den Layer-2-Einträgen beantwortet:
- jede fachliche Aussage mit KB-ID und `stand` belegt,
- **kein** Trainingswissen, **kein** Web, **keine** Tools,
- ehrliche Verweigerung, wenn die Wissensbasis nichts hergibt,
- Korpuskonflikte (z. B. ATZ-Ersatzquote 28,5 % vs. 27,5 %, lb-atz-07/09/12)
werden **beidseitig mit ⚠** referenziert — nie still aufgelöst,
- später Chat-Oberfläche in Odoo Enterprise.
## 3. Architektur (Phase A — eigener schlanker RAG-Service)
```mermaid
flowchart TD
KB[wissensbasis/dokumente/*.md + kb.json] -->|Ingest: Frontmatter + H2-Sektionen| IDX[(data/index.db - SQLite: FTS5 BM25 + Vektoren + Metadaten)]
IDX -->|Hybrid-Retrieval: BM25 + Dense, Metadaten-Filter, cross_refs| CTX[Kontextblöcke 8-12]
CTX -->|Systemprompt: nur Kontext, Zitierpflicht| LLM[Ollama qwen3:32b]
LLM -->|Antwort-Entwurf| CHECK{Post-Validierung: zitierte IDs ⊆ retrieved IDs?}
CHECK -->|ja| A[Antwort + Quellenblock]
CHECK -->|nein, 1x| LLM
CHECK -->|nein, 2x| R[Antwort als unsicher markiert / verweigert]
A --> API[FastAPI: POST /ask, GET /health, POST /reindex]
R --> API
API --> CLI[CLI + Mini-Web-UI zum Testen]
```
**Warum kein LangChain/LlamaIndex:** 601 Dokumente, sauberes Schema —
die Pipeline ist als eigenständiger Python-Code (~400600 Zeilen)
überschaubar und gibt volle Kontrolle über die Grounding- und
Zitier-Regeln, die hier der kritische Teil sind. Frameworks würden
Abhängigkeiten einführen, ohne das Kernproblem (Grounding) abzunehmen.
**Warum Layer 2 als Retrieval-Korpus:** kuratiert, eigene Worte
(lizenzkonform), versioniert, Metadaten-annotiert. Layer-1-Volltexte
bleiben außen vor (offener Lizenz-/Provisioning-Punkt laut README);
eine spätere Erweiterung für Tiefenzitate ist möglich, ohne die
Architektur zu ändern.
## 4. Modell-Empfehlung (Ollama)
| Modell | Rolle | Größe (Q4) | Begründung |
|---|---|---|---|
| **`qwen3.8:27b`** | **primärer Bake-off-Kandidat** | ~18 GB · 256k ctx | neueste Qwen-Generation (Ollama-Library, verifiziert 2026-09); Thinking per Request abschaltbar; Vision vorhanden, hier ungenutzt |
| `qwen3:32b` | bekannte Größe / Fallback | ~20 GB | bestes Deutsch + Instruction-Following im ≤32B-Bereich; Zitierdisziplin entscheidender als Weltwissen (das wir unterdrücken) |
| `gemma3:27b` | Alternative (Bake-off) | ~17 GB | sehr gutes Deutsch, 128k Kontext |
| `mistral-small3.2:24b` | Bake-off (Deutsch-Kandidat) | ~15 GB · 128k ctx | europäischer Anbieter, Europasprachen-Fokus → starke Deutsch-Hypothese (im Bake-off verifizieren); starkes Instruction-Following + wenige Wiederholungsfehler; kein Thinking-Modus |
| `qwen3:30b-a3b` | Latenz-Alternative | ~18 GB | MoE (3,3B aktiv) → deutlich schneller, etwas schwächer |
| `qwen3:14b` | Dev/Bake-off | ~9 GB | falls Zitierqualität reicht → halbe Latenz |
| **`bge-m3`** | **Embeddings** | ~1,2 GB | multilingual (Deutsch stark), 8k Kontext, über Ollama `/api/embed` |
| `bge-reranker-v2-m3` | optionales Reranking | ~1,1 GB | Rerank-API der installierten Ollama-Version vorher verifizieren; Fallback: Hybrid-Score ohne Reranker |
**VRAM-Budget:** Antwortmodell (1520 GB) + bge-m3 (~1,5 GB) + KV-Cache für
RAG-Prompts (48k Tokens Kontext, ~16k Kontextfenster ≈ 34 GB) ⇒
~2026 GB von 32 GB — passt mit Reserve. Thinking-Modus abschalten
(Latenz; bei `qwen3.8:27b` ist Thinking Default **an** → per Request
deaktivieren, API-Option lt. Library: `reasoning_effort`,
`preserve_thinking` — beim Implementieren verifizieren).
**Empfehlung:** Bake-off-Feld (M3): `qwen3.8:27b` und `qwen3:32b`
(Front-Runner) sowie `gemma3:27b` und `mistral-small3.2:24b` als
Deutsch-Kandidaten — Deutsch-/Zitierqualität ist jeweils unverifiziert.
Verbindliche Entscheidung erst nach Bake-off auf dem Goldset — Kriterium
bleibt Zitier-Präzision vor Latenz; liegt `qwen3:14b` bei gleicher
Zitierqualität auf, gewinnt Latenz.
## 5. Grounding-Konzept (der kritische Teil)
1. **Systemprompt (deutsch):** antworte ausschließlich aus den
nummerierten Kontextblöcken; jede fachliche Aussage mit
`[kb-id]`-Beleg; Werte **immer mit** `(Stand YYYY-MM)`; fehlt etwas →
„Dazu enthält die Wissensbasis keine Aussage“ + ggf. verwandte
Cluster nennen; §-Zitate nur wenn die Quelle sie nennt; Korpus-
konflikte beidseitig mit ⚠ darstellen; keine Ergänzungen aus
Trainingswissen.
2. **Kontextblöcke** mit Metadatenkopf (ID · Titel · Stand · topic ·
Quellenwerk) — das Modell sieht nur, was im Retrieval war.
3. **Post-Validierung:** jede zitierte ID muss in der Retrieved-Menge
stehen; Verstoß → eine Regenerierung mit härterem Hinweis, dann
Verweigern/„unsicher“. Temperatur ~0,1.
4. **Kein Ausweg nach außen:** keine Tools, kein Browsing, kein
Web-Search-Hook — architektonisch gibt es nur Wissensbasis → Prompt.
## 6. Wissensdatenbank-Umsetzung (Vorschlag)
Die bestehende Wissensbasis ist bereits retrieval-gerecht — **kein
Umbau nötig**, nur ein Ingest-Index:
- **Chunking:** H2-Sektionen je Eintrag als Retrieval-Einheit (Parent-
Child: Treffer auf Sektion, Kontext = ganze Sektion + Metadatenkopf);
`Kernwerte & Fristen`-Tabellen als eigene Chunks (Zahlenfragen!);
Frontmatter im Index (topic, tags, legal_bases, stand).
- **Hybrid-Retrieval:** SQLite FTS5 (BM25; deutsche Normalisierung:
Umlaut-Folding beim Indexing) + Dense-Embeddings (bge-m3) + optional
Reranker; Reciprocal-Rank-Fusion; `cross_refs` der Top-Treffer als
kontrollierte Kontext-Erweiterung.
- **Metadaten-Filter:** `topic`-Vorfokus aus der Frage, Aktualitäts-
gewichtung über `stand`.
- **Quelle der Ingestion:** Layer-2-Frontmatter direkt (Single Source of
Truth); `kb.json` zusätzlich als Konsistenz-Gate (Anzahl/IDs müssen
matchen).
- **Update-Zyklus:** nach jedem neuen Batch einmal `POST /reindex`
(vollständiger Rebuild dauert bei 601 Einträgen Sekunden; Embeddings
gecacht, nur neue Einträge einbetten).
- **Speicherung:** eine SQLite-Datei `data/index.db` (gitignored) —
keine externe Vektor-DB nötig; Skalierungsreserve bis ~10.000
Einträge ohne Architekturwechsel.
## 7. Neue Dateien (Phase A)
```
agent/
config.py # Ollama-URL, Modellnamen, Ports (ENV-override)
ingest.py # Layer-2 → index.db (FTS5 + Vektoren via /api/embed)
retrieve.py # Hybrid-Suche + Filter + cross_refs (+ optional Rerank)
generate.py # Prompt-Bau, Ollama-Chat, Post-Validierung, Antwortformat
api.py # FastAPI: /ask, /health, /reindex
cli.py # Frage im Terminal (Dev-Loop)
eval/goldset.yaml # 3050 Fragen → Soll-IDs (inkl. Konfliktfälle)
eval/evaluate.py # Recall@k, Zitier-Präzision, Verweigerungsraten, Latenz
web/index.html # minimalistischer Test-Chat
data/ # index.db (gitignored)
tests/ # pytest: Ingest-, Retrieval-, Grounding-Unit-Tests
```
## 8. Validierung
- **Goldset:** 3050 Fragen mit Soll-IDs je Cluster, inkl. 35
Outside-KB-Fragen (Verweigerung!) und Konfliktfragen (ATZ-Quoten).
- **Metriken:** Retrieval-Recall@8 (Ziel >0,9), Zitier-Präzision
(100 % zitierte IDs ∈ retrieved), Verweigerungskorrektheit,
End-zu-End-Latenz.
- **Modell-Bake-off (M3):** qwen3.8:27b vs. qwen3:32b vs. gemma3:27b vs.
mistral-small3.2:24b (plus qwen3:14b als Latenz-Untergrenze) auf dem
Goldset; Entscheidung dokumentieren (analog D1/D2-Stil des Projekts).
- **Unit-Tests:** Ingest-Schema, Umlaut-Normalisierung, Post-Validierung
(Halluzinations-ID → Regenerierung), Konflikt-Darstellung.
## 9. Phase B: Odoo-Enterprise-Integration (später)
- **Option A (empfohlen):** dünnes Custom-Modul mit OWL-Chat-Panel,
`ir.config_parameter` für die Service-URL, rollenbasierter Zugriff.
Der RAG-Service bleibt Single Source of Truth für Grounding und
Zitate; Ollama bleibt extern. Unabhängig von Odoo-Version-Features.
- **Option B (zu prüfen):** nativer Odoo-LLM-Stack (in Odoo 19 neu
eingeführte `llm`/Agent-/Knowledge-Module) mit Ollama als
OpenAI-kompatiblem Provider. **Muss zuerst gegen Euren konkreten
Odoo-19-Quellstand verifiziert werden** (Modulnamen/APIs sind hier
nicht im Projekt und werden nicht aus Trainingswissen behauptet).
Striktes Grounding + Zitierdisziplin wären dort nachzubauen.
- Entscheidung erst nach Verifikation; Phase A läuft davon unabhängig
und wird von beiden Optionen unverändert genutzt.
- **Datenschutz-Bonus:** der Agent ist reiner Wissensassistent — es
fließen keine Mitarbeiter-/Abrechnungsdaten ins Modell, nur die
Frage und Layer-2-Fachtexte.
## 10. Risiken & offene Punkte
1. **Ollama-Erreichbarkeit** aus der Zed-Sandbox nicht gegeben —
Verifikation vom Host: `curl http://100.183.83.12:11435/api/tags`;
Modelle ggf. erst pullen (`qwen3:32b`, `bge-m3`, …).
2. **Ollama-Version:** `/api/embed` + allfällige Rerank-Unterstützung
prüfen; Fallback ohne Reranker ist unkritisch.
3. **Strix Halo (gfx-1151):** Ollama/ROCm muss die Karte unterstützen
(Server läuft offenbar bereits — im Bake-off Performance messen).
4. **Halluzination trotz allem:** Prompt + Post-Validierung reduzieren,
aber nicht eliminieren → Eval-Suite als Dauerschutz; Antworten
führen immer Quellen-IDs (Nachprüfbarkeit durch den Nutzer).
5. **WIKU-Layer-1** fehlt lokal — nur relevant, falls später Layer-1-
Tiefenzitate gewünscht (Lizenzpunkt aus README bleibt offen).
6. **Odoo-19-LLM-Module** unverifiziert → Phase B separat planen.
## 11. Meilensteine
| # | Inhalt | Ergebnis |
|---|---|---|
| M1 | Ingest + Index + Hybrid-Retrieval (ohne LLM) | Recall@8 auf Goldset messbar |
| M2 | Ollama-Anbindung (Embed + Generate), Grounding-Regeln, `/ask`-API, CLI | nutzbarer Agent im Terminal |
| M3 | Eval-Suite + Modell-Bake-off | dokumentierte Modell-Entscheidung |
| M4 | Odoo-Integration | separater Plan nach Odoo-19-Verifikation |
## 12. Entscheidungspunkte (an Dich)
1. **Modell:** `qwen3.8:27b` als primärer Bake-off-Kandidat ok — oder
gleich als Startmodell festlegen (und `qwen3:32b` nur als Fallback)?
2. **Bake-off:** vergleichst Du die drei Antwortmodelle auf dem Goldset
(empfohlen) oder legen wir qwen3:32b direkt fest?
3. **Layer 1:** bewusst außen vor in Phase A — einverstanden?
4. **Odoo-Version für Phase B:** Odoo 19 Enterprise (passend zu
`l10n_at_hr_payroll*`) — bitte bestätigen.
## 13. Umsetzungsstand (2026-09-14)
- **M1 erledigt (offline):** Ingest + Index (601 Einträge → 3.005 Chunks,
FTS5-BM25) + Hybrid-Retrieval (Dense-Code vorhanden, am Host zu messen).
Goldset 31 Fragen (IDs gegen kb.json verifiziert, inkl. ATZ-Konfliktfall
und 4 Verweigerungsfälle). Baseline BM25-only: Hit-Rate 0,871 ·
Recall@8 0,855 · MRR 0,476 — 4 Fehltreffer sind Komposita-/Stamm-
Muster, die die Dense-Suche abdecken soll. 41 Unit-Tests grün.
- **M2 implementiert:** Ollama-Client (embed/chat, think-Fallback),
Systemprompt mit Zitierpflicht, Post-Validierung (1× Regenerierung, dann
Verweigerung), `/ask`-API + CLI + Test-Chat. **Host-Validierung mit
echtem Ollama noch offen** (aus der Zed-Sandbox nicht erreichbar).
- **M3 offen:** Bake-off auf dem Host (qwen3.8:27b vs. qwen3:32b vs.
gemma3:27b vs. mistral-small3.2:24b; qwen3:14b als Latenz-Untergrenze).
- Betrieb/Host-Schritte: `agent/README.md`.