tools/
Struktur und Muster für den Tools-Ordner.
Der Tools-Ordner ist das Herzstück deines Workspace. Hier lebt alles, was das Modell tun kann.
Tool-Schichten
Tools können auf vier Ebenen leben. Höhere Ebenen überschreiben niedrigere bei gleichem Tool-Namen, weil der Loader alphabetisch über alle Pfade merged.
| Schicht | Pfad | Wer nutzt es |
|---|---|---|
| 0 — Built-in (Code) | _tool_catalog.php (PHP-Code, nicht Workspace) |
Eingebaute Tools mit fertigem Handler — getKnowledge, setSuggestions. Wir referenzieren sie via 'extends'. |
| 1 — Plattform-shared | config/tenants/_shared/tools/ |
Tenant-agnostische Tools, die jeder Workspace bekommt. Database-Gateway, Email-Send, generische Google-Drive/Sheets-Tools. |
| 2 — Workspace | <workspace>/tools/ |
Workspace-weit, von mehreren Assistants genutzt. Tenant-spezifische API-Tools (eigene API-Keys, eigene Endpoints). |
| 3 — Assistant-lokal | <workspace>/assistants/<id>/tools/ |
Use-Case-Tools, die nur ein Assistant nutzt. Macht Recipes als Bundle veröffentlichbar (Assistant + seine Tools in einem Ordner). |
Wichtig: Auf Schicht 3 sind Tools technisch trotzdem global verfügbar (tools.<name> ist Workspace-Map, kein echter Scope). Die Datei-Lokalität ist nur Doku-/Bundle-Hilfe. Bei Namenskonflikten gewinnt die alphabetisch späteste Datei — wenn du das Risiko vermeiden willst, nutze einen Prefix wie gutscheine_createCode.
Was wo hingehört (Faustregel):
- Schicht 1 (
_shared/): Tool nutzt OAuth-Connection oder Per-User-Token, keine Tenant-spezifischen Endpoints/IDs hardcoded → Plattform-shared. - Schicht 2 (workspace-tools): Tool nutzt Tenant-eigene API-Keys, Endpoints oder Datenstrukturen → tenant-lokal.
- Schicht 3 (assistant-tools): Tool ist nur für diesen einen Use-Case sinnvoll, nicht von anderen Assistants nutzbar.
Empfohlene Struktur (Workspace-Schicht 2)
Nach API gruppieren, dann nach fachlicher Kategorie:
<workspace>/
├── tools/ # Schicht 2 — workspace-weit
│ ├── shop/
│ │ ├── orders/
│ │ │ ├── getOrder.php
│ │ │ ├── listOrders.php
│ │ │ └── cancelOrder.php
│ │ └── products/
│ │ ├── searchProducts.php
│ │ └── getStock.php
│ └── kb/
│ ├── searchFaq.php
│ └── searchProducts.php
└── assistants/
└── gutscheine/ # Schicht 3 — assistant-lokal
├── gutscheine.php # Assistant-Config
└── tools/
├── createDiscountCode.php
└── deactivateDiscountCode.php
Der Loader scannt rekursiv — Ordner-Namen haben keine Bedeutung für die Plattform, nur für dich.
Recipe-Bundling
Wenn du einen Assistant als Recipe veröffentlichen willst (z.B. „Gutschein-Workflow"): leg ihn unter assistants/<id>/ mit assistants/<id>/tools/ an. Dann ist alles in einem Ordner — Assistant-Config + alle nur dafür gebauten Tools. Beim Recipe-Import kopierst du den ganzen Ordner.
Tools, die der Assistant aus Schicht 1/2 nutzt (z.B. setSuggestions, searchItems), werden im Recipe als „Voraussetzungen" dokumentiert, nicht mit-kopiert.
Ein Tool pro Datei
Empfehlung. Gründe:
- Diffs bleiben klein
- Umbenennen per Git-Move behält History
- Einzelne Tools ohne Merge-Konflikte editierbar
tm monitorzeigt pro Tool — einfacher nachzuverfolgen
Abweichen nur, wenn mehrere Tools eine private Helperfunktion teilen, die sonst dupliziert würde.
Dateinamen-Konvention
Dateiname = Tool-Name, camelCase. Beispiele:
getOrder.php→ ToolgetOrdersendInvoice.php→ ToolsendInvoicelistUserDropboxFiles.php→ ToollistUserDropboxFiles
Kein technischer Zwang (der Tool-Name steht im Array-Key), aber konsistent macht Refactorings einfacher.
Tool-Datei-Grundform
<?php
return [
'tools' => [
'<toolName>' => [
'description' => '...',
'parameters' => [
'type' => 'object',
'properties' => [ /* ... */ ],
'required' => [ /* ... */ ],
],
// eines oder mehreres von:
'api' => '<api_key>',
'handler' => function (array $results, array $args, array $ctx): array { /* ... */ },
// optional:
'preprocess' => function ($args, $ctx) { /* ... */ },
'postprocess' => function ($result, $args, $ctx) { /* ... */ },
'mapping' => [ /* ... */ ],
'statusMessages' => [ /* ... */ ],
'args_fixture' => [ /* ... */ ],
'extends' => '<baseToolName>',
],
],
];
Siehe Tool als Config-Datei für komplette Beispiele.
Mehrere Tools in einer Datei
Wenn Tools eng verwandt sind und Helferfunktionen teilen:
<?php
function calculatePriceWithTax(float $net, float $rate): float {
return round($net * (1 + $rate), 2);
}
return [
'tools' => [
'priceWithTax' => [
'description' => '...',
'parameters' => [ /* ... */ ],
'handler' => function (array $results, array $args, array $ctx) {
return ['gross' => calculatePriceWithTax($args['net'], 0.19)];
},
],
'priceWithoutTax' => [
'description' => '...',
'parameters' => [ /* ... */ ],
'handler' => function (array $results, array $args, array $ctx) {
return ['net' => round($args['gross'] / 1.19, 2)];
},
],
],
];
extends für Shared Tools
Tools, die über Workspace-Grenzen hinweg nützlich sind (getKnowledge, setSuggestions, ...), sind in der Shared-Library definiert. Du überschreibst nur das, was spezifisch für deinen Workspace ist:
return [
'tools' => [
'searchFaq' => [
'extends' => 'getKnowledge',
'description' => 'Kunden-FAQ durchsuchen.',
'options' => [
'knowledge_base' => 'faq',
'limit' => 3,
],
],
],
];
Größen-Budget
Ein Workspace ist nicht dafür gemacht, 500 Tools zu haben. Realistische Größe:
- Pro Assistant: 5–15 Tools in der Allowlist
- Pro Workspace total: 20–80 Tools
- Bei mehr: Zeichen, dass du einige Tools zu generisch gebaut hast — vielleicht kann
listOrdersmit Filter statt 10 spezialisierter Listen-Tools machen
Refactoring
Wenn sich Muster herauskristallisieren (alle list*-Tools machen dasselbe bis auf Path + Mapping), ziehe eine Helferfunktion oder ein Shared-Tool raus. Aber: drei Vorkommen mindestens, bevor du abstrahierst — sonst baust du Abstraktionen gegen die Glaskugel.
Nach dem Anlegen
tm sync
Syntax-Fehler werden abgewiesen und namentlich gemeldet.
tm test-tool <neuerTool>
Direkter Smoke-Test, bevor du's ins Modell gibst.