eb1a768876
Diagnose: q-008 bekam die 'Verweise'-Navigationssektion von lb-end-03 als Kontext (BM25-Langennormalisierung bevorzugt duenne Chunks) -> inhaltlose Bloecke -> korrekte Verweigerung nach Regel 4. q-031 enthaelt eine falsche Praemisse (kein 'Mindestlohngesetz' in Oesterreich; Wissensbasis sagt: kein gesetzlich betraglich festgelegtes Mindestentgelt). Fixes: (a) retrieve.py _representative_chunk - pro Eintrag beste Inhalts- sektion (Zusammenfassung > Kernwerte > Rechtsgrundlagen > Payroll > sonstige > Verweise), Section-Swap aus dem Index falls nur 'Verweise' rangiert; (b) generate.py Prompt v2 - Regel 4 erlaubt Teilantworten, Regel 8 verlangt Prämisse-Korrektur mit Muster-Beispiel. Bestaetigungslauf qwen3.8:27b (35 Fragen): Zitier-Praezision 100 % (8 Regenerierungen, alle geheilt), Verweigerung korrekt 94,3 %, erwartete Quelle 83,9 % (v1 80,6 %), mean 34 s. q-008 + q-031 behoben; verbleibender bekannter Fall q-029 (breite Survey-Frage, sicherer Fehlermodus). Retrieval-Metriken unveraendert (Recall@8 0,952). 41 Tests gruen.
121 lines
5.9 KiB
Python
121 lines
5.9 KiB
Python
"""Gemeinsame Konstanten für die Wissensbasis-Tools (Intake + Registry).
|
||
|
||
Quellen-ID-Räume: `lb` (Lexis Briefings), `wk` (WIKU Personal), `kv`
|
||
(WKO.at Kollektivvertrag-Seiten), `ris` (RIS-Gesetze). Die bestehende
|
||
Cluster-Map (topic -> ID-Präfix) der lb/wk-Einträge wird von
|
||
`build_registry.py` aus den Layer-2-Frontmatterdaten abgeleitet; nur die
|
||
Cluster der neuen Quellen sind hier deklariert.
|
||
|
||
LAW_MAP begründet je Gesetz topic/chapter aus dem tatsächlichen Inhalt
|
||
der .ris-Dateien (Stand 2026-09-15, vgl. §-Inventar der Quelldateien):
|
||
Gesetze ohne natürlichen Cluster bekommen einen eigenen Rechtsgebiet-
|
||
Cluster oder landen bewusst im Sammel-Cluster `normen-sonstige`.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
|
||
# Alle ID-Räume des Korpus (Schema-Beschreibung für kb.json).
|
||
ID_SPACES = {
|
||
"lb": "Lexis Briefings Personalrecht (Lexis 360-Export)",
|
||
"wk": "WIKU Personal (Fachbroschüren, Arbeitsunterlagen, Casebook, Personal aktuell)",
|
||
"kv": "WKO.at — Kollektivvertrag-Dokumente (Lohn-/Gehaltsordnungen, KV-Texte)",
|
||
"ris": "RIS — Rechtsinformationssystem des Bundes (Gesetzes-§-Auschnitte)",
|
||
}
|
||
|
||
# Neue Cluster der Quellen kv/ris: slug -> (ID-Präfix, Anzeigename).
|
||
NEW_CLUSTERS = {
|
||
"kollektivvertraege": ("kvt", "Kollektivverträge (WKO.at)"),
|
||
"zivilrecht-normen": ("zvr", "Zivilrechtliche Normen (ABGB, ZPO & Co.)"),
|
||
"arbeitsvertragsrecht": ("avr", "Arbeitsvertragsrecht (AVRAG)"),
|
||
"arbeitsgerichtsbarkeit": ("agg", "Arbeits- und Sozialgerichtsbarkeit (ASGG)"),
|
||
"lohnsteuer": ("lst", "Lohnsteuer & Einkommensteuer (EStG)"),
|
||
"abgabenverfahren": ("abo", "Abgabenverfahren (BAO)"),
|
||
"normen-sonstige": ("nso", "Weitere Gesetze und Verordnungen"),
|
||
}
|
||
|
||
# Werk-Namen der neuen Quellen (Frontmatter `work`).
|
||
WORK_KV = "WKO.at – Kollektivvertrag"
|
||
WORK_RIS = "RIS – Rechtsinformationssystem des Bundes"
|
||
|
||
# .ris/<Datei ohne .md> -> (topic, chapter, lfd. Nummer im Cluster).
|
||
# Nummern sind fix (dann eingefroren); neue Gesetze hängen in ihrem Cluster
|
||
# an der höchsten Nummer an.
|
||
LAW_MAP: dict[str, tuple[str, str, int]] = {
|
||
"ABGB": ("zivilrecht-normen", "Zivilrecht", 1),
|
||
"IPRG": ("zivilrecht-normen", "Zivilrecht", 2),
|
||
"KSchG": ("zivilrecht-normen", "Verbraucherschutz", 3),
|
||
"ZPO": ("zivilrecht-normen", "Zivilrecht", 4),
|
||
"APG": ("pension", "Pensionsrecht", 1),
|
||
"APSG": ("arbeitszeitmodelle", "Arbeitsrecht (Kurzarbeit)", 1),
|
||
"APflG": ("lehrlinge", "Arbeitsrecht (Lehrverhältnis)", 1),
|
||
"BAG": ("lehrlinge", "Arbeitsrecht (Lehrverhältnis)", 2),
|
||
"ARG": ("ruhezeiten", "Arbeitsrecht", 1),
|
||
"ASGG": ("arbeitsgerichtsbarkeit", "Arbeits- und Sozialgerichtsbarkeit", 1),
|
||
"ASVG": ("beitragsrecht-asvg", "Sozialversicherungsrecht", 1),
|
||
"ASchG": ("arbeitnehmerschutz", "Arbeitnehmerschutz", 1),
|
||
"BS-V": ("arbeitnehmerschutz", "Arbeitnehmerschutz", 2),
|
||
"BauV": ("arbeitnehmerschutz", "Arbeitnehmerschutz", 3),
|
||
"AVRAG": ("arbeitsvertragsrecht", "Arbeitsrecht", 1),
|
||
"AZG": ("arbeitszeitgrenzen", "Arbeitsrecht", 1),
|
||
"AktG": ("vorstand", "Gesellschaftsrecht", 1),
|
||
"AlVG": ("altersteilzeit", "Sozialversicherungsrecht", 1),
|
||
"AngG": ("beendigungsarten", "Arbeitsrecht", 1),
|
||
"LAG": ("beendigungsarten", "Arbeitsrecht", 2),
|
||
"ArbIG": ("normen-sonstige", "Arbeitsrecht", 1),
|
||
"RStDG": ("normen-sonstige", "Dienstrecht", 2),
|
||
"StGB": ("normen-sonstige", "Strafrecht", 3),
|
||
"StPO": ("normen-sonstige", "Strafrecht", 4),
|
||
"UGB": ("normen-sonstige", "Gesellschaftsrecht", 5),
|
||
"ArbVG": ("betriebsrat", "Arbeitsverfassungsrecht", 1),
|
||
"AÜG": ("arbeitskrafteuberlassung", "Arbeitsrecht", 1),
|
||
"BAO": ("abgabenverfahren", "Steuerrecht", 1),
|
||
"BBG": ("behinderte", "Sozialrecht", 1),
|
||
"BEinstG": ("behinderte", "Sozialrecht", 2),
|
||
"BMSVG": ("vorsorgeleistungen", "Betriebliche Vorsorge", 1),
|
||
"PKG": ("vorsorgeleistungen", "Betriebliche Vorsorge", 2),
|
||
"FlexKapGG": ("vorsorgeleistungen", "Gesellschaftsrecht", 3),
|
||
"BSVG": ("bauerliche-sozialversicherung", "Sozialversicherungsrecht", 1),
|
||
"BUAG": ("endabrechnung", "Arbeitsrecht", 1),
|
||
"DHG": ("dienstnehmerhaftung", "Arbeitsrecht", 1),
|
||
"EFZG": ("krankenstand", "Arbeitsrecht", 1),
|
||
"EO": ("lohnpfandung", "Exekutionsrecht", 1),
|
||
"EStG": ("lohnsteuer", "Steuerrecht", 1),
|
||
"FLAG": ("kinderbetreuungsgeld", "Familienleistungen", 1),
|
||
"KBGG": ("kinderbetreuungsgeld", "Familienleistungen", 2),
|
||
"FamZeitbG": ("familienzeit", "Familienleistungen", 1),
|
||
"GSVG": ("gsvg-fsvg", "Sozialversicherungsrecht", 1),
|
||
"GewO": ("gewerbe", "Gewerberecht", 1),
|
||
"GlBG": ("gleichbehandlung", "Arbeitsrecht", 1),
|
||
"GmbHH_PLACEHOLDER": ("geschaftsfuhrer", "Gesellschaftsrecht", 1),
|
||
"IESG": ("insolvenz-betriebsubergang", "Insolvenzrecht", 1),
|
||
"KJBG": ("jugendliche", "Jugendschutz", 1),
|
||
"LSD-BG": ("lohndumping", "Arbeitsrecht", 1),
|
||
"MSchG": ("schwangerschaft", "Mutterschutz", 1),
|
||
"MSchV": ("schwangerschaft", "Mutterschutz", 2),
|
||
"NSchG": ("nachtschwerarbeit", "Arbeitsrecht", 1),
|
||
"Sachbezugswerteverordnung": ("sachbezuge", "Steuerrecht", 1),
|
||
"Schwerarbeitsverordnung": ("schwerarbeit", "Arbeitsrecht", 1),
|
||
"UrlG": ("urlaub", "Arbeitsrecht", 1),
|
||
"TAG": ("urlaub", "Arbeitsrecht", 2),
|
||
"VBG": ("urlaub", "Dienstrecht", 3),
|
||
"VKG": ("karenz", "Familienrecht", 1),
|
||
"ZDG": ("prasenzdienst", "Zivildienst", 1),
|
||
}
|
||
|
||
DATE_RE = re.compile(r"(\d{1,2})\.\s*(\d{1,2})\.\s*(\d{4})")
|
||
YEAR_RE = re.compile(r"(20\d\d)")
|
||
|
||
|
||
def ascii_slug(text: str) -> str:
|
||
"""Wissensbasis-Konvention: Umlaute auf Basisbuchstabe (ü→u, nicht ue),
|
||
ß→ss, Rest klein; alles außer a-z0-9 und Bindestrich fällt weg."""
|
||
repl = {"ä": "a", "ö": "o", "ü": "u", "Ä": "a", "Ö": "o", "Ü": "u", "ß": "ss"}
|
||
for k, v in repl.items():
|
||
text = text.replace(k, v)
|
||
return re.sub(r"[^a-z0-9-]+", "-", text.lower()).strip("-")
|
||
|
||
|
||
def stand_from_date(date_match: tuple[str, str, str]) -> str:
|
||
d, m, y = date_match
|
||
return f"{y}-{int(m):02d}" |