In allen Auftraegen stand fest "5-10 wichtigste Faktenaussagen", bei der Aktualisierung "3-5 neue". Die Zahl hatte damit nichts mit der Menge des Materials zu tun. Aus 16 Meldungen wurden genauso viele Fakten wie aus 91. Beide Wege lasen dieselbe Vorgabe verschieden. Der Weg ueber die Anthropic-CLI behandelte sie als Richtwert und lag zwischen 9 und 21 Fakten, ueberschritt die Zehn also regelmaessig. Das Modell auf dem EU-Weg las sie als Zielvorgabe und traf sie in neun von siebzehn Laeufen exakt. Daher wirkten die EU-Berichte so gleichfoermig, nicht die Lage bestimmte den Umfang, sondern die Zahl im Text. Bei der Ceuta-Lage bedeutete das, dass zehn Punkte eine europaweite Krise abbilden mussten, in der Opferzahlen, die schwimmende Barriere, das Sondertreffen der Innenminister, Italiens Schengen-Aussetzung, der offene Brief von 22 Staaten, die Rueckfuehrungen und mehrere Regierungsaeusserungen vorkamen. faktenspanne() berechnet den Richtwert jetzt aus der Zahl der Meldungen, grob eine Aussage je fuenf Meldungen, begrenzt auf 5 bis 20 bei der Erstpruefung und 3 bis 10 bei der Aktualisierung. 16 Meldungen ergeben weiterhin 5 bis 10, 88 Meldungen ergeben 9 bis 18. Alle Werte sind ueber Umgebungsvariablen einstellbar. Dazu die Formulierung. Die Spanne ist jetzt ausdruecklich als Richtwert und nicht als Zielvorgabe bezeichnet, mit der Erlaubnis, weniger zu nennen wenn das Material nicht mehr hergibt, und mehr wenn sonst wichtige Punkte fehlen wuerden. Betroffen sind die vier Faktencheck-Vorlagen und die Triage. Neu sind 25 Pruefungen, insgesamt laufen 279 ohne Netzzugriff und ohne Kosten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
126 Zeilen
5.1 KiB
Python
126 Zeilen
5.1 KiB
Python
"""Testet, dass die Zahl der Faktenaussagen mit dem Material waechst.
|
|
|
|
Laeuft ohne Netzzugriff und ohne Kosten, der Modellaufruf ist ersetzt.
|
|
|
|
Aufruf aus dem Projektstamm:
|
|
venv/bin/python tests/test_faktenspanne.py
|
|
"""
|
|
import asyncio
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src"))
|
|
|
|
import agents.factchecker as fcmod # noqa: E402
|
|
import agents.claude_client as cc # noqa: E402
|
|
from agents.factchecker import ( # noqa: E402
|
|
FactCheckerAgent, faktenspanne, FACTCHECK_PROMPT_TEMPLATE,
|
|
RESEARCH_FACTCHECK_PROMPT_TEMPLATE, INCREMENTAL_FACTCHECK_PROMPT_TEMPLATE,
|
|
INCREMENTAL_RESEARCH_FACTCHECK_PROMPT_TEMPLATE, TRIAGE_PROMPT_TEMPLATE,
|
|
)
|
|
from agents.claude_client import _ai_backend_var, ClaudeUsage # noqa: E402
|
|
from config import FAKTEN_MAX, FAKTEN_MIN, FAKTEN_NEU_MAX, FAKTEN_NEU_MIN # 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))
|
|
|
|
|
|
print("\nA) Die Spanne waechst mit der Zahl der Meldungen")
|
|
klein = faktenspanne(16)
|
|
mittel = faktenspanne(53)
|
|
gross = faktenspanne(88)
|
|
sehr_gross = faktenspanne(300)
|
|
pruefe("A1 wenige Meldungen ergeben die alte Spanne", klein == (5, 10), klein)
|
|
pruefe("A2 mehr Meldungen ergeben mehr Fakten", gross[1] > klein[1], (klein, gross))
|
|
pruefe("A3 die Reihenfolge stimmt",
|
|
klein[1] <= mittel[1] <= gross[1] <= sehr_gross[1],
|
|
(klein, mittel, gross, sehr_gross))
|
|
pruefe("A4 Obergrenze wird eingehalten", sehr_gross[1] == FAKTEN_MAX, sehr_gross)
|
|
pruefe("A5 Untergrenze wird eingehalten", faktenspanne(0)[0] == FAKTEN_MIN, faktenspanne(0))
|
|
pruefe("A6 Untergrenze liegt nie ueber der Obergrenze",
|
|
all(faktenspanne(n)[0] <= faktenspanne(n)[1] for n in range(0, 400, 7)))
|
|
pruefe("A7 negative Eingabe bricht nicht", faktenspanne(-5) == (5, 10), faktenspanne(-5))
|
|
|
|
print("\nB) Fuer die Aktualisierung gilt ein engerer Korridor")
|
|
neu_klein = faktenspanne(8, neu=True)
|
|
neu_gross = faktenspanne(200, neu=True)
|
|
pruefe("B1 wenige neue Meldungen ergeben die alte Spanne",
|
|
neu_klein == (FAKTEN_NEU_MIN, FAKTEN_NEU_MIN * 2), neu_klein)
|
|
pruefe("B2 Obergrenze der Aktualisierung greift", neu_gross[1] == FAKTEN_NEU_MAX, neu_gross)
|
|
pruefe("B3 Aktualisierung bleibt unter der Erstpruefung",
|
|
faktenspanne(200, neu=True)[1] < faktenspanne(200)[1])
|
|
|
|
print("\nC) Die Auftraege enthalten Platzhalter statt fester Zahlen")
|
|
for name, vorlage in (
|
|
("Erstpruefung adhoc", FACTCHECK_PROMPT_TEMPLATE),
|
|
("Erstpruefung Recherche", RESEARCH_FACTCHECK_PROMPT_TEMPLATE),
|
|
("Aktualisierung adhoc", INCREMENTAL_FACTCHECK_PROMPT_TEMPLATE),
|
|
("Aktualisierung Recherche", INCREMENTAL_RESEARCH_FACTCHECK_PROMPT_TEMPLATE),
|
|
("Triage", TRIAGE_PROMPT_TEMPLATE)):
|
|
pruefe(f"C {name} nutzt den Richtwert",
|
|
"{fakten_min}" in vorlage and "{fakten_max}" in vorlage)
|
|
pruefe(f"C {name} ohne feste Vorgabe",
|
|
"5-10" not in vorlage and "3-5 " not in vorlage and "(3-5" not in vorlage)
|
|
|
|
pruefe("C Richtwert ist ausdruecklich keine Zielvorgabe",
|
|
"Richtwert und keine" in FACTCHECK_PROMPT_TEMPLATE
|
|
and "Zielvorgabe" in FACTCHECK_PROMPT_TEMPLATE)
|
|
pruefe("C Abweichen nach oben und unten ist erlaubt",
|
|
"weniger" in FACTCHECK_PROMPT_TEMPLATE and "mehr" in FACTCHECK_PROMPT_TEMPLATE)
|
|
|
|
print("\nD) Der berechnete Wert landet im Auftrag")
|
|
letzter = {"prompt": ""}
|
|
|
|
|
|
async def fake_call_claude(prompt, tools="WebSearch,WebFetch", model=None,
|
|
raw_text=False, timeout=None):
|
|
letzter["prompt"] = prompt
|
|
return "[]", ClaudeUsage(input_tokens=10, output_tokens=5, cost_usd=0.01)
|
|
|
|
|
|
fcmod.call_claude = fake_call_claude
|
|
cc.call_claude = fake_call_claude
|
|
_ai_backend_var.set("cli")
|
|
fc = FactCheckerAgent()
|
|
|
|
|
|
def meldungen(n):
|
|
return [{"headline": f"Meldung {i}", "source": "tagesschau",
|
|
"source_url": f"https://tagesschau.de/{i}", "content_original": "t"}
|
|
for i in range(n)]
|
|
|
|
|
|
asyncio.run(fc.check("Lage", meldungen(16), "adhoc", "Deutsch"))
|
|
u, o = faktenspanne(16)
|
|
pruefe("D1 kleine Lage traegt ihren Richtwert",
|
|
f"etwa {u} bis {o} Aussagen" in letzter["prompt"],
|
|
[z for z in letzter["prompt"].splitlines() if "Richtwert" in z][:1])
|
|
|
|
asyncio.run(fc.check("Lage", meldungen(88), "adhoc", "Deutsch"))
|
|
u2, o2 = faktenspanne(88)
|
|
pruefe("D2 grosse Lage traegt einen groesseren Richtwert",
|
|
f"etwa {u2} bis {o2} Aussagen" in letzter["prompt"] and o2 > o,
|
|
[z for z in letzter["prompt"].splitlines() if "Richtwert" in z][:1])
|
|
|
|
asyncio.run(fc.check_incremental(
|
|
"Lage", meldungen(30),
|
|
[{"claim": "Ein bestehender Punkt", "status": "confirmed", "evidence": "e"}],
|
|
"adhoc", "Deutsch"))
|
|
u3, o3 = faktenspanne(30, neu=True)
|
|
pruefe("D3 Aktualisierung nutzt den engeren Korridor",
|
|
f"etwa\n {u3} bis {o3}" in letzter["prompt"] or f"{u3} bis {o3}" in letzter["prompt"],
|
|
[z for z in letzter["prompt"].splitlines() if "Richtwert" in z][:1])
|
|
|
|
print("\nErgebnis: " + str(ok) + " bestanden, " + str(fail) + " fehlgeschlagen")
|
|
sys.exit(1 if fail else 0)
|