29 KiB
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
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.jsonals 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.103.83.12:11435(Ziel-Instanz mit Modell-Zoo, Custom-Port; der Host betreibt zusätzlich eine fast leere Instanz auf 11434): aus der Zed-Sandbox erreichbar (2026-09-14, nach URL-Korrektur) —bge-m3wurde per API gepullt,qwen3.8:27bwar bereits installiert. - GPU: Radeon AI Pro R9700, 32 GB (Strix Halo / RDNA 5) — budgetiert Modellwahl auf ~26–28 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
standbelegt, - 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)
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 (~400–600 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 (15–20 GB) + bge-m3 (~1,5 GB) + KV-Cache für
RAG-Prompts (4–8k Tokens Kontext, ~16k Kontextfenster ≈ 3–4 GB) ⇒
~20–26 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.
Bake-off-Ergebnis (2026-09-14, Abschnitt 13):
qwen3.8:27bhat gewonnen und ist als Antwortmodell fixiert.
5. Grounding-Konzept (der kritische Teil)
- 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. - Kontextblöcke mit Metadatenkopf (ID · Titel · Stand · topic · Quellenwerk) — das Modell sieht nur, was im Retrieval war.
- Post-Validierung: jede zitierte ID muss in der Retrieved-Menge stehen; Verstoß → eine Regenerierung mit härterem Hinweis, dann Verweigern/„unsicher“. Temperatur ~0,1.
- 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_refsder Top-Treffer als kontrollierte Kontext-Erweiterung. - Metadaten-Filter:
topic-Vorfokus aus der Frage, Aktualitäts- gewichtung überstand. - Quelle der Ingestion: Layer-2-Frontmatter direkt (Single Source of
Truth);
kb.jsonzusä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 # 30–50 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: 30–50 Fragen mit Soll-IDs je Cluster, inkl. 3–5 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_parameterfü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
- Ollama-Erreichbarkeit gegeben (Ziel-Instanz 11435; Sandbox kann
verbinden). Achtung Doppel-Instanz auf 11434 — URL nicht "korrigieren".
Cloud-Modelle (
*:cloud) nicht für Antworten verwenden (Anforderung: lokal). - Ollama-Version:
/api/embed+ allfällige Rerank-Unterstützung prüfen; Fallback ohne Reranker ist unkritisch. - Strix Halo (gfx-1151): Ollama/ROCm muss die Karte unterstützen (Server läuft offenbar bereits — im Bake-off Performance messen).
- 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).
- WIKU-Layer-1 fehlt lokal — nur relevant, falls später Layer-1- Tiefenzitate gewünscht (Lizenzpunkt aus README bleibt offen).
- 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)
- Modell:
qwen3.8:27bals primärer Bake-off-Kandidat ok — oder gleich als Startmodell festlegen (undqwen3:32bnur als Fallback)? - Bake-off: vergleichst Du die drei Antwortmodelle auf dem Goldset (empfohlen) oder legen wir qwen3:32b direkt fest?
- Layer 1: bewusst außen vor in Phase A — einverstanden?
- 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 und akzeptiert: Hybrid-Index (601 Einträge → 3.005 Chunks, FTS5-BM25 + bge-m3-Dense, Ollama :11435), Goldset 31 Fragen. Recall@8 0,952 > 0,9 (Hit-Rate 0,968, MRR 0,690; BM25-only-Vergleich: 0,855 — die vier Komposita-Fehltreffer behebt die Dense-Suche alle). 41 Unit-Tests grün.
-
M2 erledigt und gegen das echte Modell validiert (qwen3.8:27b, Thinking aus): Zitier-Präzision 100 % (4 Verletzungen → Post- Validierung → Regenerierung, alle geheilt), Verweigerung korrekt 94,3 %, Latenz mean 32 s / p95 53 s. ATZ-Konfliktfall wird korrekt beidseitig mit ⚠ beantwortet; harte Verweigerungsfälle (UStVA) funktionieren.
-
M3 erledigt — Bake-off (2026-09-14, Goldset 35 Fragen, Thinking aus):
Kandidat Zitier-Präzision Verweigerung korrekt Erw. Quelle mean/p95 Regen qwen3.8:27b ✅ 100 % 94,3 % 80,6 % 32 s / 53 s 4 gemma4:26b 100 % 91,4 % 67,7 % 7,9 s / 12 s 4 gemma4:12B 94,3 % 91,4 % 77,4 % 21 s / 40 s 8 qwen3.6:27B 94,3 % 85,7 % 80,6 % 43 s / 89 s 10 muse-glimmer:latest 100 % 77,1 % 74,2 % 37,5 s / 51 s 1 mistral-small3.1:24b 100 % 71,4 % 58,1 % 20 s / 43 s 2 Entscheidung (D7):
qwen3.8:27bist das Antwortmodell (Protokoll: Zitier-Präzision → Verweigerungskorrektheit → Latenz).gemma4:26bwird als dokumentierter Latenz-Kandidat für späteres interaktives Tuning geführt (4× schneller bei 100 % Zitier-Präzision, aber schwächere Quellentreue und 3 statt 2 Fehlverweigerungen). mistral-small3.1 (Deutsch-Hypothese) ist praktisch widerlegt: 71,4 % Verweigerungs- korrektheit. muse-glimmer (2026-09-14 nachgereicht): 100 % Zitier-Präzision bei nur 1 Regenerierung (diszipliniertestes Modell), aber 8/35 falsche Verweigerungen (Überverweigerung teils trotz vorhandener Zitate) und 37,5 s mean — Rang 5 von 6, schlägt qwen3.8 in keiner Kennzahl. q-008 und q-031 verweigern alle Top-Kandidaten — Prompt-/Retrieval-Tuning-Thema, kein Modellthema.Prompt v2 + Kontext-Tuning (2026-09-14): (a) Regel 4 erlaubt Teilantworten, Regel 8 verlangt Prämisse-Korrektur mit Muster-Beispiel; (b) Kontextblöcke pro Eintrag = beste Inhaltssektion statt bester Rangfolge-Chunk („Verweise“-Sektionen zuletzt — BM25-Längennormalisierung rangiert die dünnen Navigations-Chips bevorzugt, Ursache q-008). Bestätigungslauf qwen3.8:27b (35 Fragen): Zitier-Präzision 100 %, Verweigerung korrekt 94,3 % (q-008/q-031 behoben; neuer bekannter Fall q-029 — breite Survey-Frage, sicherer Fehlermodus), erwartete Quelle 83,9 % (v1: 80,6 %), mean 34 s. Report
data/eval-qwen38-v2.json. -
M4 offen: Odoo-Integration (separater Plan nach Verifikation der Odoo-19-LLM-Module).
-
KV/RIS-Erweiterung (2026-09-15, D9/D10): Korpus 601 → 1 274 Einträge (614 WKO-KV-Dokumente
kv-kvt-…, 59 RIS-Gesetzeris-…; quelltreu generiert — D9), 14 984 Chunks. Retrieval kalibriert (D10: dense_weight 2.0, rrf_k 20, Pool 150) → Recall@8 0,923 (>0,9 ✓). Antwortmodus: Zitier-Präzision 95,2 %, Verweigerung 90,5 % — Fehlverweigerungs-Tuning (offen, laufender Arbeitsstand) fortsetzen. Intake:tools/ingest_sources.py, Registry:tools/build_registry.py. -
Tuning abgeschlossen (2026-09-15, D11): Ursache der Fehlverweige- rungen war Prompt-Overflow (lange KV-Chunks > 16k — Systemprompt trunciert weg). Fix: num_ctx 32 768, max_context_chars=90 000 (
trim_results), cross_ref_expand 6 → alle 4 Fälle geheilt (10/10 Bestätigungsläufe). Retrieval Recall@8 0,946 · Antworten: Zitier- Präzision 97,6 %, Verweigerung 97,6 % (Gate ✓), erwartete Quelle 91,9 %, Latenz mean 33 s. Offen: q-015 Branchen-Noise, q-024 transiente Flakiness (leerer Draft). -
Komplexe-Fragen Stufe 1 (2026-09-15, M6/D12): Query-Planer (Heuristik-Gate → 1-3 Sub-Queries mit Stand-Jahr und Scope), Multi- Query-Retrieval mit Per-Query-Slots und Scope-Filtern („gesetz“/„kv“). Eval (46 Fragen): Zitier-Präzision 97,8 %, Verweigerung 97,8 % (Gate ✓), erwartete Quelle 90,2 %, Latenz mean 33,5 s. Goldset 42 → 46 Fragen (q-110–113). Offen für Stufe 2: Aggregation → Map-Reduce (q-029), Rückfragen statt Verweigerung (API-first), danach Rechtsprechung-Intake (
rj-*, Lexis-md/json). -
Antworttyp-Routing Stufe 2 (2026-09-15, M6/D13): Planer-Typ
survey→ Map-Reduce (survey_blocks=16; Map destilliert je Block mit KB-ID, Reduce synthetisiert; Validierung unverändert über die Union). Systemprompt-Regel 9: Kontext-Abhängigkeit → belegte allgemeine Aussage + eine Rückfrage (API-first). q-029 geheilt (vorher hart- näckigste Fehlverweigerung), q-015 mit KV-Abhängigkeits-Hinweis. Eval: 97,8 % / 97,8 % / 90,2 %, Latenz mean 34,2 s (Map-Reduce nur bei Survey-Fragen, ~95 s). Nächster Schritt: Rechtsprechung-Intake (rj-*), dann M4 (Odoo; Privacy-Neubewertung für Lohndaten-Zugriff). -
Output-Budget + length-Retry (2026-09-15, M6/D14): q-024-Flakiness war num_predict=1024 (done_reason=length, abgeschnittene Zitationen), nicht Thinking. Fix: num_predict=2048, chat_full() mit done_reason, technischer 2×-Budget-Retry bei length. Voll-Eval erstmals mit allen Gates erfüllt: Zitier-Präzision 100 %, Verweigerung 100 %, erwartete Quelle 92,7 % (46/46), Latenz mean 39,6 s. M6 damit abgeschlossen; nächster Schritt: Rechtsprechung-Intake (
rj-*), dann M4 (Odoo; Privacy-Neubewertung für Lohndaten-Zugriff). -
Rechtsprechungs-Intake (2026-09-15, D15):
.rechtsprechung/wurde übertools/ingest_sources.py --source rjin den neuen ID-Raumrj-rjs-*übernommen: 320 RIS-OGD-Entscheidungen/Rechtssätze, 23 zitierte RIS-Normauszüge sowie 5 als nichtamtliche lexetius- Textwiedergaben markierte EuGH-Urteile. Korpus: 1.274 → 1.622 Einträge, 77 Cluster; lange Volltexte werden an Absatzgrenzen in H2- Chunks geteilt. Reindex: 16.480 Chunks, 1.496 neue bge-m3-Embeddings, 75 s. Erweiterter Retrieval-Eval (50 Fragen, inkl. q-120–123): Hit-Rate 0,956 · Recall@8 0,922 (Gate >0,9 ✓) · MRR 0,661; alle neuen Fälle gefunden. Die gezielten Antwortläufe q-120–123 sind alle zitiergültig, nicht verweigert und ohne Regenerierung; EuGH q-123 weist nach einer Prompt-Regel explizit auf die nichtamtliche Wiedergabe hin. Voller Antwortmodus-Eval (50 Fragen): 100 % Zitier-Präzision, 100 % Verweigerung korrekt, erwartete Quelle 93,3 %, Latenz mean 39,4 s / p95 82,2 s, fünf Regenerierungen; Reportdata/eval-qwen38-rj.json(lokal, unversioniert). Vor einem Commit der quellentreuen Volltexte ist die Publikations-/Lizenzfreigabe bewusst zu bestätigen; sie folgt nicht automatisch aus der Regel zu amtlichen Gesetzestexten. -
API-first-Festigung (2026-09-16, D19): Der eigenständige Service hat einen versionierten, strikt validierten Vertrag unter
/v1/ask,/v1/healthund/v1/reindex; die alten Pfade bleiben deprecated. Antworten liefern Status, Quellen, extrahierte belegte Konflikte, Rückfrage, Planungsmetadaten und einen expliziten Grounding-Block. Request-IDs erlauben technische Korrelation ohne Personenkennzeichen. Optionaler Bearer-Schutz trennt Service- und Admin-Key; ein nicht-lokaler Bind ohne Service-Key wird fail-closed abgelehnt. Health und Fehlerantworten geben keine interne Ollama-URL bzw. Exception-Details mehr aus. Der gemeinsam genutzte SQLite- Retriever ist für FastAPI-Worker-Threads serialisiert. Privacy-Grenze: v1 akzeptiert ausschließlichmode=knowledgeund keine freien Payroll-/ Mitarbeiterdatenfelder. Die spätere Odoo-Lohndatenintegration erhält einen getrennten tenant-autorisierten Vertrag; sie wird nicht durch Anhängen von Rohdaten an/v1/askumgesetzt. Vertrag und Odoo-Clientregeln:docs/API.md. -
Test-Frontend (2026-09-16, D20): FastAPI liefert ein responsives, dependency-freies same-origin UI unter
/aus. Es rendert Antworttext XSS-sicher, zeigt Health, Quellen, Konflikte, Rückfragen sowie Grounding- Metadaten und speichert weder Fragen noch Chatverlauf. Ein Service-Key wird nur vom Benutzer eingegeben und optional imsessionStoragedes Tabs gehalten. Zielbetrieb isthttp://100.103.83.12:8080/mit Bind an das Tailscale-Interface und gesetztemPV_API_KEY; CSP und weitere Security- Header schützen die UI, ohne FastAPI/docszu blockieren. -
Docker-Deployment (2026-09-16, D21): Ein gehärteter Compose-Service betreibt UI und API gemeinsam auf
100.103.83.12:8080und nutzt das externe Netzollama_default. Ollama bleibt ein separater bestehender Container; sein Netzwerk-DNS-Alias wird überOLLAMA_URLkonfiguriert. Index und KB bleiben Host-Bind-Mounts, Secrets und lokale Korpora außerhalb des Images. Der Stack verlangt einen Service-Key, läuft als konfigurierbare unprivilegierte UID/GID mit read-only Root-FS und besitzt einen Readiness-Healthcheck. Deployment und initialer Indexaufbau:docs/DOCKER.md. -
Bootstrap, Audit und Bewertungen (2026-09-16, D22): Vor jedem API-Start prüft der Container, ob Chunks und Embeddings im persistenten Index vollständig sind, und baut einen fehlenden/unvollständigen Index automatisch über Ollama auf; bei Embedding-Fehlern startet die API nicht. Interaktionen werden strukturiert in
data/audit.dbund optional als JSON nach stdout geloggt; Inhaltsprotokoll und 30-Tage-Aufbewahrung sind konfigurierbar. Eine authentisierte/v1/ratings-API sowie die UI erfassen Daumen hoch/runter und optionales Feedback pro Request-ID. Im Metadatenmodus werden auch indirekte Freitexte aus Quellen, Konflikten und Suchplan entfernt. -
Antwortkommentare (2026-09-16, D23): Unabhängig von
up|downkönnen Nutzer über eine dauerhaft sichtbare Kommentarbox mehrere Kommentare pro Antwort erfassen./v1/commentsbindet jeden Kommentar an die verifizierte Request-ID; Audit-CLI und SQLite-Log führen die datierten Kommentare mit der ursprünglichen Frage/Antwort zusammen. Im Metadatenmodus bleibt der Kommentartext aus Persistenz und stdout entfernt. -
Kostenaufstellung für Gestaltungsfragen (2026-09-16, D24): Der Decision-Support-Trigger matched jetzt auf normalisierter Frage (NFKD-Folding plus ue-Varianten) — Fragen wie "guenstigste loesung" ohne Umlaute liefen vorher in den generischen LLM-Planer und verweigerten. Bei expliziter Kostennnabsicht ("wieviel kostet mich das", "einmalig ... bar auszahlen") ergänzt der deterministische Plan zwei gesetzlich gescopte Queries: Lohnsteuer einmaliger Bezüge (lb-son-04) und Arbeitgeberbelastung (lb-sva-06, lb-lnk); die Zukunftssicherungs-Query fällt dann zugunsten der Slots weg. Systemprompt Regel 11 verlangt nun die Anwendung auf den konkreten Fall: Arbeitgeberkosten Schritt für Schritt aus belegten Sätzen, Annahmen explizit, Rückfrage nur bei wesentlichem Fehlen. think=true: Leerer Content (Antwort nur im thinking-Feld) führt zu einem einmaligen Retry ohne Thinking statt HTTP 503. Goldset +2 (q-128/q-129, beide recall=1,00); Offline-Eval 56 Fragen: Hit-Rate 0,98 · Recall@8 0,95 · MRR 0,67. Real-Läufe beider User-Fragen: verifiziert, mit belegter Rechnung (AG-SV auf 500 €, Lohnsteuer, Prämienvergleich).
-
Betrieb:
agent/README.md.
14. Phase B / M4 — Odoo-Integration (D25-Planung, Stand 2026-09-16)
Vorentscheidung (mit User): Odoo orchestriert und rechnet (System of
Record); der Agent konsumiert nur das vorgegebene Ergebnis und prüft
Plausibilität gegen die KB. Kein Rückpfad Odoo → Agent-Tools im Agenten;
die Aufweichung der Privacy-Regel 8 passiert bewusst erst hier und wird
im Agenten per Feature-Flag (PV_REVIEW_MODE, default aus) freigeschaltet.
14.1 Modul-Review (verlinkt als .oddo-module/ → ../odoo-at-payroll/addons)
- Vier Module, Odoo 19.0 (
l10n_at_hr_payroll19.0.10.0.0 auf der echtenhr_payroll-Engine;l10n_at_hr_payroll_private19.0.17.0.0;l10n_at_gemeinde_payrollBgld./GemBG;l10n_at_payroll_dokumente), LGPL-3, ~22k Zeilen Python + ~7,5k XML, ~25 Testdateien. - Kein Agenten-/HTTP-Code vorhanden (keine Controller, requests, ir.config_parameter) — M4 startet bei null, nichts ist zurückzubauen.
- Tenant-Isolation nativ:
ir.rule-Company-Regeln (GP9/AP9-Muster), Felder am Vertrag mitgroup_hr_payroll_user-Gruppen. - Rechenkern komplett:
sozialversicherung.py(SVDN/SVDG, §-49- Ausschaltungen mit Jahres-Kumulative_l10n_at_sv49_ytd, WF-Satzvektor Bundesland×Jahr, DAG),lohnsteuer.py(§ 66 kumulativ, § 67 Sechstel/ Fünftel, § 68-Freibeträge mit YTD-Verbrauch_l10n_at_st_frei_ytd),payslip_private.py(KommSt § 9, DZ §§ 122/126 WKG, FLAF-DB § 41 FLAG, SZ-Basis/Dienstzeitfaktor),sachbezuege/reisekosten(km-YTD-Split). Genau die Jahres-Salden, die im KB-Chat nur Annahmen sind, sind hier real. - Parametersystem:
hr.rule.parameter-Seeds 2026 (SV-Werte aus ÖGK- TASY-Export, gegen offiziellen Report gespiegelt; LST 2026; 2027-Rahmen), NSchAB/Wien als Company-Flags. - KV-Katalog in Odoo:
l10n.at.payroll.kv(+ versioniertekv.wert, Gruppen/Stufen, Import-Wizard per CSV-Paste mit Sprungwarnung >10 %).library_variant_idverlinkt auf die KV-Library-Variante (z. B. SI-2203, Seed SI-2203/SI-2748). Brücke gebaut (2026-09-16): die KV-Library führt bereits einen WKO-Match-Report (wko/match-report.json: wko_slug → oegb_variant_id, 407 matched / 32 low / 175 unmatched); daraus erzeugttools/build_kv_variant_map.pydie versionierte Maptools/catalogs/kv_variant_map.json(105 Varianten, 439/614 KB-Einträge abgedeckt; Docs je Variante mit kv_kvt_id+slug+doctype; Low-Confidence markiert). Odoo kann prolibrary_variant_iddie zugehörigen KB-Einträge auflösen; die 175 unmatched sind WKO-aktuelle Dokumente ohne ÖGB- Gegenstück (z. B. KV-Abschluss-News). Tests:tests/test_kv_variant_map.py(inkl. Seed-Abdeckung SI-2203/SI-2748). - Vertragsfelder für Kontext-Whitelist vorhanden:
hr.version(KV, Gruppe, Erfahrungsstufe, Überzahlung, Vordienstzeiten), Company (Bundesland, KommSt-Gemeinde, NSchAB).
14.2 Architektur M4 (geplant)
- Neues fünftes Modul
l10n_at_payroll_agent(statt Einbau in die vier bestehenden): Service-Client, Kontext-Builder, Verdict-UI. Depends:l10n_at_hr_payroll_private(+hr_payroll). - Konfiguration: Service-URL + API-Key über
ir.config_parameter, nur lesbar für eine eigene Gruppepv_agent_user; Key nie in Views/Logs. - Serverseitiger Client (
pv.agent.client, Odoo-requests, Timeout, neutrale Fehler): Aufruf/v1/askmitmode=reviewund schematisiertem Kontext — Whitelist hart kodiert (facts: key/value/note; keine Namen, SVNR, Geburtsdaten; KV-Name/Code, Bundesland/Gemeinde, Beträge, Jahres- salden, Berechnungsergebnis mit Basis).X-Request-IDaus Odoo. - Workflow „Plausibilitätsprüfung einer geplanten Auszahlung“ (Pilot):
Wizard/Dialog am Payslip/Kontext → Odoo rechnet mit den bestehenden
_l10n_at_*-Methoden auf einem Draft →computation-Objekt (Komponenten, Bemessung, Ergebnis) → Agent prüft gegen KB → strukturiertesplausibility- Verdict (verdict plausible/implausible/not-checkable + checks mit erwartet/erhalten/⚠/source_id) → Anzeige im Dialog; keine automatische Korrektur, Odoo bleibt autoritativ. - Agent-seitig (M4.2, umgesetzt 2026-09-16): v1.x-Contract
mode=reviewcontext-Feld (extra=forbid, Whitelist-Schema: facts ≤40 mit key-Muster[a-z0-9_.-]/value ≤200, computation mit components ≤40). Prompt-Addendum mit drei Beweisklassen (KB-Beleg vs. übermittelter Wert vs. Odoo-Berechnung), Injection-Abgrenzung (Kontext ist Daten, keine Anweisungen) und Verdict-Format (Abschnitt „Plausibilitätsprüfung:“ mit OK/WARN ⚠/OFFEN- Zeilen; OK/WARN brauchen KB-Beleg, sonst Regenerierung → bleibt der Abschnitt aus, fällt das Verdict ehrlich aufnot_checkablestatt die zitiergültige Fachantwort zu verwerfen). Feature-FlagPV_REVIEW_MODE(default aus — Test-Agent bleibt knowledge-only).grounding.data_scopeim Review:knowledge_base_plus_review_context. Audit speichert den Kontext incontext_json(Metadatenmodus: ohne Freitext). Teststests/test_review.py(8 Fälle, offline).
- Audit: Odoo protokolliert gesendete facts/Ergebnis + request_id;
agentseitig deckt sich
data/audit.dbüber dieselbe Request-ID. - Nicht-Ziele Phase B: keine Lohnart-Erstellung durch den Agenten, keine automatischen Buchungen, kein direkter Mitarbeiterzugriff im Chat (Mandanten-/Rollengrenze bleibt in Odoo).