WebSearch: eingetragene Web-Quellen via Haiku vorselektieren

Bisher hatten Quellen vom Typ web_source keine praktische Wirkung auf
die Recherche - sie lagen nur als Marker in der DB. Jetzt werden sie
aktiv in den Recherche-Prompt eingebunden.

Ablauf:
1. Vor dem Hauptaufruf an Opus prüft ein günstiger Haiku-Call alle
   aktiven Web-Quellen des Tenants (plus globale) und wählt die
   thematisch passenden aus. Leere Selektion ist ausdrücklich erlaubt.
2. Die ausgewählten Domains werden dem Recherche-Prompt als
   "EINGETRAGENE WEB-QUELLEN" Block beigegeben mit der Empfehlung,
   gezielt mit "site:domain query" zu suchen, falls thematisch passend.
3. site: ist Empfehlung, kein Zwang - Claude bleibt flexibel und
   ergänzt seine sonstige Recherche.

- source_rules.get_feeds_with_metadata: SELECT um notes-Feld erweitert,
  damit der Selektor besseren Kontext zur Quelle hat.
- ResearcherAgent.select_relevant_web_sources: neuer Helper analog zu
  select_relevant_feeds, mit Skip-Optimierung wenn ≤3 Quellen.
- WEB_SOURCE_SELECTION_PROMPT: explizite Regel "lieber leer als
  pauschal alle", verhindert Token-Verschwendung.
- ResearcherAgent.search: neuer Parameter preferred_sources, beide
  Templates (RESEARCH + DEEP_RESEARCH) bekommen optionalen
  preferred_sources_block.
- Orchestrator._web_search_pipeline: Vorselektion vor researcher.search,
  Token-Usage in usage_acc, Logging der gewählten Domains.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
2026-05-01 16:45:17 +02:00
Ursprung 19038472cf
Commit 0a6208c289
3 geänderte Dateien mit 125 neuen und 6 gelöschten Zeilen

Datei anzeigen

@@ -649,14 +649,14 @@ async def get_feeds_with_metadata(tenant_id: int = None, source_type: str = "rss
try:
if tenant_id:
cursor = await db.execute(
"SELECT name, url, domain, category, COALESCE(article_count, 0) AS article_count FROM sources "
"SELECT name, url, domain, category, notes, COALESCE(article_count, 0) AS article_count FROM sources "
"WHERE source_type = ? AND status = 'active' "
"AND (tenant_id IS NULL OR tenant_id = ?)",
(source_type, tenant_id),
)
else:
cursor = await db.execute(
"SELECT name, url, domain, category, COALESCE(article_count, 0) AS article_count FROM sources "
"SELECT name, url, domain, category, notes, COALESCE(article_count, 0) AS article_count FROM sources "
"WHERE source_type = ? AND status = 'active'",
(source_type,),
)