feat(llm): Web-Suche per Tool-Calling (Weg 2) — Sonar + ToolCallingLLM

Frische-/Web-Such-Funktion: Das zentrale Modell entscheidet selbst via
web_search-Tool, ob es tagesaktuelle Fakten braucht, holt sie über
perplexity/sonar und formuliert die Antwort in Persona (Augment).

- SonarTool (app/tools/web_search.py): Fakten via perplexity/sonar,
  Citations als Metadaten, honest-punt-Sentinel bei Fehler/Timeout.
- ToolCallingLLM (app/providers/llm/tool_calling.py): agentischer Loop als
  LLMProvider; complete() + gestreamtes stream() mit SSE-Tool-Assembler;
  Persona- + Trigger- + Vorrang-Prompt (Tool-Ergebnis schlaegt Gedaechtnis).
- Verdrahtung: Registry-Eintrag openrouter-tools; web_search_enabled
  (global an, pro Nutzer/Profil abschaltbar) via Route-Layering;
  build_orchestrator waehlt tool-faehig vs. plain, Fallback-Kette erhalten.
- Filler: ephemerer Beruhigungssatz beim Tool-Start (sofort angezeigt UND
  gesprochen als Satz null), nie in semantic_response/History; on_tool_start
  defensiv durch die stream()-Kette gefaedelt (kein Bruch bestehender Provider).
- Modellwechsel: Standard auf mistralai/mistral-small-3.2-24b-instruct
  (tool-faehig; im Eval einziger Recall-Gate-Passer). 2501 ist tool-unfaehig.
- Eval-Harness (eval/tool_calling/): Datensatz + Runner zur Modellauswahl.

Doc: Docs/weg2-tool-calling.md. Tests: 290 gruen.
Offen (Schritt 5): Koreferenz-Vorstufe (nl-Pronomen) + Metrik-Zaehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-29 21:37:08 +02:00
commit 703a695bed
19 changed files with 1470 additions and 9 deletions

View file

@ -874,7 +874,7 @@ default_language = "de"
default_input_endpoint = "local-default"
default_output_endpoint = "local-default"
openrouter_llm_model = "mistralai/mistral-small-24b-instruct-2501"
openrouter_llm_model = "mistralai/mistral-small-3.2-24b-instruct"
llm_fallback = "openrouter"
# WICHTIG: Piper als TTS-Fallback. Greift, wenn Cartesia eine Sprache nicht kann
@ -1636,7 +1636,7 @@ Priorität der Wartezeit bei 429:
Für zuverlässigen Betrieb das **Bezahlmodell** verwenden:
```toml
openrouter_llm_model = "mistralai/mistral-small-24b-instruct-2501"
openrouter_llm_model = "mistralai/mistral-small-3.2-24b-instruct"
```
Free-Tier-Modelle (`:free`-Suffix) sind für Produktion ungeeignet — hohe