Ablauf eines Tool-Aufrufs
Was zwischen "Modell wählt Tool" und "Ergebnis geht zurück" im Detail passiert.
Wenn das LLM entscheidet, ein Tool aufzurufen, übernimmt der ToolRunner. Er ist der Leim zwischen deklarativer Tool-Config und tatsächlichem Request.
Die Pipeline auf einen Blick
args ──▶ preprocess ──▶ API-Call ──▶ mapping ──▶ postprocess ──▶ handler ──▶ result
Jeder Schritt ist optional (außer mindestens einem von API-Call und handler). Du kannst also ein Tool bauen, das nur aus handler besteht, oder eines, das nur API-Call + Mapping ist.
Schritt für Schritt
1. Args entgegennehmen
Das Modell liefert ein JSON-Objekt, das gegen das parameters-Schema validiert wird. Fehlt ein required Feld, erhält das Modell eine Fehlermeldung zurück und versucht es erneut.
2. Placeholder auflösen
Vor dem API-Call ersetzt der Runner bestimmte Platzhalter in den Argumenten:
| Platzhalter | Ersetzt durch |
|---|---|
<PDF:foo.pdf> |
Volltext-Inhalt der hochgeladenen Datei |
<PDF_CONTENT_AVAILABLE> |
true/false — gibt es überhaupt PDFs im Chat? |
{{user_message_count}} |
Anzahl Nutzer-Nachrichten in der Session |
Siehe Platzhalter-Referenz.
3. preprocess
Eine optionale PHP-Funktion, die die Args umschreiben kann, bevor sie in den Request wandern.
'preprocess' => function (array $args, array $ctx) {
$args['date'] = (new \DateTime($args['date']))->format('Y-m-d');
return $args;
},
Nützlich für Formatkonvertierungen, Default-Werte oder um zusätzliche Header aus der Session zu ziehen.
4. API-Call
Wenn api gesetzt ist, baut der Runner den Request aus:
apis.php→ Basis-URL, Auth-Header, Timeoutsmethod→GET/POST/PUT/PATCH/DELETEpath→ Pfad, Platzhalter in{curly}werden aus den Args gefülltbody/query→ Mapping der restlichen Args auf Payload oder Query-String
Test-Modus: Mit tm test-tool ... --dry-run überspringt der Runner alle schreibenden Requests (POST, PUT, PATCH, DELETE) und liefert einen Stub zurück. So kannst du Tools gefahrlos im Trockenlauf ausführen.
5. Mapping / Normalize
Das Response-JSON wird gegen mapping durchgeschoben:
'mapping' => [
'id' => 'invoice_id',
'total' => 'amounts.gross',
],
Links steht der Schlüssel, den das Modell sieht. Rechts der Pfad (dot-notation) im API-Response. Felder die im Mapping nicht auftauchen, werden weggelassen — das reduziert Token-Verbrauch und versteckt interne Felder.
6. postprocess
Nach dem Mapping läuft optional eine weitere PHP-Funktion auf dem Ergebnis.
'postprocess' => function (array $result, array $args, $ctx) {
$result['totalWithTax'] = round($result['total'] * 1.19, 2);
return $result;
},
Unterschied zu preprocess: läuft nach dem API-Call und bekommt das Response-Objekt. Unterschied zu handler: darf keine eigenen API-Calls machen (dafür ist handler da, siehe unten).
7. handler (nur bei Handler-only oder hybrid)
Ein Handler ist die PHP-Funktion, die das Tool ausführt, wenn es keine API gibt — oder wenn du zusätzlich zur API weitere Calls (verschachtelte Tool-Aufrufe) brauchst.
'handler' => function (array $results, array $args, array $ctx) {
$invoice = $ctx->runTool('getInvoice', ['id' => $args['id']]);
$customer = $ctx->runTool('getCustomer', ['id' => $invoice['customer_id']]);
return ['invoice' => $invoice, 'customer' => $customer];
},
Über $ctx->runTool() rufst du andere Tools auf. Das ist wie ein Funktionsaufruf — Auth, Mapping und Logging laufen automatisch mit.
Limit: Maximal 10 verschachtelte Aufrufe pro Tool-Kette. Schützt vor Endlosschleifen.
8. Ergebnis zurück ans Modell
Das Endergebnis geht zurück in die Chat-Pipeline und wird dem LLM als tool_result serviert. Im ChatMessages-Log siehst du beides: Args, die reingingen, und das Ergebnis, das rauskam.
Logging und Debugging
Jeder Tool-Aufruf wird mit einem Eintrag in task_log_entries versehen:
tool_name,args,result,duration_ms- Bei API-Calls zusätzlich: HTTP-Status, Request-URL, Response-Bytes
entry_type→chatoderscheduled_task— so filterst du im Log nach Quelle
Zum Anschauen: /manage/tasks → Assistant öffnen → Reiter "Ausführungen".
Häufige Fehlerbilder
| Symptom | Ursache |
|---|---|
| Tool wird nie aufgerufen | Fehlt in der tools-Liste des Chats/Assistants |
args-Validation-Error |
Modell hat ein Pflichtfeld vergessen — Beschreibung schärfen |
| 401 beim API-Call | OAuth abgelaufen — Verbindung unter /manage/o-auth-connections erneuern |
| Leeres Ergebnis obwohl API 200 liefert | Mapping-Pfad passt nicht zum Response, mapping überprüfen |