Faktencheck: Ursprungsquellen bei fehlender URL anzeigen statt blind herabstufen
Dieser Commit ist enthalten in:
@@ -469,7 +469,7 @@ class FactCheckerAgent:
|
||||
|
||||
try:
|
||||
result, usage = await call_claude(prompt)
|
||||
facts = self._parse_response(result)
|
||||
facts = self._parse_response(result, articles=articles)
|
||||
logger.info(f"Faktencheck: {len(facts)} Fakten geprüft")
|
||||
return facts, usage
|
||||
except TimeoutError:
|
||||
@@ -511,7 +511,7 @@ class FactCheckerAgent:
|
||||
|
||||
try:
|
||||
result, usage = await call_claude(prompt)
|
||||
facts = self._parse_response(result)
|
||||
facts = self._parse_response(result, articles=new_articles)
|
||||
logger.info(f"Inkrementeller Faktencheck: {len(facts)} Fakten (neu + aktualisiert)")
|
||||
return facts, usage
|
||||
except TimeoutError:
|
||||
@@ -705,27 +705,66 @@ class FactCheckerAgent:
|
||||
logger.warning("Konnte Triage-Antwort nicht als JSON parsen")
|
||||
return None
|
||||
|
||||
def _validate_facts(self, facts: list[dict]) -> list[dict]:
|
||||
"""Validiert Fakten: confirmed/established ohne URL wird herabgestuft."""
|
||||
def _validate_facts(self, facts: list[dict], articles: list[dict] = None) -> list[dict]:
|
||||
"""Validiert Fakten: Bei fehlender URL werden Ursprungsquellen aus den Artikeln ergaenzt."""
|
||||
url_pattern = re.compile(r'https?://')
|
||||
# Verfuegbare Artikel-URLs sammeln
|
||||
article_sources = []
|
||||
if articles:
|
||||
for a in articles:
|
||||
url = a.get("source_url", "")
|
||||
source = a.get("source", "")
|
||||
headline = a.get("headline_de") or a.get("headline", "")
|
||||
if url:
|
||||
article_sources.append({"url": url, "source": source, "headline": headline})
|
||||
|
||||
for fact in facts:
|
||||
status = fact.get("status", "")
|
||||
evidence = fact.get("evidence") or ""
|
||||
if status in ("confirmed", "established") and not url_pattern.search(evidence):
|
||||
# Passende Ursprungsquellen finden (Keyword-Match auf Claim)
|
||||
claim_lower = (fact.get("claim") or "").lower()
|
||||
claim_words = [w for w in claim_lower.split() if len(w) >= 4][:8]
|
||||
matched_sources = []
|
||||
for src in article_sources:
|
||||
src_text = (src["headline"] + " " + src["source"]).lower()
|
||||
matches = sum(1 for w in claim_words if w in src_text)
|
||||
if matches >= max(1, len(claim_words) // 4):
|
||||
matched_sources.append(src)
|
||||
if len(matched_sources) >= 3:
|
||||
break
|
||||
|
||||
if matched_sources:
|
||||
# Ursprungsquellen anhaengen statt herabstufen
|
||||
source_refs = "; ".join(
|
||||
f"{s['source']} ({s['url']})" for s in matched_sources
|
||||
)
|
||||
fact["evidence"] = (
|
||||
evidence.rstrip(". ") +
|
||||
". [Ursprungsquellen: " + source_refs +
|
||||
" — Quellenlinks zum Zeitpunkt der Recherche moeglicherweise nicht mehr verfuegbar]"
|
||||
)
|
||||
logger.info(
|
||||
f"Fakt '{fact.get('claim', '')[:50]}...' ergaenzt mit "
|
||||
f"{len(matched_sources)} Ursprungsquelle(n)"
|
||||
)
|
||||
else:
|
||||
# Keine passende Quelle gefunden -> herabstufen
|
||||
old_status = status
|
||||
fact["status"] = "unconfirmed" if status == "confirmed" else "unverified"
|
||||
logger.warning(
|
||||
f"Fakt herabgestuft ({old_status} -> {fact['status']}): "
|
||||
f"keine URL in Evidenz: '{fact.get('claim', '')[:60]}...'"
|
||||
f"keine URL in Evidenz und keine passende Ursprungsquelle: "
|
||||
f"'{fact.get('claim', '')[:60]}...'"
|
||||
)
|
||||
return facts
|
||||
|
||||
def _parse_response(self, response: str) -> list[dict]:
|
||||
def _parse_response(self, response: str, articles: list[dict] = None) -> list[dict]:
|
||||
"""Parst die Claude-Antwort als JSON-Array."""
|
||||
try:
|
||||
data = json.loads(response)
|
||||
if isinstance(data, list):
|
||||
return self._validate_facts(data)
|
||||
return self._validate_facts(data, articles=articles)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ MAGIC_LINK_EXPIRE_MINUTES = 10
|
||||
MAGIC_LINK_BASE_URL = os.environ.get("MAGIC_LINK_BASE_URL", "https://monitor.aegis-sight.de")
|
||||
|
||||
# Telegram (Telethon)
|
||||
TELEGRAM_API_ID = int(os.environ.get("TELEGRAM_API_ID", "2040"))
|
||||
TELEGRAM_API_HASH = os.environ.get("TELEGRAM_API_HASH", "b18441a1ff607e10a989891a5462e627")
|
||||
TELEGRAM_API_ID = int(os.environ.get("TELEGRAM_API_ID", "31330502"))
|
||||
TELEGRAM_API_HASH = os.environ.get("TELEGRAM_API_HASH", "842db7220ad2d5371269d6d88cde6a84")
|
||||
TELEGRAM_SESSION_PATH = os.environ.get("TELEGRAM_SESSION_PATH", "/home/claude-dev/.telegram/telegram_session")
|
||||
|
||||
|
||||
@@ -4557,16 +4557,16 @@ a.map-popup-article:hover {
|
||||
.tg-cat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 6px 16px;
|
||||
gap: 8px 24px;
|
||||
}
|
||||
.tg-cat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 10px;
|
||||
font-size: 13px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
padding: 3px 0;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.tg-cat-item input[type="checkbox"] {
|
||||
accent-color: var(--accent);
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren