Tools, die Dateien erzeugen
PDFs, Bilder, CSVs generieren — Konventionen und Fallstricke.
Wenn ein Tool eine Datei erzeugt (PDF-Rechnung, Logo-Mockup, Report-CSV), willst du sie dem Benutzer zurückgeben, im Chat verlinken oder in einem Cloud-Speicher ablegen. Hier sind die Muster, die sich bewährt haben.
Muster 1: Datei im Chat zum Download verlinken
Nutze das interne Helfer-Tool provideDownload (_shared/tools/files/). Es schreibt den Inhalt nach config/tenants/{tenant}/files/downloads/, hängt einen Zeitstempel-Suffix an den Filename (gegen Kollisionen) und gibt eine Manage-URL zurück, die du als Markdown-Link weitergeben kannst.
'handler' => function (array $results, array $args, array $ctx): array {
$csv = "tracking_code,from,to\n12345,DHL,DPD\n";
$download = $ctx['tool']('provideDownload', [
'filename' => 'carrier_wechsel.csv', // bekommt -YYYYMMDD-HHMMSS-hex6 angehaengt
'content' => $csv,
'mime' => 'text/csv', // optional — wird sonst aus der Endung geraten
]);
if (!($download['success'] ?? false)) {
return ['success' => false, 'error' => $download['error']];
}
return [
'success' => true,
'download_url' => $download['download_url'],
'filename' => $download['filename'],
'size_bytes' => $download['size_bytes'],
'message' => "CSV bereit: [Download]({$download['download_url']})",
];
},
URL-Format: /manage/tenant-files/serve/{tenant}/files/downloads/{filename} — ausgeliefert vom bestehenden Manage/TenantFilesController::serve() (Path-Traversal-sicher per realpath-Check). Auth: Manage-Cookie (admin/manager). End-User im Public-Widget, die nicht eingeloggt sind, sehen den Login-Screen.
Logging: jeder provideDownload-Aufruf landet als verschachtelter Eintrag in task_executions (tool_name=provideDownload, mit args.filename und Result-Size). Audit-Trail welche Datei wann von welchem Chat bereitgestellt wurde — ohne extra Tabelle.
Sanitize: Filename wird auf [A-Za-z0-9._-] reduziert + per basename() von Pfad-Anteilen befreit. Stem auf 80 Zeichen begrenzt. Content max. 50 MB.
Nicht direkt vom LLM aufrufen lassen: provideDownload ist internal: true und nicht in assistants.<slug>.tools oder public.tools gelistet. Domain-Tools rufen es per $ctx['tool']('provideDownload', …) auf — das LLM sieht nur das aufrufende Tool und die fertige URL im Result.
Nicht für Public-Chats: wenn dein Tool aus einem Tenant-Public-Widget heraus läuft und der Endnutzer kein Manage-Login hat, ist der Link nicht klickbar. Dann brauchst du Muster 2 (Cloud-Storage) oder ein eigenes signiertes URL-Schema.
Muster 2: Datei in OAuth-Storage legen
Für persistente Ablage (Google Drive, Dropbox) nutzt du ein schon existierendes Upload-Tool:
'handler' => function (array $results, array $args, array $ctx): array {
$csv = buildCsv($args);
$tmp = tempnam(sys_get_temp_dir(), 'report_') . '.csv';
file_put_contents($tmp, $csv);
$upload = $ctx->runTool('uploadToDropbox', [
'path' => '/Reports/' . date('Y-m') . '/report.csv',
'localPath' => $tmp,
]);
unlink($tmp);
return $upload; // enthält Dropbox-URL
},
Muster 3: Binary direkt ans Modell zurückgeben
Tu das nicht. Bilder oder PDFs direkt als Base64 im Tool-Result zurückzugeben explodiert dir den Token-Verbrauch und die Latenz. Gib immer eine URL oder einen Dateipfad zurück, nicht den Rohinhalt.
PDF-Generierung
Für PDFs gibt es mehrere Wege:
- HTML → PDF via Headless Chrome / wkhtmltopdf (gut für layoutlastige Templates)
- Direkter PDF-Build mit einer Library wie TCPDF (feingranulare Kontrolle)
- Externer Dienst (APITemplate.io, DocRaptor) — kein eigenes Binary nötig, dafür API-Kosten
Workspace-Betreiber hat typisch eine Präferenz. Frage, welcher Weg unterstützt ist.
Bild-Generierung
| Zweck | Werkzeug |
|---|---|
| AI-Images (Logos, Mockups) | OpenAI Images, Stable Diffusion via eigenem Service |
| Charts / Diagramme | QuickChart.io, Chart.js headless |
| Dynamische Thumbnails | imagecreate* (GD) oder Intervention Image |
Speichere das Ergebnis lokal oder in Dropbox/Drive — nicht direkt zurück ans Modell.
Naming-Konventionen
Dateien sollten so heißen, wie der Benutzer sie erwartet:
- Zeitstempel im Namen, wenn das Tool mehrmals pro Tag läuft (
report_2026-04-19.csv) - Nutzername / Workspace-Code bei geteilten Ordnern
- Nie
output.pdfoderfile.csv— zu generisch, findet keiner wieder
Aufräumen
Tools, die viele temporäre Dateien erzeugen, sollten nach dem Upload aufräumen:
register_shutdown_function(fn() => @unlink($tmp));
Oder ein Scheduled Task, der täglich alte Dateien in uploads/tmp/ löscht.
Sicherheitshinweis
Dateien in webroot/uploads/ sind öffentlich erreichbar. Für vertrauliche Dokumente lege sie in einen nicht-öffentlichen Pfad und liefere sie über eine Download-Action aus, die Auth prüft.