feat(recall): dynamische Google-News-Volltext-Suchfeeds pro Lage
Recall-Problem: Die Pipeline durchsuchte nur ~28 feste site:-RSS-Feeds plus Claude-WebSearch. Japanische Security-Vendor-Blogs, Fachportale und Regionalmedien (Cybertrust, ITmedia, INTERNET Watch, Reuters Japan ...) tauchten in keinem festen Feed auf. Bei der Test-Lage "Qilin Ransomware Japan" fand die Pipeline 20 Kandidaten — eine generische Google-News-JP- Suche zum selben Thema liefert 49. Fix: researcher.build_news_search_feeds baut pro Refresh einen Google-News- Volltext-Suchfeed je Sprache (news.google.com/rss/search?q=keywords&hl=..&gl=..). Query = Top-4-Keywords der jeweiligen Sprache aus der Keyword-Extraktion. Der Orchestrator haengt diese Feeds an die selektierten site:-Feeds an; sie laufen durch dieselbe Pipeline (Keyword-Match, Pre-Topic-Translate, Topic-Filter). Precision bleibt, Recall steigt. - researcher.py: build_news_search_feeds + _GNEWS_LOCALE-Tabelle. - orchestrator._rss_pipeline: Suchfeeds aus source_language_whitelist (jp_demo: ['ja']) bzw. output+research_language (normale Orgs) gebaut und an selected_feeds angehaengt. - rss_parser._apply_domain_cap: Suchfeeds (domain 'google-news-search-<lang>') bekommen Cap 25 statt 10 — sie sind der Recall-Treiber, Topic-Filter uebernimmt die Precision. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -6,6 +6,11 @@ import httpx
|
||||
from datetime import datetime, timezone
|
||||
from config import TIMEZONE, MAX_ARTICLES_PER_DOMAIN_RSS
|
||||
from source_rules import _extract_domain
|
||||
|
||||
# Cap fuer dynamische Google-News-Suchfeeds — hoeher als der normale Domain-Cap,
|
||||
# weil ein Suchfeed gezielt fuer breiten Recall gebaut wird. Topic-Filter
|
||||
# entscheidet danach ueber die Precision.
|
||||
MAX_ARTICLES_PER_DOMAIN_RSS_SEARCH = 25
|
||||
from feeds.transcript_extractors._common import html_to_text
|
||||
from services.post_refresh_qc import normalize_german_umlauts
|
||||
from agents.researcher import keywords_for_language, flatten_keywords
|
||||
@@ -276,10 +281,15 @@ class RSSParser:
|
||||
for domain, domain_articles in by_domain.items():
|
||||
# Nach Relevanz sortieren (beste zuerst)
|
||||
domain_articles.sort(key=lambda a: a.get("relevance_score", 0), reverse=True)
|
||||
kept = domain_articles[:MAX_ARTICLES_PER_DOMAIN_RSS]
|
||||
if len(domain_articles) > MAX_ARTICLES_PER_DOMAIN_RSS:
|
||||
# Dynamische Google-News-Suchfeeds ("google-news-search-<lang>") sind
|
||||
# der Recall-Treiber und bekommen einen hoeheren Cap als feste Feeds.
|
||||
cap = (MAX_ARTICLES_PER_DOMAIN_RSS_SEARCH
|
||||
if domain.startswith("google-news-search-")
|
||||
else MAX_ARTICLES_PER_DOMAIN_RSS)
|
||||
kept = domain_articles[:cap]
|
||||
if len(domain_articles) > cap:
|
||||
logger.info(
|
||||
f"Domain-Cap: {domain} von {len(domain_articles)} auf {MAX_ARTICLES_PER_DOMAIN_RSS} Artikel begrenzt"
|
||||
f"Domain-Cap: {domain} von {len(domain_articles)} auf {cap} Artikel begrenzt"
|
||||
)
|
||||
capped.extend(kept)
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren