M1+M2: RAG-Pipeline mit verbindlichem Grounding
agent/-Paket: Ingest (601 Layer-2-Eintraege -> 3005 Chunks, FTS5-BM25 + Vektoren-Cache), Hybrid-Retrieval (RRF, Stand-Boost, cross_ref-Erweiterung), Ollama-Client (embed/chat, think-Flag-Fallback, kurzes Connect-Budget), Systemprompt mit Zitierpflicht, Post-Validierung (zitierte IDs gemaess Retrieved-Set, 1x Regenerierung, dann Verweigerung), FastAPI (/ask, /health, /reindex), CLI, Goldset (31 Fragen, IDs gegen kb.json verifiziert, inkl. ATZ-Konfliktfall + 4 Verweigerungsfaelle), Eval-Suite, Test-Chat. 41 Offline-Tests gruen. Baseline BM25-only: Hit-Rate 0,871 / Recall@8 0,855 / MRR 0,476. Hybrid-Messung, Antwortmodus-Eval und Modell-Bake-off (M3) auf dem Host ausstaendig (Ollama aus der Zed-Sandbox nicht erreichbar). MEMORY.md und planung.md Umsetzungsstand aktualisiert.
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
"""Fixtures: Mini-Wissensbasis (3 Einträge), Index, Fake-Ollama-Client.
|
||||
|
||||
Alle Tests laufen offline — kein Ollama, kein Netz. Die Mini-KB folgt dem
|
||||
verbindlichen Layer-2-Schema (Frontmatter + H2-Sektionen + kb.json-Gate).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from agent.config import Config
|
||||
|
||||
DOC_ATZ = """---
|
||||
id: lb-min-01
|
||||
batch: 1
|
||||
title: "Altersteilzeit - Überblick"
|
||||
work: "Lexis Briefings Personalrecht"
|
||||
chapter: "Beschäftigungsverhältnisse"
|
||||
topic: altersteilzeit
|
||||
author: "Marek"
|
||||
stand: 2026-01
|
||||
source:
|
||||
pdf: ".lexis360/Lexis360_test_atz.pdf"
|
||||
text: ".lexis360/md/test_atz.md"
|
||||
legal_bases: ["AlVG", "AZG § 19e"]
|
||||
tags: [altersteilzeit, lohnausgleich]
|
||||
cross_refs: ["lb-min-02"]
|
||||
---
|
||||
|
||||
# Altersteilzeit – Überblick
|
||||
|
||||
*Lexis Briefings Personalrecht, Marek, Stand Jänner 2026 (lb-min-01).*
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Altersteilzeit ist eine Teilzeit, bei der der Arbeitnehmer zusätzlich zum
|
||||
Teilzeitentgelt einen Lohnausgleich erhält; das AMS ersetzt dem Arbeitgeber
|
||||
einen Teil der Zusatzkosten (Altersteilzeitgeld).
|
||||
|
||||
## Kernwerte & Fristen (Stand 2026-01)
|
||||
|
||||
| Wert / Regel | Detail |
|
||||
|---|---|
|
||||
| Ersatzquote | 28,5 % der ersetzbaren Kosten (Stand 2026-01) |
|
||||
|
||||
## Rechtsgrundlagen
|
||||
|
||||
- AZG § 19e (Kündigungszuschlag)
|
||||
|
||||
## Verweise
|
||||
|
||||
- lb-min-02
|
||||
"""
|
||||
|
||||
DOC_URL = """---
|
||||
id: lb-min-02
|
||||
batch: 1
|
||||
title: "Urlaubsanspruch und Verbrauch"
|
||||
work: "Lexis Briefings Personalrecht"
|
||||
chapter: "Urlaub & Karenzierung"
|
||||
topic: urlaub
|
||||
author: "Marek"
|
||||
stand: 2026-07
|
||||
source:
|
||||
pdf: ".lexis360/Lexis360_test_urlaub.pdf"
|
||||
text: ".lexis360/md/test_urlaub.md"
|
||||
legal_bases: ["UrlG"]
|
||||
tags: [urlaub, urlaubsentgelt]
|
||||
cross_refs: ["lb-min-01"]
|
||||
---
|
||||
|
||||
# Urlaubsanspruch und Verbrauch
|
||||
|
||||
*Lexis Briefings Personalrecht, Marek, Stand Juli 2026 (lb-min-02).*
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Der gesetzliche Urlaubsanspruch beträgt fünf Werktage Wochenurlaub je
|
||||
Dienstjahr; das Urlaubsentgelt ist wie Entgeltfortzahlung zu berechnen.
|
||||
|
||||
## Kernwerte & Fristen (Stand 2026-07)
|
||||
|
||||
| Wert / Regel | Detail |
|
||||
|---|---|
|
||||
| Mindesturlaub | 5 Wochen je Dienstjahr (Stand 2026-07) |
|
||||
|
||||
## Verweise
|
||||
|
||||
- lb-min-01
|
||||
"""
|
||||
|
||||
DOC_WIKU = """---
|
||||
id: wk-min-01
|
||||
batch: 1
|
||||
title: "WIKU Praxisfall: Lohnverrechnung"
|
||||
work: "WIKU Fachbroschüre"
|
||||
chapter: "WIKU Fachbroschüre"
|
||||
topic: lohnverrechnung
|
||||
author: "Wilhelm Kurzböck"
|
||||
stand: 2026-03
|
||||
source:
|
||||
pdf: ".wiku/test_lohnverrechnung.pdf"
|
||||
text: ".wiku/md/test_lohnverrechnung.md"
|
||||
legal_bases: ["EStG § 25"]
|
||||
tags: [lohnverrechnung, praxisfall]
|
||||
cross_refs: ["lb-min-01"]
|
||||
---
|
||||
|
||||
# WIKU Praxisfall: Lohnverrechnung
|
||||
|
||||
*WIKU Fachbroschüre, Wilhelm Kurzböck, Stand März 2026 (wk-min-01).*
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Praxisfall zur laufenden Lohnverrechnung: Abrechnungsperiode und
|
||||
Beitragsgrundlagen sind monatlich festzulegen.
|
||||
|
||||
## Verweise
|
||||
|
||||
- lb-min-01
|
||||
"""
|
||||
|
||||
MIN_DOCS = {
|
||||
"altersteilzeit_uberblick.md": DOC_ATZ,
|
||||
"urlaubsanspruch.md": DOC_URL,
|
||||
"wiku_lohnverrechnung.md": DOC_WIKU,
|
||||
}
|
||||
MIN_IDS = ["lb-min-01", "lb-min-02", "wk-min-01"]
|
||||
|
||||
|
||||
def write_mini_kb(root: Path) -> Path:
|
||||
doc = root / "dokumente"
|
||||
doc.mkdir(parents=True, exist_ok=True)
|
||||
for name, content in MIN_DOCS.items():
|
||||
(doc / name).write_text(content, encoding="utf-8")
|
||||
kb = {
|
||||
"n_entries": len(MIN_IDS),
|
||||
"entries": [{"id": i, "title": i} for i in MIN_IDS],
|
||||
}
|
||||
(root / "kb.json").write_text(
|
||||
json.dumps(kb, ensure_ascii=False, indent=1), encoding="utf-8"
|
||||
)
|
||||
return root
|
||||
|
||||
|
||||
class FakeOllama:
|
||||
"""Skriptbarer Chat-Client; embed() schlägt fehl (Dense bleibt ungetestet)."""
|
||||
|
||||
def __init__(self, answers: list[str] | None = None):
|
||||
self.answers = list(answers or [])
|
||||
self.calls = 0
|
||||
self.last_messages: list | None = None
|
||||
|
||||
def chat(self, model, messages, **kwargs):
|
||||
self.calls += 1
|
||||
self.last_messages = messages
|
||||
if not self.answers:
|
||||
raise AssertionError("FakeOllama: keine skriptierte Antwort mehr")
|
||||
return self.answers.pop(0)
|
||||
|
||||
def embed(self, model, texts):
|
||||
raise RuntimeError("embed nicht verfügbar (offline Test)")
|
||||
|
||||
def is_up(self):
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mini_kb(tmp_path):
|
||||
return write_mini_kb(tmp_path / "kb")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mini_cfg(tmp_path, mini_kb) -> Config:
|
||||
return Config(
|
||||
kb_dir=str(mini_kb),
|
||||
db_path=str(tmp_path / "index.db"),
|
||||
embed_off=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mini_index(mini_cfg) -> Config:
|
||||
from agent.ingest import build_index
|
||||
|
||||
stats = build_index(mini_cfg)
|
||||
assert stats.n_entries == 3
|
||||
assert stats.n_chunks >= 7
|
||||
assert stats.embed_error is None
|
||||
return mini_cfg
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_ollama():
|
||||
return FakeOllama
|
||||
Reference in New Issue
Block a user