refactor: move tool behavior out of SYSTEM_PROMPT into each tool's own description

Auf Wunsch: wie ein Tool funktioniert (wann aufrufen, wie mit dem
Ergebnis umgehen) gehört ausschließlich in seine FunctionSchema.description,
nie in den globalen SYSTEM_PROMPT. SYSTEM_PROMPT enthält jetzt nur noch
Astras Persona, Sprachausgabe-Formatierung und eine generische
"nutze Werkzeuge statt zu erfinden"-Zeile, die kein einzelnes Tool nennt.

Gemessener Preis dieser saubereren Trennung: read_news-Zuverlässigkeit
sinkt von ca. 60-75% (Tool-Regeln zusätzlich im System-Prompt dupliziert)
auf ca. 20-50% (Regeln nur noch in der Tool-Description). generate_image
bleibt bei ~100%, vermutlich weil es die einzige naheliegende Aktion für
Bild-Anfragen ist, während read_news mit generate_image um die
Modell-Aufmerksamkeit konkurriert. Bewusst so gebaut trotz der Zahlen —
Architekturentscheidung, keine Regression, die ich für einen Fehler halte.

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 18:01:31 +02:00
parent e7760688f0
commit 9f1e87434c
3 changed files with 22 additions and 18 deletions

View file

@ -60,12 +60,16 @@ nur für die Dauer der Session, nichts wird auf Disk geschrieben.
aktuellen Nachrichten gefragt wird, und fasst die Schlagzeilen mündlich aktuellen Nachrichten gefragt wird, und fasst die Schlagzeilen mündlich
zusammen statt sie roh vorzulesen. zusammen statt sie roh vorzulesen.
Mit zwei gleichzeitig verfügbaren Tools (Bild + Nachrichten) sinkt beim Tool-Verhalten steht bewusst ausschließlich in der jeweiligen
lokalen 9,7B-Modell die Zuverlässigkeit, tatsächlich das Werkzeug `FunctionSchema.description` (siehe `astra/tools.py`), nicht im
aufzurufen statt Inhalte zu erfinden — gemessen ca. 6075 % je nach `SYSTEM_PROMPT` — eine Quelle der Wahrheit pro Werkzeug statt duplizierter
Formulierung, trotz gestrafftem System-Prompt und reduzierter Temperatur Regeln in einem wachsenden globalen Prompt. Gemessener Preis davon: mit
(`0.4`). Bildgenerierung allein bleibt zuverlässig (~100 %). Bekannte zwei gleichzeitig verfügbaren Tools (Bild + Nachrichten) ruft das lokale
Grenze eines kleinen lokalen Modells, kein Bug. 9,7B-Modell `read_news` nur noch in ca. 2050 % der Fälle tatsächlich auf
(vorher, mit Tool-Regeln zusätzlich im System-Prompt, ca. 6075 %) und
erfindet sonst Schlagzeilen. Bildgenerierung bleibt bei ~100 % zuverlässig.
Bekannte Grenze eines kleinen lokalen Modells bei Tool-Konkurrenz, kein
Bug — die saubere Trennung war eine bewusste Architekturentscheidung.
## Starten ## Starten

View file

@ -64,16 +64,12 @@ VOICE_NAMES = frozenset(voice["name"] for voice in VOICES)
SYSTEM_PROMPT = ( SYSTEM_PROMPT = (
"Du bist Astra, ein freundlicher deutschsprachiger Gesprächsassistent. " "Du bist Astra, ein freundlicher deutschsprachiger Gesprächsassistent. "
"Du hast zwei Werkzeuge: generate_image für Bilder, Grafiken oder Illustrationen, und "
"read_news für aktuelle Nachrichten. Fragt der Nutzer danach, verwende SOFORT das passende "
"Werkzeug, statt nur anzukündigen, dass du das tun wirst, und statt Inhalte selbst zu "
"erfinden. Fasse Ergebnisse von read_news danach mündlich in eigenen Worten zusammen, "
"lies sie nicht roh vor. "
"Antworte natürlich und knapp, normalerweise in ein bis drei kurzen Sätzen. " "Antworte natürlich und knapp, normalerweise in ein bis drei kurzen Sätzen. "
"Deine Antwort wird vorgelesen: kein Markdown, keine Sternchen, keine Listen. " "Deine Antwort wird vorgelesen: kein Markdown, keine Sternchen, keine Listen. "
"Sprich Zahlen und Abkürzungen verständlich aus. Stelle bei Bedarf eine kurze Rückfrage. " "Sprich Zahlen und Abkürzungen verständlich aus. Stelle bei Bedarf eine kurze Rückfrage. "
"Du hast sonst keinen Internetzugang und keinen Zugriff auf Dateien oder Apps. " "Nutze verfügbare Werkzeuge, wenn sie zur Anfrage passen, statt Inhalte selbst zu erfinden "
"Behaupte nicht, andere Aktionen ausgeführt zu haben. " "oder nur anzukündigen, dass du etwas tun wirst. Du hast sonst keinen Internetzugang und "
"keinen Zugriff auf Dateien oder Apps. Behaupte nicht, andere Aktionen ausgeführt zu haben. "
"Wenn der Nutzer ein Bild, ein Dokument oder ein PDF hochlädt, geht dessen Inhalt oder " "Wenn der Nutzer ein Bild, ein Dokument oder ein PDF hochlädt, geht dessen Inhalt oder "
"eine Textzusammenfassung als Nachricht in dieses Gespräch ein." "eine Textzusammenfassung als Nachricht in dieses Gespräch ein."
) )

View file

@ -62,8 +62,11 @@ def build_tools(config: Settings, notify: Callable[[dict], None], media_store: d
name="read_news", name="read_news",
description=( description=(
"Lies aktuelle Schlagzeilen aus kuratierten deutschen RSS-Feeds zu einem " "Lies aktuelle Schlagzeilen aus kuratierten deutschen RSS-Feeds zu einem "
"Themenbereich vor. Nutze dieses Werkzeug, wenn der Nutzer nach aktuellen " "Themenbereich vor. Rufe dieses Werkzeug sofort auf, wenn der Nutzer nach "
"Nachrichten, News oder was gerade in einem Themenbereich passiert, fragt." "aktuellen Nachrichten, News oder was gerade in einem Themenbereich passiert, "
"fragt — erfinde selbst keine Nachrichten und kündige den Abruf nicht nur an. "
"Fasse die zurückgegebenen Schlagzeilen danach mündlich in eigenen Worten "
"zusammen, statt sie roh vorzulesen."
), ),
properties={ properties={
"topic": { "topic": {
@ -82,9 +85,10 @@ def build_tools(config: Settings, notify: Callable[[dict], None], media_store: d
generate_image_schema = FunctionSchema( generate_image_schema = FunctionSchema(
name="generate_image", name="generate_image",
description=( description=(
"Erzeuge ein Bild anhand einer Beschreibung und zeige es dem Nutzer an. " "Erzeuge ein Bild anhand einer Beschreibung und zeige es dem Nutzer sofort an. "
"Nutze dieses Werkzeug, wenn der Nutzer explizit ein Bild, eine Grafik " "Rufe dieses Werkzeug auf, sobald der Nutzer ein Bild, eine Grafik oder eine "
"oder eine Illustration wünscht." "Illustration möchte — beschreibe das Bild nicht nur in Worten, erzeuge es "
"tatsächlich."
), ),
properties={ properties={
"prompt": { "prompt": {