Typisierte Tool-Calls: warum unsere Agents nichts erfinden
Unter der Haube unserer Aktionsschicht: Input-Schemas, Output-Validierung und eine Handvoll harter Regeln.
Wer 2024 oder 2025 mit einem KI-Agent gebaut hat, kennt das Problem: Der Bot beschließt plötzlich, einen Tool-Aufruf zu machen, den es nicht gibt, oder greift ein Feld ab, das er erfindet, oder gibt als ID eine Zufallszahl aus. Der Fix, den alle anwenden, ist Reinforcement: 'nutze nur diese 5 Tools', 'IDs sind immer Integers', 'wenn du dir nicht sicher bist, frag den Nutzer'. Klappt manchmal. Scheitert manchmal. Und du weißt nie vorher, welches von beidem.
Bei Conveya haben wir uns entschieden, den Raum für Fehler wegzudesignen, statt ihn wegzuprompten. Der Agent sieht nur Tool-Calls, die syntaktisch gültig sind, und kann nur Output erzeugen, der von einem Schema validiert ist. Hier ist, wie das funktioniert.
Schritt 1: typisierte Tool-Definitionen
Jedes Tool hat ein JSON-Schema, das den Input definiert, verifiziert zur Compile-Zeit. Wir generieren sie aus TypeScript-Typen, keine handgeschriebenen Schemas, die mit der Implementierung auseinanderlaufen.
defineTool({
name: "shopify_get_order",
description: "Look up a Shopify order by id, email, or phone.",
input: z.object({
orderId: z.string().regex(/^\d+$/).optional(),
email: z.string().email().optional(),
phone: z.string().regex(/^\+?[0-9 ]+$/).optional(),
}).refine((v) => v.orderId || v.email || v.phone, "one identifier required"),
output: ShopifyOrder, // exhaustive type from the Shopify SDK
exec: async (input, ctx) => { ... },
});Dieses Schema wird als JSON-Schema an das LLM geschickt, über die native Tool-Use-API (Anthropics tool_use, OpenAIs function_call). Das LLM bekommt also nicht 'es gibt ein Tool, das Bestellungen abruft, überleg dir, was du brauchst', sondern 'es gibt ein Tool mit genau dieser Signatur, du MUSST eines dieser drei Felder ausfüllen'. Zods Runtime-Parser fängt den Rest.
Schritt 2: Validierung vor der Ausführung
Zwischen dem Moment, in dem das LLM sagt 'ruf dieses Tool mit diesen Args auf', und dem Moment, in dem unser Code das Tool ausführt, liegt eine Validierungsschicht. Drei Checks:
- Schema-Validierung: erfüllt der Input das Zod-Schema? Wenn nicht → Rückmeldung an das LLM mit dem Validierungsfehler, keine Ausführung.
- Authorization-Check: hat dieser Agent die Berechtigung für dieses Tool? Pro Agent kannst du Tools erlauben oder blockieren; auch Write-Tools bekommen nur dann eine Berechtigung, wenn der Merchant sie ausdrücklich aktiviert hat.
- Rate-Budget-Check: wie viele Tool-Calls hat diese Session schon gemacht? Verhindert ein Looping, bei dem der Agent endlos weiter nachschlägt, ohne den Kunden anzusprechen.
Fällt einer der drei durch, bekommt das LLM einen strukturierten Fehler zurück. Bei Schema-Fehlern mit der exakten Validierungsmeldung ('expected integer, received string for field orderId'). Das LLM korrigiert sich in 95% der Fälle beim nächsten Turn von selbst. Die restlichen 5%, meist ein halluziniertes Tool, das es nicht gibt, werden blockiert.
Schritt 3: Output-Validierung
Wenn das Tool selbst Daten zurückgibt (eine Shopify-Bestellung, ein HubSpot-Deal), validieren wir auch diesen Output. Das wirkt übertrieben, das Shopify-SDK ist doch typisiert, aber es fängt drei Probleme ab:
- Externe APIs verletzen ihr eigenes Schema. Wir hatten tatsächlich einmal ein 'price'-Feld als String statt als Number von einer 'stabilen' API.
- Neue Felder, die das LLM verwirren. Wir strippen Felder, die nicht in unserem Output-Schema stehen, damit der Agent eine fokussierte Payload bekommt.
- Encoding-Probleme (Smart Quotes, emoji-broken UTF-8). Wir normalisieren zu UTF-8 NFC, bevor es in den Kontext gelangt.
Schritt 4: Tool-Ergebnisse als typisierter Kontext
Wie das LLM das Ergebnis zurückbekommt, ist ebenfalls typisiert. Nicht als loser JSON-String, den es erst parsen muss, sondern als strukturierter tool_result-Block, in dem die Schema-Keys garantiert sind. Das verhindert, dass das LLM in späteren Turns Felder halluziniert, die nicht im Ergebnis stehen.
Was das bringt
Wir haben das in unseren internen Benchmarks gemessen (3.000 echte Support-Gespräche von vier Merchants):
- Halluzinierte Tool-Calls: von 4,2% der Turns auf 0,1%. Die restlichen 0,1% sind Edge Cases (das LLM erfindet einen Tool-Namen, der zufällig einem bestehenden ähnelt).
- Blockierte Calls: ~12% aller Tool-Aufrufe werden von der Validierung gestoppt. Davon korrigiert das LLM 95% beim nächsten Turn.
- Zeit bis zum ersten Tool-Call: 180ms im Schnitt (gemessen in apps/web/src/lib/integrations/integration-tools.ts). Die Schema-Validierung selbst ist im Sub-Millisekunden-Bereich.
- Kundenrelevante Vorfälle durch einen falschen Tool-Call: 0 in 6 Monaten Produktion.
Was das NICHT löst
Typisierte Tool-Calls verhindern, dass der Agent erfundene Aktionen ausführt. Sie verhindern nicht, dass er erfundene ANTWORTEN auf Basis korrekt abgerufener Daten gibt. Dafür haben wir eine eigene Schicht, Output-Grounding, die ich in einem folgenden Beitrag behandle.
Weiterlesen
Kundenservice automatisieren: wo fängst du an?
Kundenservice automatisieren fängt nicht bei der Software an, sondern bei deinem eigenen Postfach. Welche Fragen nimmst du zuerst, wie misst du Erfolg, und welche Anfängerfehler kosten dich unnötig Zeit.
LesenPlaybookChatbot oder KI-Agent? Der Unterschied in einfacher Sprache
Ein Chatbot folgt einem Entscheidungsbaum, ein KI-Agent liest live mit und sucht die Antwort selbst heraus. Der Unterschied in klarer Sprache, plus wann du was brauchst.
LesenPlaybook5 Fehler beim Einsatz eines KI-Chatbots (und wie du sie vermeidest)
Die meisten KI-Chatbots scheitern nicht an der Technik, sondern an fünf Entscheidungen drumherum. Von fehlendem Datenzugriff bis zum Wunsch, alles auf einmal zu automatisieren: das sind die Stolperfallen und wie du sie vermeidest.
LesenBereit, das selbst zu bauen?
Bring deinen eigenen KI-Agent live auf deiner Website, per E-Mail, WhatsApp und am Telefon. Starte heute, ohne Aufwand.