Der Recherchebericht der EU-Fassung enthielt seit zwei Laeufen nur noch den
Abschnitt ZUSAMMENFASSUNG, 1513 statt der ueblichen mehreren tausend Zeichen.
Die Abschnitte HINTERGRUND, AKTEURE, AKTUELLE LAGE, EINSCHAETZUNG und
QUELLENQUALITAET fehlten vollstaendig.
Ursache. Das Modell lieferte zuerst ein knappes JSON, bemerkte den Fehler
selbst ("Wait, I need to include the full briefing content in the summary
field. Let me redo this properly with all sections") und haengte danach eine
zweite, vollstaendige Fassung an. Unsere Auswertung nahm das erste Objekt und
verwarf alles danach. Der Bericht war also da, wir haben ihn weggeworfen.
Behoben. json_utils bekommt extract_json_objects und extract_json_arrays, die
alle vollstaendigen Bloecke einer Antwort liefern statt nur den ersten. Der
Analyzer waehlt daraus den Bericht mit dem laengsten summary, der Faktencheck
die Liste mit den meisten Fakten. Beide protokollieren, wenn mehrere Fassungen
auftauchen, damit der Fall sichtbar bleibt. Die bisherigen Einzelabfragen
extract_json_object und extract_json_array bleiben unveraendert, damit sich an
den uebrigen Aufrufstellen nichts aendert.
Gegenprobe an der echten Antwort aus Lauf 50. Vorher 1513 Zeichen mit einem
Abschnitt, jetzt 8189 Zeichen mit allen sechs.
Neu sind 16 Pruefungen, insgesamt laufen 96 ohne Netzzugriff und ohne Kosten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
108 Zeilen
4.6 KiB
Python
108 Zeilen
4.6 KiB
Python
"""Testet den Umgang mit Antworten, die mehrere Fassungen enthalten.
|
|
|
|
Modelle korrigieren sich gelegentlich selbst und haengen nach einer ersten,
|
|
knappen Antwort eine zweite, vollstaendige an. Wer nur die erste nimmt,
|
|
verliert den Grossteil des Berichts. Genau das ist in Lage 49 und 50 passiert,
|
|
dort blieb von sechs Abschnitten nur einer uebrig.
|
|
|
|
Laeuft ohne Netzzugriff und ohne Kosten.
|
|
|
|
Aufruf aus dem Projektstamm:
|
|
venv/bin/python tests/test_mehrfachantwort.py
|
|
"""
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src"))
|
|
|
|
from json_utils import ( # noqa: E402
|
|
extract_json_object, extract_json_objects, extract_json_array, extract_json_arrays,
|
|
)
|
|
from agents.analyzer import AnalyzerAgent # noqa: E402
|
|
from agents.factchecker import FactCheckerAgent # noqa: E402
|
|
|
|
ok = 0
|
|
fail = 0
|
|
|
|
|
|
def pruefe(name, bedingung, extra=""):
|
|
global ok, fail
|
|
if bedingung:
|
|
ok += 1
|
|
print(" OK " + name)
|
|
else:
|
|
fail += 1
|
|
print(" FEHL " + name + " " + str(extra))
|
|
|
|
|
|
KURZ = "## ZUSAMMENFASSUNG\\n\\n- Ein einzelner Punkt."
|
|
LANG = ("## ZUSAMMENFASSUNG\\n\\n- Punkt eins.\\n\\n"
|
|
"## HINTERGRUND\\n\\nDie Vorgeschichte.\\n\\n"
|
|
"## AKTEURE\\n\\nDie Beteiligten.\\n\\n"
|
|
"## AKTUELLE LAGE\\n\\nDer Stand.\\n\\n"
|
|
"## EINSCHÄTZUNG\\n\\nDie Bewertung.\\n\\n"
|
|
"## QUELLENQUALITÄT\\n\\nDie Belege.")
|
|
|
|
SELBSTKORREKTUR = (
|
|
'```json\n{\n "summary": "' + KURZ + '",\n "sources": [{"nr": 1}]\n}\n```\n\n'
|
|
'Wait, I need to include the full briefing content in the summary field. '
|
|
'Let me redo this properly with all sections.\n\n'
|
|
'```json\n{\n "summary": "' + LANG + '",\n "sources": [{"nr": 1}, {"nr": 2}]\n}\n```'
|
|
)
|
|
|
|
print("\nA) Alle Fassungen werden gefunden")
|
|
objekte = extract_json_objects(SELBSTKORREKTUR)
|
|
pruefe("A1 beide Fassungen erkannt", len(objekte) == 2, len(objekte))
|
|
pruefe("A2 erste Fassung ist die knappe",
|
|
"HINTERGRUND" not in objekte[0]["summary"])
|
|
pruefe("A3 zweite Fassung ist die vollstaendige",
|
|
"QUELLENQUALITÄT" in objekte[1]["summary"])
|
|
pruefe("A4 Einzelabfrage liefert weiterhin die erste",
|
|
"HINTERGRUND" not in extract_json_object(SELBSTKORREKTUR)["summary"])
|
|
|
|
print("\nB) Der Analyzer waehlt die vollstaendige Fassung")
|
|
bericht = AnalyzerAgent()._parse_response(SELBSTKORREKTUR)
|
|
pruefe("B1 Objekt erhalten", isinstance(bericht, dict))
|
|
abschnitte = re.findall(r"## *([A-ZÄÖÜ ]{4,20})", bericht.get("summary", ""))
|
|
pruefe("B2 alle sechs Abschnitte enthalten", len(abschnitte) == 6, abschnitte)
|
|
pruefe("B3 Quellen der gewaehlten Fassung", len(bericht.get("sources", [])) == 2,
|
|
bericht.get("sources"))
|
|
|
|
print("\nC) Eine einzelne saubere Antwort bleibt unveraendert")
|
|
einfach = '{"summary": "' + LANG + '", "sources": [{"nr": 1}]}'
|
|
b2 = AnalyzerAgent()._parse_response(einfach)
|
|
pruefe("C1 unveraendert geparst", isinstance(b2, dict) and "QUELLENQUALITÄT" in b2["summary"])
|
|
pruefe("C2 nur eine Fassung gefunden", len(extract_json_objects(einfach)) == 1)
|
|
|
|
print("\nD) Dasselbe fuer Faktenlisten")
|
|
FAKTEN_KURZ = '[{"claim": "Nur ein Fakt", "status": "confirmed", "evidence": "e"}]'
|
|
FAKTEN_LANG = ('[{"claim": "Erster Fakt", "status": "confirmed", "evidence": "e"},'
|
|
' {"claim": "Zweiter Fakt", "status": "confirmed", "evidence": "e"},'
|
|
' {"claim": "Dritter Fakt", "status": "confirmed", "evidence": "e"}]')
|
|
FC_KORREKTUR = (FAKTEN_KURZ + "\n\nMoment, ich habe Fakten vergessen. Hier vollstaendig:\n\n"
|
|
+ FAKTEN_LANG)
|
|
listen = extract_json_arrays(FC_KORREKTUR)
|
|
pruefe("D1 beide Listen erkannt", len(listen) == 2, len(listen))
|
|
pruefe("D2 Einzelabfrage liefert weiterhin die erste",
|
|
len(extract_json_array(FC_KORREKTUR)) == 1)
|
|
fakten = FactCheckerAgent()._parse_response(FC_KORREKTUR)
|
|
pruefe("D3 Faktencheck nimmt die vollstaendige Liste", len(fakten) == 3, len(fakten))
|
|
|
|
print("\nE) Unbrauchbare Antworten bleiben unbrauchbar")
|
|
pruefe("E1 Fliesstext liefert keine Objekte", extract_json_objects("Nur Text") == [])
|
|
pruefe("E2 Fliesstext liefert keine Listen", extract_json_arrays("Nur Text") == [])
|
|
pruefe("E3 leerer Text ist unkritisch",
|
|
extract_json_objects("") == [] and extract_json_arrays("") == [])
|
|
|
|
print("\nF) Kaputte erste Fassung, brauchbare zweite")
|
|
KAPUTT = ('{"summary": "abgeschnitten ' + "\n\n"
|
|
+ '{"summary": "' + LANG + '", "sources": []}')
|
|
objekte_f = extract_json_objects(KAPUTT)
|
|
pruefe("F1 die brauchbare Fassung wird gefunden",
|
|
any("QUELLENQUALITÄT" in str(o.get("summary", "")) for o in objekte_f),
|
|
len(objekte_f))
|
|
|
|
print("\nErgebnis: " + str(ok) + " bestanden, " + str(fail) + " fehlgeschlagen")
|
|
sys.exit(1 if fail else 0)
|