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
zusammen statt sie roh vorzulesen.
Mit zwei gleichzeitig verfügbaren Tools (Bild + Nachrichten) sinkt beim
lokalen 9,7B-Modell die Zuverlässigkeit, tatsächlich das Werkzeug
aufzurufen statt Inhalte zu erfinden — gemessen ca. 6075 % je nach
Formulierung, trotz gestrafftem System-Prompt und reduzierter Temperatur
(`0.4`). Bildgenerierung allein bleibt zuverlässig (~100 %). Bekannte
Grenze eines kleinen lokalen Modells, kein Bug.
Tool-Verhalten steht bewusst ausschließlich in der jeweiligen
`FunctionSchema.description` (siehe `astra/tools.py`), nicht im
`SYSTEM_PROMPT` — eine Quelle der Wahrheit pro Werkzeug statt duplizierter
Regeln in einem wachsenden globalen Prompt. Gemessener Preis davon: mit
zwei gleichzeitig verfügbaren Tools (Bild + Nachrichten) ruft das lokale
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

View file

@ -64,16 +64,12 @@ VOICE_NAMES = frozenset(voice["name"] for voice in VOICES)
SYSTEM_PROMPT = (
"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. "
"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. "
"Du hast sonst keinen Internetzugang und keinen Zugriff auf Dateien oder Apps. "
"Behaupte nicht, andere Aktionen ausgeführt zu haben. "
"Nutze verfügbare Werkzeuge, wenn sie zur Anfrage passen, statt Inhalte selbst zu erfinden "
"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 "
"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",
description=(
"Lies aktuelle Schlagzeilen aus kuratierten deutschen RSS-Feeds zu einem "
"Themenbereich vor. Nutze dieses Werkzeug, wenn der Nutzer nach aktuellen "
"Nachrichten, News oder was gerade in einem Themenbereich passiert, fragt."
"Themenbereich vor. Rufe dieses Werkzeug sofort auf, wenn der Nutzer nach "
"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={
"topic": {
@ -82,9 +85,10 @@ def build_tools(config: Settings, notify: Callable[[dict], None], media_store: d
generate_image_schema = FunctionSchema(
name="generate_image",
description=(
"Erzeuge ein Bild anhand einer Beschreibung und zeige es dem Nutzer an. "
"Nutze dieses Werkzeug, wenn der Nutzer explizit ein Bild, eine Grafik "
"oder eine Illustration wünscht."
"Erzeuge ein Bild anhand einer Beschreibung und zeige es dem Nutzer sofort an. "
"Rufe dieses Werkzeug auf, sobald der Nutzer ein Bild, eine Grafik oder eine "
"Illustration möchte — beschreibe das Bild nicht nur in Worten, erzeuge es "
"tatsächlich."
),
properties={
"prompt": {