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

32 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

Automatischer Index-Bootstrap, detailliertes Q&A-Audit und Antwortbewertungen (D22) sind umgesetzt; das externe Zielnetz ist auf ollama_default korrigiert. Offen bleibt der Zielhost-Build/Smoke-Test samt Verifikation des Ollama-DNS- Alias; danach kann der dünne Odoo-Client folgen.

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.
  • 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.
  • 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.