Haiku-Suggester: source_id in Issues-Summary für korrekte Zuordnung

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-03-08 17:29:16 +01:00
Ursprung 45d4d35f49
Commit 5986d03209

Datei anzeigen

@@ -1,4 +1,4 @@
"""KI-gestützte Quellen-Vorschläge via Haiku.""" """KI-gestützte Quellen-Vorschläge via Haiku."""
import json import json
import logging import logging
import re import re
@@ -12,8 +12,8 @@ logger = logging.getLogger("osint.source_suggester")
async def generate_suggestions(db: aiosqlite.Connection) -> int: async def generate_suggestions(db: aiosqlite.Connection) -> int:
"""Generiert Quellen-Vorschläge basierend auf Health-Checks und Lückenanalyse.""" """Generiert Quellen-Vorschläge basierend auf Health-Checks und Lückenanalyse."""
logger.info("Starte Quellen-Vorschläge via Haiku...") logger.info("Starte Quellen-Vorschläge via Haiku...")
# 1. Aktuelle Quellen laden # 1. Aktuelle Quellen laden
cursor = await db.execute( cursor = await db.execute(
@@ -33,13 +33,13 @@ async def generate_suggestions(db: aiosqlite.Connection) -> int:
""") """)
issues = [dict(row) for row in await cursor.fetchall()] issues = [dict(row) for row in await cursor.fetchall()]
# 3. Alte pending-Vorschläge entfernen (älter als 30 Tage) # 3. Alte pending-Vorschläge entfernen (älter als 30 Tage)
await db.execute( await db.execute(
"DELETE FROM source_suggestions " "DELETE FROM source_suggestions "
"WHERE status = 'pending' AND created_at < datetime('now', '-30 days')" "WHERE status = 'pending' AND created_at < datetime('now', '-30 days')"
) )
# 4. Quellen-Zusammenfassung für Haiku # 4. Quellen-Zusammenfassung für Haiku
categories = {} categories = {}
for s in sources: for s in sources:
cat = s["category"] cat = s["category"]
@@ -63,28 +63,28 @@ async def generate_suggestions(db: aiosqlite.Connection) -> int:
issues_summary = "\n\nProbleme gefunden:\n" issues_summary = "\n\nProbleme gefunden:\n"
for issue in issues[:20]: for issue in issues[:20]:
issues_summary += ( issues_summary += (
f"- {issue['name']} ({issue['domain']}): " f"- [source_id={issue['source_id']}] {issue['name']} ({issue['domain']}): "
f"{issue['check_type']} = {issue['status']} - {issue['message']}\n" f"{issue['check_type']} = {issue['status']} - {issue['message']}\n"
) )
prompt = f"""Du bist ein OSINT-Analyst und verwaltest die Quellensammlung eines Lagebildmonitors für Sicherheitsbehörden. prompt = f"""Du bist ein OSINT-Analyst und verwaltest die Quellensammlung eines Lagebildmonitors für Sicherheitsbehörden.
Aktuelle Quellensammlung:{source_summary}{issues_summary} Aktuelle Quellensammlung:{source_summary}{issues_summary}
Aufgabe: Analysiere die Quellensammlung und schlage Verbesserungen vor. Aufgabe: Analysiere die Quellensammlung und schlage Verbesserungen vor.
Beachte: Beachte:
1. Bei Problemen (nicht erreichbar, leere Feeds): Schlage "deactivate_source" vor mit der source_id 1. Bei Problemen (nicht erreichbar, leere Feeds): Schlage "deactivate_source" vor und setze "source_id" auf die ID aus [source_id=X] in der Problemliste
2. Fehlende wichtige OSINT-Quellen: Schlage "add_source" mit konkreter RSS-Feed-URL vor 2. Fehlende wichtige OSINT-Quellen: Schlage "add_source" mit konkreter RSS-Feed-URL vor
3. Fokus auf deutschsprachige + wichtige internationale Nachrichtenquellen 3. Fokus auf deutschsprachige + wichtige internationale Nachrichtenquellen
4. Nur Quellen vorschlagen, die NICHT bereits vorhanden sind 4. Nur Quellen vorschlagen, die NICHT bereits vorhanden sind
5. Maximal 5 Vorschläge 5. Maximal 5 Vorschläge
Antworte NUR mit einem JSON-Array. Jedes Element: Antworte NUR mit einem JSON-Array. Jedes Element:
{{ {{
"type": "add_source|deactivate_source|fix_url|remove_source", "type": "add_source|deactivate_source|fix_url|remove_source",
"title": "Kurzer Titel", "title": "Kurzer Titel",
"description": "Begründung", "description": "Begründung",
"priority": "low|medium|high", "priority": "low|medium|high",
"source_id": null, "source_id": null,
"data": {{ "data": {{
@@ -104,7 +104,7 @@ Nur das JSON-Array, kein anderer Text."""
json_match = re.search(r'\[.*\]', response, re.DOTALL) json_match = re.search(r'\[.*\]', response, re.DOTALL)
if not json_match: if not json_match:
logger.warning("Keine Vorschläge von Haiku erhalten (kein JSON)") logger.warning("Keine Vorschläge von Haiku erhalten (kein JSON)")
return 0 return 0
suggestions = json.loads(json_match.group(0)) suggestions = json.loads(json_match.group(0))
@@ -148,14 +148,14 @@ Nur das JSON-Array, kein anderer Text."""
await db.commit() await db.commit()
logger.info( logger.info(
f"Quellen-Vorschläge: {count} neue Vorschläge generiert " f"Quellen-Vorschläge: {count} neue Vorschläge generiert "
f"(Haiku: {usage.input_tokens} in / {usage.output_tokens} out / " f"(Haiku: {usage.input_tokens} in / {usage.output_tokens} out / "
f"${usage.cost_usd:.4f})" f"${usage.cost_usd:.4f})"
) )
return count return count
except Exception as e: except Exception as e:
logger.error(f"Fehler bei Quellen-Vorschlägen: {e}", exc_info=True) logger.error(f"Fehler bei Quellen-Vorschlägen: {e}", exc_info=True)
return 0 return 0
@@ -202,7 +202,7 @@ async def apply_suggestion(
(url,), (url,),
) )
if await cursor.fetchone(): if await cursor.fetchone():
result["action"] = "übersprungen (URL bereits vorhanden)" result["action"] = "übersprungen (URL bereits vorhanden)"
new_status = "rejected" new_status = "rejected"
else: else:
await db.execute( await db.execute(
@@ -214,7 +214,7 @@ async def apply_suggestion(
) )
result["action"] = f"Quelle '{name}' angelegt" result["action"] = f"Quelle '{name}' angelegt"
else: else:
result["action"] = "übersprungen (keine URL)" result["action"] = "übersprungen (keine URL)"
new_status = "rejected" new_status = "rejected"
elif stype == "deactivate_source": elif stype == "deactivate_source":
@@ -226,7 +226,7 @@ async def apply_suggestion(
) )
result["action"] = "Quelle deaktiviert" result["action"] = "Quelle deaktiviert"
else: else:
result["action"] = "übersprungen (keine source_id)" result["action"] = "übersprungen (keine source_id)"
elif stype == "remove_source": elif stype == "remove_source":
source_id = suggestion["source_id"] source_id = suggestion["source_id"]
@@ -234,9 +234,9 @@ async def apply_suggestion(
await db.execute( await db.execute(
"DELETE FROM sources WHERE id = ?", (source_id,), "DELETE FROM sources WHERE id = ?", (source_id,),
) )
result["action"] = "Quelle gelöscht" result["action"] = "Quelle gelöscht"
else: else:
result["action"] = "übersprungen (keine source_id)" result["action"] = "übersprungen (keine source_id)"
elif stype == "fix_url": elif stype == "fix_url":
source_id = suggestion["source_id"] source_id = suggestion["source_id"]
@@ -248,7 +248,7 @@ async def apply_suggestion(
) )
result["action"] = f"URL aktualisiert auf {new_url}" result["action"] = f"URL aktualisiert auf {new_url}"
else: else:
result["action"] = "übersprungen (keine source_id oder URL)" result["action"] = "übersprungen (keine source_id oder URL)"
await db.execute( await db.execute(
"UPDATE source_suggestions SET status = ?, reviewed_at = CURRENT_TIMESTAMP " "UPDATE source_suggestions SET status = ?, reviewed_at = CURRENT_TIMESTAMP "