Files
odoo-at-payroll/pv-agent/.agents/MEMORY.md
T

37 KiB
Raw Blame History

Agent-Memory — pv-agent

Rollender Übergabe-Log für agent-Threads. Workflow: .agents/SKILL.md (agent-memory-Skill). Ergänzen, nicht überschreiben.

Current focus

D24 validiert (500-Euro-Fragen verifiziert, think-Fix, Offline-Eval Recall@8 0,95). D25-Planung steht (planung.md Abschnitt 14): Odoo orchestriert und rechnet (System of Record), Agent prüft Plausibilität, kein Rückpfad; Privacy-Regel 8 wird erst per Feature-Flag PV_REVIEW_MODE im M4 aufgeweicht. Modul-Review abgeschlossen (.oddo-module/), KV-Varianten-Mapping gebaut (tools/catalogs/kv_variant_map.json, 439/614 abgedeckt, Seeds SI-2203/ SI-2748 getestet). Als Nächstes: D25-Umsetzung — Agent-review-Modus (M4.2) und Odoo-Modul l10n_at_payroll_agent (M4.1).

Completed (2026-09-16, Modul-Review/D25-Planung)

  • .oddo-module/ verlinkt (Symlink → ../odoo-at-payroll/addons, in .gitignore aufgenommen). Review gegen M4: Odoo 19.0 (Kern 19.0.10.0.0 auf hr_payroll, Privat 19.0.17.0.0, Gemeinde-Bgld, Dokumente), LGPL-3, ~22k Zeilen Python / ~7,5k XML / ~25 Testdateien. Kein HTTP-/Controller-/Agenten-Code vorhanden — M4 startet bei null. Tenant-Isolation via ir.rule (company_ids), Feldgruppen group_hr_payroll_user. Rechenkern: sozialversicherung.py (§-49-Ausschal- tungen mit Jahres-Kumulative _l10n_at_sv49_ytd, WF-Satzvektor Bundesland ×Jahr, DAG), lohnsteuer.py (§ 66/67/68 mit YTD-Freibetragsverbrauch), payslip_private (KommSt/DZ/FLAF-DB/SZ), sachbezuege/reisekosten. Parameter aus ÖGK-TASY-Export gegen offiziellen Report gespiegelt; 2027-Rahmen. KV-Katalog l10n.at.payroll.kv mit versionierten kv.wert, Gruppen/Stufen und CSV-Import-Wizard (Sprungwarnung >10 %); library_variant_id verlinkt auf die KV-Library (SI-2203/SI-2748). D25-Planung steht in planung.md Abschnitt 14: neues Modul l10n_at_payroll_agent, ir.config_parameter plus eigene Gruppe, serverseitiger Client, hart kodierte Kontext-Whitelist (facts key/value/note, keine Personendaten), Pilot-Workflow Plausibilitätsprüfung einer geplanten Auszahlung (Odoo rechnet auf dem Draft-Payslip, Agent liefert strukturiertes Verdict), Agent-M4.2: mode=review plus context-Schema, drei Beweisklassen, Injection-Abgrenzung und Feature-Flag PV_REVIEW_MODE (default aus). Nicht-Ziele fixiert.
  • KV-Varianten-Mapping (Odoo ↔ KB, 2026-09-16): Die KV-Library führt bereits wko/match-report.json (wko_slug → oegb_variant_id; 407 matched / 32 low / 175 unmatched). tools/build_kv_variant_map.py erzeugt daraus tools/catalogs/kv_variant_map.json: 105 Varianten (98 matched, 7 low), 439/614 KB-Einträge abgedeckt; je Variante docs mit kv_kvt_id + slug + doctype als Paare, Low-Confidence markiert, Rest in unmatched_kb_entries (WKO-aktuelle Dokumente ohne ÖGB-Gegenstück, z. B. KV-Abschluss-News). Odoo kann pro library_variant_id die zugehörigen KB-Einträge auflösen; Seed-Abdeckung SI-2203/SI-2748 testet tests/test_kv_variant_map.py. Tests gesamt 98 grün.

