feat(rss/telegram): sprach-aware Keyword-Matching für nicht-lateinische Quellen

Bisher generierte Haiku Keywords nur in DE/EN/Romaji. Japanische RSS-Feeds
(z.B. MOD-GNews mit "防衛省・自衛隊の宇宙政策") matchten daher nie, weil
"jieitai" ≠ "自衛隊". Arabische/persische Telegram-Channels matchten nur
durch Zufall (lateinische Eigennamen in Hashtags/URLs).

Drei zusammenhängende Änderungen:

1. get_feeds_with_metadata liefert primary_language pro Feed mit.
2. FEED_SELECTION_PROMPT_TEMPLATE und KEYWORD_EXTRACTION_PROMPT verlangen
   sprach-gruppierte Keywords ({de:[...], en:[...], ja:[...], ru:[...], ...}).
   "en" enthält lateinische Eigennamen (universell). Andere Sprachen werden
   nur gegen Feeds derselben Sprache gematcht.
3. RSS- und Telegram-Parser kombinieren pro Feed/Channel die "en"-Universalbegriffe
   mit den Keywords der Quellsprache. Die Spezifik-Schwelle (1-Treffer-Match)
   greift jetzt auch ab 3 Zeichen bei Non-ASCII (CJK, Arabisch, Kyrillisch).

Backward-kompatibel: flache Keyword-Listen werden weiter akzeptiert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
2026-05-21 00:29:49 +02:00
Ursprung 168fbc3987
Commit 3345743aa5
5 geänderte Dateien mit 288 neuen und 91 gelöschten Zeilen

Datei anzeigen

@@ -844,7 +844,9 @@ class AgentOrchestrator:
try:
if incident_type == "adhoc":
_src_cursor = await db.execute(
"SELECT COUNT(*) AS cnt FROM sources WHERE tenant_id = ? AND status = 'active'",
"SELECT COUNT(*) AS cnt FROM sources "
"WHERE status = 'active' "
"AND (tenant_id IS NULL OR tenant_id = ?)",
(tenant_id,),
)
_src_row = await _src_cursor.fetchone()
@@ -971,7 +973,11 @@ class AgentOrchestrator:
if pd_kw_usage:
usage_acc.add(pd_kw_usage)
articles = await pd_parser.search_feeds_selective(title, podcast_feeds, keywords=pd_keywords)
# Podcast-Parser erwartet (noch) eine flache Liste – Podcasts sind
# primaer deutschsprachig, daher reicht das gemeinsame Flatten.
from agents.researcher import flatten_keywords
pd_keywords_flat = flatten_keywords(pd_keywords)
articles = await pd_parser.search_feeds_selective(title, podcast_feeds, keywords=pd_keywords_flat or None)
logger.info(f"Podcast-Pipeline: {len(articles)} Episoden gefunden")
return articles, None
@@ -1009,7 +1015,10 @@ class AgentOrchestrator:
tg_keywords, tg_kw_usage = await tg_researcher.extract_dynamic_keywords(title, tg_headlines)
if tg_kw_usage:
usage_acc.add(tg_kw_usage)
logger.info(f"Telegram-Keywords: {tg_keywords}")
if isinstance(tg_keywords, dict):
logger.info(f"Telegram-Keywords (Sprachen): { {k: len(v) for k, v in tg_keywords.items()} }")
else:
logger.info(f"Telegram-Keywords: {tg_keywords}")
articles = await tg_parser.search_channels(title, tenant_id=tenant_id, keywords=tg_keywords, channel_ids=selected_ids)
logger.info(f"Telegram-Pipeline: {len(articles)} Nachrichten")