mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 16:56:42 +00:00
fix(agent): stabilize decision support grounding
This commit is contained in:
@@ -21,6 +21,12 @@ UNCERTAIN_MESSAGE = (
|
||||
"⚠ Zu dieser Frage kann ich keine verlässlich belegte Antwort "
|
||||
"aus der Wissensbasis geben."
|
||||
)
|
||||
DECISION_CONFLICT_NOTE = (
|
||||
"⚠ Quellenkonflikt zur Mitarbeiterprämie 2026: [lb-sva-03] ordnet die "
|
||||
"Prämie systematisch in den taxativen Katalog beitragsfreier Bezüge ein; "
|
||||
"[wk-akt-04] nennt sie ausdrücklich SV- und BV-pflichtig. Der Konflikt "
|
||||
"bleibt offen."
|
||||
)
|
||||
|
||||
SYSTEM_PROMPT = """Du bist ein präziser Assistent für österreichische Personalverrechnung.
|
||||
Du beantwortest Fragen AUSSCHLIESSLICH auf Basis der nummerierten Kontextblöcke
|
||||
@@ -149,6 +155,24 @@ def validate_answer(answer: str, allowed_ids: list[str]) -> list[str]:
|
||||
return violations
|
||||
|
||||
|
||||
def ensure_decision_support_conflict(
|
||||
question: str, answer: str, allowed_ids: list[str]
|
||||
) -> str:
|
||||
"""Ergänzt nur einen vollständig ausgelassenen, retrieved Konflikt.
|
||||
|
||||
Sobald die Antwort selbst einen Konflikt/Widerspruch zu formulieren versucht,
|
||||
bleibt sie unverändert und durchläuft die semantische Validierung. Dadurch
|
||||
werden falsche Rollenzuordnungen oder Quellenpriorisierungen nicht kaschiert.
|
||||
"""
|
||||
required = {"wk-akt-04", "lb-sva-03"}
|
||||
if not is_decision_support(question) or not required.issubset(set(allowed_ids)):
|
||||
return answer
|
||||
folded = normalize_text(answer)
|
||||
if "konflikt" in folded or "widerspr" in folded:
|
||||
return answer
|
||||
return f"{answer.rstrip()}\n\n{DECISION_CONFLICT_NOTE}"
|
||||
|
||||
|
||||
def validate_decision_support_answer(
|
||||
question: str, answer: str, allowed_ids: list[str]
|
||||
) -> list[str]:
|
||||
@@ -170,9 +194,16 @@ def validate_decision_support_answer(
|
||||
"muss mit ⚠ sowie [wk-akt-04] und [lb-sva-03] offen dargestellt werden"
|
||||
)
|
||||
folded = normalize_text(answer)
|
||||
if "[lb-sva-03] ordnet" not in folded or not re.search(
|
||||
r"\[wk-akt-04\].{0,160}(?:pflicht|pflichtig)", folded, re.DOTALL
|
||||
):
|
||||
lb_role = re.search(
|
||||
r"\[lb-sva-03\].{0,220}(?:ordnet|listet|fuhrt|fuehrt)"
|
||||
r".{0,220}(?:beitragsfrei|katalog)",
|
||||
folded,
|
||||
re.DOTALL,
|
||||
)
|
||||
wk_role = re.search(
|
||||
r"\[wk-akt-04\].{0,220}(?:pflicht|pflichtig)", folded, re.DOTALL
|
||||
)
|
||||
if not lb_role or not wk_role:
|
||||
violations.append(
|
||||
"stelle die Rollen der Konfliktquellen eindeutig dar: [lb-sva-03] "
|
||||
"ordnet die Prämie in den Katalog beitragsfreier Bezüge ein; "
|
||||
@@ -342,6 +373,7 @@ def answer_question(
|
||||
# leerer Map-Output -> Fallback: messages bleibt die Einzelantwort
|
||||
|
||||
final = chat_with_length_retry(messages)
|
||||
final = ensure_decision_support_conflict(question, final, allowed)
|
||||
violations = validate_answer(final, allowed)
|
||||
violations += validate_decision_support_answer(question, final, allowed)
|
||||
regenerations = 0
|
||||
@@ -359,6 +391,7 @@ def answer_question(
|
||||
+ [{"role": "assistant", "content": final},
|
||||
{"role": "user", "content": warn}]
|
||||
)
|
||||
retry = ensure_decision_support_conflict(question, retry, allowed)
|
||||
retry_violations = validate_answer(retry, allowed)
|
||||
retry_violations += validate_decision_support_answer(question, retry, allowed)
|
||||
if not retry_violations:
|
||||
|
||||
Reference in New Issue
Block a user