Completed (2026-09-14)

  • Planung (planung.md): Architektur, Grounding-Regeln, Modellfeld, Meilensteine M1M4; Skill .agents/skills/pv-rag-agent/SKILL.md.
  • Commits: 25eb285 (Wissensbasis-Import), bf8191b (Planung + Skills), 2cba72a (M1+M2-Implementierung, 41 Tests).
  • Implementierung M1+M2 (agent/): kb/ingest/retrieve/generate/ ollama_client/api/cli/eval, Goldset 31 Fragen, Test-Chat.
  • Ollama-Anbindung verifiziert (Ziel-Instanz http://100.103.83.12:11435, Ollama 0.32.13): bge-m3 per API gepullt; qwen3.8:27b war bereits installiert. Hybrid-Index: 3.005 Chunks, alle eingebettet (144 s).
  • M1-Akzeptanz erreicht: Retrieval Hybrid Hit-Rate 0,968 · Recall@8 0,952 (>0,9 ✓) · MRR 0,690 (BM25-only: 0,871/0,855/0,476).
  • M2-Antwortmodus-Eval (qwen3.8:27b, Thinking aus): Zitier-Präzision 100 % (4 Regenerierungen, alle geheilt) · Verweigerung korrekt 94,3 % · erwartete Quelle zitiert 80,6 % · Latenz mean 32 s / p95 53 s. Report: data/eval-qwen38.json.
  • Der ATZ-Konfliktfall (28,5 vs. 27,5 %) wird korrekt mit ⚠ und beiden IDs beantwortet; harte Verweigerung (UStVA, Retrieval nicht leer) funktioniert.
  • Topologie geklärt: 100.103.83.12 ist die Remote-GPU-Maschine (Entwicklungsumgebung, R9700, Tailscale); die Dev-Maschine selbst hat einen eigenen, fast leeren Ollama auf localhost:11434 (dorthin ging der erste bge-m3-Pull — auf die GPU-Maschine neu gepullt). Ziel-Instanz ist ausschließlich http://100.103.83.12:11435.
  • Bake-off M3 — abgeschlossen (D7): alle 6 Kandidaten gemessen (Tabelle in planung.md Abschnitt 13). qwen3.8:27b gewinnt (100 % Zitier-Präzision, 94,3 % Verweigerung korrekt, 80,6 % erwartete Quelle, 32 s mean). gemma4:26b = dokumentierter Latenz-Kandidat (7,9 s mean, 100 % Zitier-Präzision, aber 67,7 % Quellentreue, 3 Fehlverweigerungen). mistral-small3.1: 71,4 % Verweigerungskorrektheit — Deutsch-Hypothese praktisch widerlegt. muse-glimmer:latest (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. qwen3.6:27B: 2 dauerhafte Zitierverletzungen, 10 Regenerierungen. gemma4:12B: 2 Verletzungen. Reports: data/eval-*.json. q-008/q-031 verweigern alle Top-Kandidaten — Prompt-/Retrieval-Tuning (nicht modellspezifisch).
  • Remote-GPU-Maschine: nach systemctl restart ollama (User-Aktion) liefen alle Läufe stabil; zwischen Kandidaten wurde per keep_alive: 0 entladen (Crash-Ursache war der Modell-Swap-Stress beim gemma4-Lauf).

Completed (2026-09-15)

  • KB-Erweiterung um WKO-Kollektivverträge + RIS-Gesetze (User-Auftrag): .firecrawl/kv-portal/wko-kv/docs/ (614 KV-Dokumente: Lohn-/Gehaltsordnungen mit Lohntabellen, KV-Abschlüsse, Erläuterungen, Zusatz-KVs) und .firecrawl/ris/gesetze/ (59 Gesetze, nur die im Lexis360-Bestand zitierten §-Ausschnitte, Stand 2026-09-13) → 1 274 Layer-2-Einträge (kv-kvt-001…614, ris-<prefix>-nn), 14 984 Chunks, Reindex 8,5 min (11 947 neue Embeddings, bge-m3 auf :11435).

    • D9 (2026-09-15): kv/ris-Einträge sind quellentreu generiert (keine Eigene-Worte-Kuratierung — Gesetze sind amtliche Werke, KV- Lohntabellen zahlenexakt; Konvention 2 der Wissensbasis gilt nur für die lizenzierten Quellen lb/wk). Tool-Output: tools/ingest_sources.py
      • tools/build_registry.py (Registry in diesem Repo neu gebaut — das Lexis-Tool build_lexis_kb.py lebt im Schwesterprojekt), eingefrorene Kataloge tools/catalogs/*.json (nur Metadaten, versioniert).
    • ID-Räume: kv-kvt-<nnn> (ein Cluster kollektivvertraege, Branche als Tag) und ris-<cluster-prefix>-<nn> auf der bestehenden Cluster-Map + 7 neue Cluster (kvt/zvr/avr/agg/lst/abo/nso); LAW_MAP in tools/kb_common.py dokumentiert die 59 Zuordnungen.
    • D10 (Retrieval-Kalibrierung, Goldset-Sweep 2026-09-15): Korpus- verdopplung drückte Recall@8 auf 0,851 — kalibriert auf dense_weight=2.0 (Dense-Liste relativ zu BM25 gewichtet; BM25 ist durch KV-§-Titel-Matches inflationiert), rrf_k=20, candidate_pool=150Recall@8 0,923 (>0,9 ✓), Hit-Rate 0,973 (36/37), MRR 0,667. ENV: PV_DENSE_WEIGHT/PV_RRF_K/ PV_CANDIDATE_POOL. Entry-RRF-Variante getestet: kein Mehrwert.
    • Antwortmodus-Eval (qwen3.8:27b, 42 Fragen): Zitier-Präzision 95,2 % (2 Verletzungen), Verweigerung korrekt 90,5 % (4 Fehlver- weigerungen, u.a. q-022/q-029), erwartete Quelle zitiert 83,8 %, Latenz mean 31 s / p95 53 s, 9 Regenerierungen — unter den M3- Gates (100 % / 94,3 %): Korpusverdopplung macht Grounding härter; das laufende Fehlverweigerungs-Tuning (uncommittete Änderungen in generate.py/retrieve.py/goldset.yaml, unangetastet) ist weiteres Thema. Report: data/eval-qwen38-kvris.json.
    • Goldset: +7 Fragen (q-101106 KV-Preise/§-Fragen, r-005 Branchen- Refusal) — append-only. q-021 rekalibriert: erwartete ID jetzt lb-kar-04 („Karenz - Anspruch, Beginn und Dauer“, Top-1, deckt Beginn/Dauer vollständig; lb-kar-01 nur Nachbarschaft — dokumentiert im Goldset-Note).
    • Tests: 41 → 49, alle grün (neue ID-Räume, html-Source, Konverter, LAW_MAP-Vollabdeckung, Korpus-Integrationszahl 1 274).
  • D15 (Rechtsprechungs-Intake, 2026-09-15): .rechtsprechung/ → neuer ID-Raum rj-rjs-*, gemeinsamer Cluster rechtsprechung: 320 RIS-OGD- Entscheidungen/Rechtssätze, 23 zitierte RIS-Normauszüge und 5 als nichtamtlich markierte EuGH-lexetius-Textwiedergaben. Parser: tools/ingest_sources.py --source rj; lange Volltexte werden an Absatzgrenzen in H2-Chunks geteilt. rj_catalog.json friert die IDs ein; Registry/Index und Agent-Zitatvalidierung kennen rj-*. Korpus 1.274 → 1.622 Einträge / 77 Cluster; Reindex: 16.480 Chunks, 1.496 neue Embeddings in 75 s. Retrieval-Goldset erweitert um q-120123: Hit-Rate 0,956 · Recall@8 0,922 (Gate ✓) · MRR 0,661, alle vier neuen Fälle gefunden. Gezielte Antwortläufe q-120123: alle zitiergültig, nicht verweigert, keine Regenerierung; Systemprompt verlangt für als nicht amtlich markierte Quellen die gleiche Einschränkung in der Antwort (q-123 verifiziert). Voller Antwortmodus-Eval (50 Fragen): 100 % Zitier-Präzision, 100 % Verweigerung korrekt, erwartete Quelle 93,3 %, mean 39,4 s / p95 82,2 s, 5 Regenerierungen; Report data/eval-qwen38-rj.json (lokal/unversioniert). Offline-Tests: 65 grün. Nicht als generelle Commit-Freigabe lesen: Die gelieferten OGH/VwGH/VfGH-Dateien nennen überwiegend RIS-OGD, der User sprach jedoch von Lexis/opendataloader; die Publikations-/Lizenzfreigabe der quellentreuen Volltexte ist vor Commit ausdrücklich zu prüfen. EuGH-Wiedergaben sind als nichtamtlich markiert und verweisen auf EUR-Lex.

Completed (2026-09-16)

  • D16 (Gestaltungsfragen / Retrieval-Tuning): Goldset um q-124126 erweitert (500-Euro-Zusatzzahlung, Alternativen, Mitarbeiterprämie 2026). agent/query_planner.py erkennt Decision-Support deterministisch und zerlegt in drei gesetzlich gescopte Queries: Mitarbeiterprämie samt §-49-Konflikt, Zukunftssicherung sowie Sach-/zweckgebundene Leistungen. Decision-Support läuft als specific, nicht als Survey/Map-Reduce. Retriever.search() nutzt denselben Plan im Offline-Eval; search_multi() behält Scope/Jahr auch bei einer einzelnen Query. Ergebnis (53 Goldsetfragen / 48 Retrieval-Fälle): q-124/q-125/q-126 jeweils Recall@8 1,00, gesamt Recall@8 0,9271 (Gate >0,9), Hit-Rate 0,9583, MRR 0,6158. Bekannte Altfehler q-015/q-113 bleiben unverändert.

  • Decision-Support-Grounding: Systemprompt trennt Barzahlung von zweckgebundenen Leistungen, verbietet pauschale Sieger ohne Kontext, verlangt vergleichbare Abgabendimensionen und genau eine Rückfrage. Der bekannte Konflikt Mitarbeiterprämie 2026 wird offen dargestellt: wk-akt-04 nennt SV/BV/DB/DZ/KommSt-Pflicht, lb-sva-03 ordnet die Prämie systematisch in den taxativen Katalog beitragsfreier Bezüge ein. Zusätzlich erzwingt ein semantischer Post-Validation-Gate ⚠ + beide IDs + korrekte Rollen und verhindert interne Regelverweise bzw. Quellenpriorisierung. Gezielte Real-Läufe: q-124/q-125 nicht verweigert, zitiergültig; q-126 war bereits sauber. Tests 72 grün; git diff --check sauber.

  • D16 Voll-Eval (2026-09-16): 53 Fragen mit qwen3.8:27b; Report data/eval-qwen38-decision-support.json (lokal/gitignored). Retrieval: Hit-Rate 0,9583, Recall@8 0,9271, MRR 0,6266. Antworten: Zitier-Präzision 98,11 %, Verweigerung korrekt 98,11 %, erwartete Quelle 91,67 %, mean 40,3 s / p95 98,4 s, 4 Regenerierungen. Einziger Gate-Fehler: q-125 eskalierte nach zwei semantisch unzureichenden Antworten zu UNCERTAIN (verified=false). Direkte Wiederholung von q-125 war mit einer Regenerierung vollständig, zitiergültig und stellte den Konflikt korrekt dar — verbleibend ist stochastische Gate-/Regenerierungs-Flakiness, kein Retrieval-Fehler. q-124 und q-126 bestanden im Voll-Eval.

  • D16 Stabilisierung/Bestätigung (2026-09-16): Bei vollständig ausgelassenem, aber retrieved Mitarbeiterprämien-Konflikt ergänzt die Pipeline deterministisch eine feste, ausschließlich aus lb-sva-03 und wk-akt-04 formulierte ⚠-Notiz. Eigene Konfliktversuche bleiben ungeändert und werden weiter auf Rollenfehler/Priorisierung geprüft. Der semantische Gate akzeptiert gleichwertige Verben (ordnet, listet, führt) statt fragiler exakter Wortwahl. Sonden: q-124 3/3 ohne Regenerierung, q-125 3/3 verifiziert, q-126 verifiziert. Voller Bestätigungslauf (53 Fragen), Report data/eval-qwen38-decision-support-confirm.json: Zitier-Präzision 100 %, erwartete Quelle 93,75 %, mean 40,3 s / p95 69,6 s, 3 Regenerierungen; q-124126 alle bestanden. Verweigerungskorrektheit 98,11 % nur wegen der bekannten stochastischen Survey-Fehlverweigerung q-029; direkte q-029- Wiederholung antwortete verifiziert (aber sehr langsam, 280 s).

  • q-015 fachlich rekalibriert: Eine allgemeine Tagesgeldfrage mischt zwei Ebenen: Der KV regelt vorrangig den arbeitsrechtlichen Anspruch, das EStG den steuerfreien Satz. Branchen-KV-Treffer sind deshalb korrekt; q-015 hat keine künstliche Einzel-Pflicht-ID mehr. Neue q-127 prüft ausdrücklich nur die steuerliche Höhe und findet lb-rei-09. Goldset 54 Fragen; Offline-Retrieval nun Hit-Rate 0,9792, Recall@8 0,9479, MRR 0,6653. Tests 75 grün.

  • llama.cpp-LLM-Eval (2026-09-16): Lokaler/privater OpenAI-kompatibler Server aus .env getestet (Zugangsdaten/URLs nicht dokumentiert oder versioniert); nur das LLM wurde gewechselt, Retrieval und bge-m3-Index blieben unverändert. llama.cpp benötigt chat_template_kwargs.enable_thinking=false; Chatmodell meldet sich als qwen3.8. 54-Fragen-Report: data/eval-llamacpp-qwen38.json (lokal/gitignored). Zitier-Präzision 100 %, Verweigerung korrekt 96,3 %, erwartete Quelle 91,67 %, mean 54,9 s / p95 104,8 s, 2 Regenerierungen. q-124126 bestanden alle ohne Regenerierung. Fehlverweigerungen: q-029 (bekannte Survey-Flakiness, 357 s) und q-102. q-102 ist kein LLM-Defizit: Mit Planner lieferte der KV-Scope viele ähnlich benannte KVs und nur einen irrelevanten Abschnitt von kv-kvt-197; ohne Planner beantwortete dasselbe llama.cpp-Modell die Frage in 14,8 s korrekt aus kv-kvt-056. Schluss: llama.cpp ist grounding-stabil, aber langsamer als die Ollama-Basislinie; q-102 erfordert später Planner-/Retrieval-Tuning, nicht Modell-Tuning.

  • llama.cpp-Rerun nach Server-Tuning (2026-09-16): Report data/eval-llamacpp-qwen38-rerun.json (lokal/gitignored), gleicher Aufbau wie zuvor: nur LLM über llama.cpp, Retrieval/bge-m3 unverändert. Ergebnis: Zitier-Präzision 100 %, Verweigerung korrekt 98,15 %, erwartete Quelle 95,83 %, mean 53,5 s / p95 105,4 s, 6 Regenerierungen. Gegenüber dem ersten Lauf: q-029 geheilt, erwartete Quelle +4,16 Prozentpunkte, mean ca. 1,3 s schneller; q-102 bleibt als einzige Fehlverweigerung reproduzierbar (Planner-/Kontextproblem). q-124126 bestehen weiterhin; q-125/q-126 benötigten diesmal je eine Regenerierung. Das Server-Tuning ist damit qualitativ besser, erhöht aber die Regenerierungen (2 → 6); Latenz bleibt deutlich über der Ollama-Basislinie.

  • llama.cpp Full-Stack (Chat + Embeddings, 2026-09-16): llama.cpp- Embeddings sind trotz gleicher Dimension 1024 inkompatibel zu Ollama bge-m3 (Same-Text-Cosinus nur ca. -0,004 bis 0,017); deshalb separater, lokaler Index data/index-llamacpp.db. Vollständig 16.480 Chunks; der Embedding-Server lehnt einzelne Texte > ca. 9.000 Zeichen ab, daher wurden nur für diesen Test längere Embedding-Inputs deterministisch auf 9.000 Zeichen gekürzt (KB und Produktionsindex unverändert). Indexbau ca. 17,4 min. Retrieval ohne Neukalibrierung: Hit-Rate 0,9583, Recall@8 0,9323, MRR 0,5977 (Gate >0,9 erfüllt). Vollreport data/eval-llamacpp-fullstack.json (lokal/gitignored): Zitier-Präzision 100 %, Verweigerung korrekt 94,44 %, erwartete Quelle 87,5 %, mean 44,6 s, p95 130,8 s, 3 Regenerierungen. Full-Stack ist schneller als llama.cpp-LLM + bge-m3 (53,5 s), aber fachlich schwächer: Fehlverweigerungen q-029, q-102, q-111. q-102: korrektes Dokument kv-kvt-056, aber falscher repräsentativer Abschnitt; q-111: Embedding-Retrieval verfehlt kv-kvt-001 komplett und liefert fremde Lehrlingstabellen. Decision-Support q-124126 besteht weiter. Fazit: Full-Stack ist testfähig und erfüllt das Retrieval- Recall-Gate, ersetzt bge-m3 aber noch nicht; zuerst eigene RRF-/Dense- Kalibrierung und KV-Metadaten-/Representative-Section-Tuning nötig.

  • llama.cpp Full-Stack Rerun (2026-09-16): identischer separater Index und unveränderte Parameter; Report data/eval-llamacpp-fullstack-rerun.json. Zitier-Präzision 100 %, Verweigerung korrekt 98,15 %, erwartete Quelle 91,67 %, mean 49,5 s / p95 101,2 s, nur 1 Regenerierung. Gegenüber erstem Full-Stack-Lauf: q-029 und q-111 geheilt, Fehlverweigerungen 3 → 1, erwartete Quelle +4,17 Prozentpunkte, Regenerierungen 3 → 1; Latenz dagegen 44,6 → 49,5 s. Einziger reproduzierbarer Fehler bleibt q-102 (korrektes Dokument, falscher Representative-Section-Kontext). q-124126 alle ohne Regenerierung bestanden. Full-Stack erreicht damit dieselbe Verweigerungs- korrektheit wie der aktualisierte llama.cpp-LLM+bge-m3-Lauf, bleibt aber bei erwarteter Quelle darunter (91,67 % vs. 95,83 %).

  • D17 (Runtime-Entscheidung, 2026-09-16): Nach den llama.cpp-LLM- und Full-Stack-Vergleichen bleibt der Agent bei Ollama mit qwen3.8:27b + bge-m3. Gründe: beste bzw. stabilere Gesamtbalance aus erwarteter Quellentreue, Verweigerungskorrektheit und Latenz; llama.cpp- Full-Stack zeigte embedding-spezifische KV-/Representative-Section-Fehler und höhere Flakiness. Der letzte angeforderte Full-Stack-Rerun wurde nach drei Fragen vom User abgebrochen und nicht gewertet. Lokale llama.cpp- Indizes/Reports und .env bleiben unversioniert; keine Integration in die Produktionskonfiguration.

  • Finaler Ollama-54-Eval (2026-09-16): Nach Rückkehr zum Ziel-Stack qwen3.8:27b + bge-m3, Report data/eval-qwen38-final-54.json (lokal/gitignored). Retrieval: Hit-Rate 0,9792, Recall@8 0,9479, MRR 0,6378. Antworten: Zitier-Präzision 100 %, Verweigerung korrekt 100 %, erwartete Quelle 95,83 %, mean 38,8 s, p95 67,2 s, 2 Regenerierungen. q-029 sowie q-124127 bestanden; damit beide Haupt-Gates erstmals im finalen 54er-Set vollständig erfüllt. Zwei Quellentreue- Abweichungen ohne Grounding-Verstoß: q-127 zitiert alternative Reisekosten- Quellen (lb-rei-01/03, wk-rei-01 plus KVs) statt erwarteter lb-rei-09; q-025 zitiert Betriebsübergangs-/Insolvenzquellen statt der bisherigen Expected-ID. Optional später Goldset-Expected-IDs fachlich rekalibrieren, aber kein Release-Blocker.

  • D18 (Rechtsprechungsnutzung, 2026-09-16, User-Freigabe): Auf gerichtliche Entscheidungen darf in Antworten verwiesen werden. Ihr Inhalt darf in eigenen Worten wiedergegeben und stellenweise zitiert werden. Die Agentenantwort muss weiterhin die retrieved rj-*-/RIS-KB-ID nennen, nichtamtliche Wiedergaben ausdrücklich kennzeichnen und darf keine nicht retrieved Entscheidung ergänzen. Diese Freigabe umfasst Referenz, Zusammenfassung und auszugsweise Zitate; sie ist keine pauschale Freigabe zur Veröffentlichung vollständiger quellentreuer Entscheidungstexte.

Open issues / blockers

  • Push: Remote localhost:3003 aus der Sandbox nicht erreichbar — User pusht vom Host.
  • Fehlverweigerungen — Stand nach Prompt v2: q-008 und q-031 sind behoben (Section-Priorität + Regel 8). Verbleibend: q-029 („Welche Neuerungen behandelt WIKU Personal aktuell 2026?“) — breite Survey-Frage über 12 Hefte; Kontext enthält echte Zusammenfassungen, Modell verweigert trotzdem (sicherer Fehlermodus). Mögliche spätere Hebel: Survey-Rule im Prompt, mehr Kontextblöcke oder Map-Reduce-artige Zusammenfassung. Weitere Iterationen erst mit neuen Evaluationsdaten.
  • Latenz: mean 32 s/ Antwort ist hoch (Dense 27B + große Prompts). Hebel: weniger Kontextblöcke (aktuell 8+6), schnellere Kandidaten.
  • Cloud-Modelle (*:cloud auf der Ollama-Instanz) sind für Antworten tabu (Anforderung: lokal). Nicht versehentlich konfigurieren.
  • Bake-off M3 — erledigt (siehe Completed; Entscheidung D7 in planung.md). Nach Tuning von Prompt/Retrieval: erneuter kurzer Bestätigungslauf nur mit dem Sieger.
  • M4 Odoo: native LLM-Module des konkreten Odoo-19-Stands verifizieren (keine API-Annahmen); Option A (dünnes Custom-Modul + Service-API) ist Default.
  • Nach KV/RIS-Erweiterung (2026-09-15): Antwortmodus vor dem Tuning (D10-Follow-up) unter den Gates (Zitier-Präzision 95,2 % — 2 Verlet- zungen; Verweigerung 90,5 % — 4 Fehlverweigerungen, u.a. q-022/q-029; erwartete Quelle 83,8 %) — Report data/eval-qwen38-kvris.json. D11-Fix behoben die 4 Fehlverweigerungen: Antwortmodus jetzt Zitier-Präzision 97,6 %, Verweigerung 97,6 % (>94,3 %-Gate ✓), erwartete Quelle 91,9 %, Retrieval Recall@8 0,946 — Report data/eval-qwen38-kvris-tuned.json. Verbleibend: q-015 (Branchen- Noise) und q-024 als transiente Flakiness (leerer Draft — Think-Ghost- Verdacht bei 32k-Kontext; beobachten).
  • q-015 (Tagesgelder Dienstreisen) — Branchen-Noise: 8 Branchen-KV- Einträge verdrängen lb-rei-01/09 (EStG-Taggeld-Abrechnung) aus Top-8; generische Branchen-Frage ist mehrdeutig geworden. Hebel: Frage präzisieren (Branche/Kontext) oder KV-§-Titel-Matches dämpfen.

Decisions & conventions

  • D1 (Planung): Schlanke Eigen-Pipeline statt LangChain/LlamaIndex — Grounding-Kontrolle schlägt Framework-Komfort bei 601 Dokumenten.
  • D2: Retrieval-Korpus ist nur Layer 2; Layer 1 bleibt aus Prompts (Lizenz); Antworten zitieren [kb-id] + (Stand YYYY-MM).
  • D3: Umlaut-Folding für FTS (NFKD, ß→ss) — gilt konsistent für Index und Query; ASCII-Slug-Konvention der Wissensbasis bleibt davon unberührt.
  • D4: Post-Validierung strikt: zitierte IDs ⊆ Retrieved-Set (Block-Kopf- IDs); Fließtext-Verweis-IDs sind KEINE Belege (Systemprompt-Regel 2) — Verstoß → 1× Regenerierung → Verweigerung (UNCERTAIN_MESSAGE).
  • D5: Vektoren-Tabelle ist Cache (Content-Hash × Modell), Rebuild löscht sie nicht; --no-embed setzt embed_off im Config-Copy.
  • D6: Leeres Retrieval → deterministische Verweigerung ohne LLM-Call.
  • D7 (Bake-off 2026-09-14): qwen3.8:27b ist das fixierte Antwortmodell (100 % Zitier-Präzision, 94,3 % Verweigerung korrekt). gemma4:26b als dokumentierter Latenz-Kandidat; ein Modellwechsel läuft nur erneut über das dokumentierte Protokoll (Skill).
  • D8 (Prompt-Tuning 2026-09-14): Prompt v2 + Kontext-Section- Priorität. (a) Regel 4 erlaubt Teilantworten bei unvollständiger Deckung; Regel 8 verlangt Prämisse-Korrektur statt Verweigerung (Muster-Beispiel „Mindestlohngesetz“). (b) _representative_chunk wählt pro Eintrag die beste Inhaltssektion (Zusammenfassung > Kernwerte > Rechtsgrundlagen

    Payroll > sonstige; „Verweise“ nur als letzter Rückgriff — BM25 rangiert die dünnen Navigations-Chunks bevorzugt, q-008-Ursache). Ergebnis v1→v2: Zitier-Präzision 100 % gehalten; q-008 + q-031 behoben; erwartete Quelle 80,6 → 83,9 %; Verweigerung 94,3 % (Fehler getauscht: neu q-029 — breite Survey-Frage über 12 Hefte, sicherer Fehlermodus). Report data/eval-qwen38-v2.json.

  • D9 (KV/RIS-Erweiterung, 2026-09-15): kv-/ris-Einträge sind quellentreu generiert (keine Eigene-Worte-Kuratierung — Gesetze sind amtliche Werke, KV-Lohntabellen zahlenexakt; Konvention 2 der Wissensbasis gilt nur für die lizenzierten Quellen lb/wk). Tool-Output: tools/ingest_sources.py + tools/build_registry.py (Registry in diesem Repo neu gebaut — das Lexis-Tool build_lexis_kb.py lebt im Schwesterprojekt), eingefrorene Kataloge tools/catalogs/*.json (nur Metadaten, versioniert). IDs: kv-kvt-<nnn> (ein Cluster kollektivvertraege, Branche als Tag), ris-<cluster-prefix>-<nn> auf der bestehenden Cluster-Map + 7 neue Cluster; LAW_MAP in tools/kb_common.py (59 Gesetze).
  • D10 (Retrieval-Kalibrierung, 2026-09-15): nach KV/RIS-Erweiterung (3.005 → 14 984 Chunks) dense_weight=2.0 (Dense relativ zu BM25 gewichtet — BM25 durch KV-§-Titel-Matches inflationiert), rrf_k=20, candidate_pool=150 → Recall@8 0,851 → 0,923 (>0,9 ✓), Hit-Rate 0,973, MRR 0,667. ENV: PV_DENSE_WEIGHT / PV_RRF_K / PV_CANDIDATE_POOL. Entry-RRF-Variante getestet: kein Mehrwert.
  • D11 (Prompt-Budget + Kontext-Abdeckung, 2026-09-15): lange KV-Chunks ueberlieferten num_ctx=16384 (q-022: 62,7 KB Prompt ≈ 18k Tokens) — Ollama trunciert den Systemprompt vorn, das Modell verliert die Zitierregeln („Block 8“-Zitate statt IDs → Regenerierung → Verweigerung). Fix: num_ctx 32 768, trim_results (max_context_chars=90 000, Tail- Bloecke ganz weg statt trunciert, Mindestbestand 6) und cross_ref_expand 3 → 6 (die Top-3 sind oft Branchen-KV-Bloecke mit leeren cross_refs — kuratierte Nachbarn kamen sonst nie nach). Ergebnis: alle 4 Fehlverweigerungs-Faelle (q-001/q-019/q-022/q-029) geheilt, 10/10 Bestaetigungslaeufe OK. Eval: Zitier-Praezision 97,6 %, Verweigerung 97,6 % (> 94,3 %-Gate ✓), erwartete Quelle 91,9 % (vorher 83,8 %), Retrieval Recall@8 0,946 (cross_ref-Extras bringen Expected-IDs nach), MRR 0,671. Latenz mean 33 s. Report data/eval-qwen38-kvris-tuned.json. Restrisiko: q-024 flaky (1/3 Laeufe — transienter leerer Draft, Think-Ghost-Verdacht); q-015 Branchen-Noise bleibt offen.
  • D12 (Komplexe-Fragen-Stufe 1 / M6, 2026-09-15): Query-Planer vor dem Retrieval: Heuristik-Gate (Jahreszahl, Vergleichs-/Aggregationsmarker, Laenge) entscheidet, ob ein kleiner LLM-Call die Frage in 1-3 Sub-Queries zerlegt (JSON, temp 0, num_predict 220; Fehler -> Original als Einzel- Query). Multi-Query-Retrieval: je Sub-Query BM25+Dense, Beitraege summieren; Per-Query-Slots (2 je Sub-Query) sichern jeden Frage- aspekt im Kontext — ohne sie dominieren Eintraege, die in mehreren Sub-Queries mittelgut matchen (q-113: 5 KV-Urlaubs-SS schlugen lb-url-05 (Rang 6) und ris-url-01 (>Top-12) ueber beide Sub-Queries). Scope-Filter je Sub-Query: Planer markiert "gesetz" (nur Lexis/WIKU/ RIS-Chunks) bzw. "kv" (nur Branchen-KV) — q-113 geheilt (ris-url-01/ lb-url-05 + KV-Ss im selben Kontext). Scope-Fallback: gefilterte Suche leer -> unscoped. Temporal-Intent: stand_year je Sub-Query, kv-Eintraege im Geltungsjahr erhalten temporal_boost (default 0 — FTS-Tag-Signal jahr-YYYY reichte in der Messung). Grounding unveraendert: ein Kontext aus der Union, Post-Validierung ueber die gesamte Retrieved- Menge, Verweigerungspflicht unveraendert. Eval (46 Fragen): Zitier-Praezision 97,8 %, Verweigerung 97,8 % (Gate > 94,3 % erfuellt), erwartete Quelle 90,2 %, Latenz mean 33,5 s. Temporal-Fragen laufen sogar ohne Planer gut (Jahr-Toknen matchen jahr-YYYY-Tags). Neue komplexe Fragen im Goldset: q-110 (Temporal 2023, nach Korrektur — eine 2024er-Friseur-Lohnordnung existiert im Korpus NICHT: kv-kvt-161 ist der Mantelvertrag ohne Lohntabelle; korrekt verweigert), q-111 (Temporal 2025), q-112 (Abfertigung Vergleich), q-113 (Gesetz+KV Multi-Source). Tests 50 -> 57. Offen: q-029 Survey (Stufe-2-Hebel: Map-Reduce), q-113 Planer- Scope flaky (1/2 Laeufe ohne gesetz-Marker).
  • D13 (Antworttyp-Routing / Stufe 2, 2026-09-15): (a) Planer liefert jetzt type: survey|specific; Survey-Fragen („Welche Neuerungen …“) laufen ueber Map-Reduce: breiteres Retrieval (survey_blocks=16, PV_SURVEY_BLOCKS), ein Map-Call destilliert JE Block als Stichpunkte mit seiner KB-ID (MAP_SYSTEM_PROMPT), ein Reduce-Call synthetisiert daraus die Antwort mit dem normalen Grounding-Prompt; Zitier- Validierung weiterhin strikt ueber die Retrieved-Union, leerer Map-Output -> Fallback Einzelantwort. (b) Systemprompt-Regel 9: bei wesentlicher Kontextabhaengigkeit (Branche, Bundesland, Zeitraum) belegte allgemeine Aussage + EINE Rueckfrage statt Verweigerung (API-first; Odoo-Chat kann die Rueckfrage als Follow-up nutzen). Ergebnis: q-029 geheilt (vorher jahrelange Fehlverweigerung; jetzt Teilantwort mit 5 belegten Heften, 95 s — Map-Reduce-Latenz nur bei Survey-Fragen), q-015 antwortet mit expliziter KV-Abhaengigkeit + Rueckfrage statt Branchen-Noise. Eval (46 Fragen): Zitier-Praezision 97,8 %, Verweigerung 97,8 % (Gate erfuellt), erwartete Quelle 90,2 %, Latenz mean 34,2 s. Tests 57 -> 59. Report data/eval-qwen38-stage2.json. Verbleibend: q-024 transiente Flakiness (Think-Ghost-Verdacht, 2/3 Laeufe sauber).
  • D14 (Output-Budget + length-Retry, 2026-09-15): q-024-Flakiness Ursache gefunden — NICHT Thinking (Hypothese widerlegt: thinking-Feld leer, keine Tags), sondern num_predict=1024: lange belegte Antworten wurden bei done_reason=length abgschnitten (q-024: 3/3 Sondenlaeufe) → unvollstaendige Zitationen → CITE_RE matcht partielle IDs → Verletzung → Regen-Eskalation → UNCERTAIN. Fix: (a) num_predict 1024 → 2048; (b) OllamaClient.chat_full() liefert (content, done_reason); (c) chat_with_length_retry: ein technischer Retry mit 2× num_predict bei length (zaehlt nicht als Regel-Regenerierung, laeuft auch im Map- und im Regenerierungspfad). Voll-Eval: Zitier-Praezision 100 %, Verweigerung korrekt 100 % (46/46 — alle M3-Gates erfuellt, erstmals ueber Basislinie), erwartete Quelle 92,7 %, Latenz mean 39,6 s / p95 78 s (+5 s: vollstaendige statt abgeschnittener Antworten). q-024: 4/4 stabil. Tests 59 → 60. Report data/eval-qwen38-lengthfix.json.
  • D19 (API-first-Vertrag, 2026-09-16): /v1/ask, /v1/health und /v1/reindex sind die stabilen Endpunkte; alte Pfade bleiben deprecated. Der Ask-Response enthält Status, Quellen, belegte Konflikte, extrahierte Rückfrage, Planungs- und Grounding-Metadaten sowie eine Request-ID. Optionaler Bearer-Schutz über PV_API_KEY; Reindex kann mit PV_ADMIN_API_KEY getrennt werden. Leere Keys sind nur lokaler Entwicklungsmodus. Health und öffentliche Fehler nennen keine internen Hosts/Exceptions. v1 ist zustandslos, knowledge_base_only, lehnt unbekannte JSON-Felder ab und akzeptiert keine Payroll-/Mitarbeiterobjekte. Odoo-Lohndaten erfordern später einen separaten tenant-autorisierten Vertrag mit Datensatzregeln, Datenminimierung, Audit und Cross-Tenant-Tests. Vertrag: docs/API.md. Der gemeinsame SQLite-Retriever ist durch check_same_thread=False plus RLock worker-thread-sicher. Neue Nicht-lokaler CLI-Bind ohne PV_API_KEY wird fail-closed abgelehnt. Neue API-/Concurrency-Tests erhöhen die Offline-Suite von 75 auf 82 grüne Tests.
  • D20 (Test-Frontend, 2026-09-16): FastAPI liefert unter / ein dependency-freies, responsives Frontend aus web/ aus. Es nutzt same-origin /v1/ask und /v1/health, zeigt Status, Quellen, Konflikte, Rückfragen, Suchplan und Grounding-Metadaten und hält den Verlauf nur im DOM. Ein optional gemerkter Service-Key liegt ausschließlich im sessionStorage des Tabs; kein Secret wird in die Assets injiziert. Modelltext wird ohne innerHTML gerendert. CSP gilt gezielt für UI/Assets, damit /docs nutzbar bleibt; globale Header setzen nosniff, DENY und no-referrer. Betrieb auf dem Tailscale-Interface: python -m agent.cli serve --host 100.103.83.12 mit gesetztem PV_API_KEY; UI auf Port 8080. Tests 82 → 84.
  • D21 (Docker-Compose-Deployment, 2026-09-16): compose.yaml baut einen gemeinsamen UI-/API-Container, bindet Port 8080 nur an 100.103.83.12 und trat zunächst dem externen Netz ollama-default bei; D22 korrigiert den tatsächlichen Namen auf ollama_default. Ollama wird nicht dupliziert; OLLAMA_URL ist konfigurierbar (Beispiel http://ollama:11434, Alias auf Zielhost noch zu verifizieren). data/ ist schreibbar, wissensbasis/ read-only; Root-FS read-only, Capabilities entfernt, no-new-privileges, unprivilegierter PUID:PGID, verpflichtender PV_API_KEY. .dockerignore hält .env, Index, Layer-1-Korpora und KB aus dem Image; Runtime-Image nutzt requirements-runtime.txt. Compose config --quiet, Image-Build und read-only Import-/Asset-Smoke-Test bestanden. Deployment: docs/DOCKER.md. Statische Deploymenttests erhöhen die Suite von 84 auf 87 Tests.
  • D22 (Bootstrap, Audit und Bewertungen, 2026-09-16): Das Container-CMD führt vor FastAPI agent.bootstrap aus. Ein Index gilt nur bei vorhandenen Chunks und vollständiger Vektorabdeckung für PV_EMBED_MODEL als bereit; andernfalls wird er im externen Netz ollama_default automatisch aufgebaut. Embedding-Fehler stoppen den Container statt BM25-only in Produktion zu starten. agent.audit protokolliert Interaktionen und Bewertungen in data/audit.db mit konfigurierbarer 30-Tage-Aufbewahrung und optionalen AUDIT-JSON-Zeilen nach stdout. PV_AUDIT_LOG_CONTENT=false entfernt alle Freitexte inklusive indirekter Inhalte in Suchplan/Quellen/Konflikten. Die authentisierte API /v1/ratings speichert up|down plus optionalen Kommentar; das Frontend zeigt die Funktion nur bei aktiviertem Audit. Keys/Header werden nie geloggt. Docker-JSON-Logs rotieren bei 50 MB × 5. Letzte Einträge: python -m agent.cli audit --limit 20. Offline-Suite 93 Tests; Compose- Validierung, Image-Rebuild und unprivilegierter read-only Bootstrap-Smoke-Test gegen den bestehenden vollständigen Index bestanden.
  • D23 (Antwortkommentare, 2026-09-16): Zusätzlich zu up|down besitzt jede protokollierte Antwort eine immer sichtbare Kommentarbox. Der authentisierte Endpunkt /v1/comments speichert mehrere, jeweils datierte Kommentare pro Request-ID in der separaten Audit-Tabelle comments; leere Texte werden abgewiesen, Maximallänge 2.000 Zeichen. agent.cli audit gibt Kommentare gemeinsam mit Interaktion und Bewertung aus. Bei deaktivierter Inhaltsprotokollierung bleiben Kommentarereignis und ID sichtbar, der Text wird weder in SQLite noch stdout gespeichert.
  • D24 (Kostenaufstellung + think-Fix, 2026-09-16): (a) is_decision_support matcht auf normalize_text(question); die Regex deckt neben NFKD-Folding (gunstig/losung) auch ue-Schreibweisen (guenstigste loesung) ab — vorher liefen solche Fragen in den generischen LLM-Planer und wurden verweigert. (b) Neue Trigger: "einmalig ... auszah", "bar auszah", "wieviel/wie viel ... kostet", "kostet mich", "mitarbeiterpra(e)m". (c) COST_INTENT: deterministischer Plan ergänzt cost_tax (einmalige Bezüge, Jahressechstel 6 %, 620 — trifft lb-son-04) und cost_lnk (Beitragssätze DN
    • Dienstgeberanteil, DB/DZ, KommSt — trifft lb-sva-06, lb-lnk-02/07); bei Kostennnabsicht fällt Zukunftssicherung weg (Slots), Cap 4 Queries. (d) Regel 11: konkrete Eckdaten → Arbeitgeberkosten Schritt für Schritt aus belegten Sätzen rechnen, Annahmen nennen, Rückfrage nur bei wesentlichem Fehlen. (e) generate.chat(): OllamaError bei think=true (leerer content, Antwort nur im thinking-Feld) → einmaliger Retry ohne thinking. (f) Goldset 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: Q1 (günstigste) verified, 1 Regen; Q2 (Kosten) verified mit AG-SV-Rechnung; PV_THINK=true verified (281 s).
  • Bake-off-Protokoll (Skill): Modellwechsel nur über dokumentierten Goldset-Vergleich; Kriterium: Zitier-Präzision > Verweigerungs- korrektheit > Latenz.

Files that matter right now

  • planung.md — Plan + Entscheidungspunkte (Abschnitt 12) + Stand.
  • .agents/skills/pv-rag-agent/SKILL.md — verbindliche Regeln.
  • agent/README.md — Betrieb, Konfiguration, Host-Schritte.
  • .oddo-module/ (Symlink) — Odoo-19-Addon-Bestand; planung.md Abschnitt 14 — M4/D25-Planung; l10n_at_hr_payroll_private/models/kollektivvertrag.py — KV-Katalog mit library_variant_id (Brücke zu KB).
  • docs/API.md — v1-Vertrag, Authentisierung, Privacy-Grenze und Odoo-Aufruf.
  • agent/api.py — versionierte Service-Oberfläche, UI-Auslieferung und Sicherheitsgrenzen.
  • web/ — dependency-freies Test-Frontend.
  • compose.yaml, Dockerfile, .env.example, .dockerignore — Zielhost-Stack und automatischer Bootstrap.
  • agent/bootstrap.py, agent/audit.py — Indexbereitschaft, Q&A-Log und Bewertungen.
  • docs/DOCKER.md — Deployment, Indexaufbau und Smoke-Tests.
  • agent/eval/goldset.yaml — Goldset (IDs gegen kb.json verifiziert).
  • agent/generate.py — Grounding-Kern (Prompt, Post-Validierung).
  • wissensbasis/README.md — Layer-2-Schema + Rechtsprechungs-Provenance.
  • tools/ingest_sources.py / tools/catalogs/rj_catalog.json — rj-Intake und gefrorene IDs.
  • agent/eval/goldset.yaml — q-120123; als Nächstes Antwortmodus-Eval.