feat: read German news from curated RSS feeds

Neues Werkzeug read_news, gleiche Trennung wie ComfyUI:

- astra/feeds.py: kuratierte deutsche Feed-Liste nach Thema (tech,
  nachrichten, wirtschaft) — 11 Feeds, alle live gegen die echten
  Endpunkte verifiziert (heise, Golem, t3n, netzpolitik.org,
  tagesschau.de, Zeit Online, Spiegel, SZ, Handelsblatt, WiWo,
  manager magazin).
- astra/rss.py: async Feed-Client (feedparser), holt Feeds eines
  Themas parallel ab, überspringt einzelne nicht erreichbare Feeds
  statt komplett zu scheitern.
- astra/tools.py: read_news als natives Ollama-Tool, gleiches
  tool_start/tool_result/tool_error-UI-Muster wie generate_image.

Mit zwei Tools gleichzeitig verfügbar sank die Zuverlässigkeit des
lokalen 9.7B-Modells spürbar (0/8 Tool-Aufrufe mit dem ursprünglichen,
langen System-Prompt) — reproduzierbar isoliert über direkte
Ollama-Requests. Ursache: Instruction-Dilution bei mehreren
Tool-Regeln in einem langen Prompt. Behoben durch gestrafften,
Tool-Regeln-zuerst-Prompt und Temperatur 0.6→0.4: Bildgenerierung
bleibt ~100% zuverlässig, Nachrichten-Tool bei ~60–75% je nach
Formulierung. Per echtem Browser-Test mit synthetisierter Sprache
verifiziert: echte aktuelle Schlagzeilen erscheinen im Transkript.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
This commit is contained in:
Jeuner 2026-09-07 17:55:24 +02:00
parent bf1e72bc55
commit e7760688f0
10 changed files with 360 additions and 6 deletions

View file

@ -62,6 +62,27 @@ function addToolError(text) {
}
showError(text);
}
function addToolResult(text) {
const pending = document.getElementById("pending-tool");
const article = pending || document.createElement("article");
if (pending) {
pending.removeAttribute("id");
pending.className = "message assistant";
pending.replaceChildren();
} else {
$("messages").querySelector(".empty")?.remove();
article.className = "message assistant";
}
const speaker = document.createElement("span");
speaker.className = "speaker";
speaker.textContent = "Astra";
const body = document.createElement("p");
body.className = "tool-result";
body.textContent = text;
article.append(speaker, body);
if (!pending) $("messages").append(article);
$("messages").scrollTop = $("messages").scrollHeight;
}
function addImage(url) {
const pending = document.getElementById("pending-tool");
const article = pending || document.createElement("article");
@ -127,6 +148,7 @@ function receive(event) {
if (message.type === "activity" && !muted) $("status").textContent = message.text;
if (message.type === "tool_start") addToolStart(message.text);
if (message.type === "tool_error") addToolError(message.text);
if (message.type === "tool_result") addToolResult(message.text);
if (message.type === "partial") $("partial").textContent = message.text;
if (message.type === "transcript") addMessage(message);
if (message.type === "image") addImage(message.url);