TaskMonkey Handbuch

Tool-State

Per-Chat-Scratch-Pad für Tools. Codes sammeln, Wizard-Schritte, Multi-Turn-Auswahl ohne History-Extraktion.

Manche Workflows brauchen State, der über mehrere Chat-Turns lebt: Codes scannen und am Ende abschicken, einen Wizard durchlaufen, Auswahl-Listen aufbauen. Das LLM aus der History rekonstruieren zu lassen ist fragil — Eingaben sehen aus wie Smalltalk (z.B. GS1-Präfixe ]C1...), Eingabe-Formate wechseln, das Modell kapituliert irgendwann mit „keine Daten erfasst".

Dafür gibt es App\Tooling\ToolState. Ein Tool-Aufruf schreibt, der nächste liest. Der State liegt im Tenant-Ordner (config/tenants/{tenant}/tmp/state/), wird also über die tm-CLI zum Entwickler gesynct und ist beim Debuggen direkt einsehbar.

Wann verwenden

  • Sammeln über Turns — Tracking-Codes, SKUs, Anhänge, Optionen.
  • Wizard-Schritte — „Schritt 2 von 4 abgeschlossen, gehe zu 3".
  • Voriges Ergebnis halten — Tool A gibt etwas zurück, Tool B braucht es später.
  • Confirm/Undo-Flows — „Letzter Eintrag war X, soll ich den entfernen?"

Wann NICHT

  • Domänen-Daten, die in die DB gehören — Bestellungen, User-Profile, Audit-Logs.
  • Settings, die mehrere Chats teilen — gehört in tenantConfig.
  • Etwas, das das Modell sich auch merken kann — wenn die letzten 2 Turns reichen, mach keinen State.

API

use App\Tooling\ToolState;

$state = ToolState::ns($ctx, 'mein_namespace');

$state->get('key', $default = null);
$state->set('key', $value, $ttl = '+24 hours');
$state->delete('key');
$state->all();        // alle Keys als array
$state->clear();      // ganzen Namespace im aktuellen Chat loeschen

$ctx ist der Standard-Tool-Context (kommt mit tmp_path, chat_id, tenant). Der Namespace muss [a-zA-Z0-9_-]+ matchen.

Lifecycle

  • TTL pro Set — jedes set() schreibt ein neues expires_at ins File. Aktive Chats verlängern sich also automatisch.
  • Auto-Expiry beim Read — abgelaufene Files werden beim nächsten get() gelöscht.
  • Lazy GC — beim ersten ToolState-Aufruf eines Tages räumt der Helper alle abgelaufenen State-Files im Tenant-tmp/state/ weg (Marker .last_gc).

Default-TTL ist 24h — lang genug für Pausen, kurz genug damit Schrott von gestern verschwindet.

Beispiel: Carrier-Wechsel

Drei Tools teilen sich einen Namespace relabeling:

// addTrackingCode
$state = ToolState::ns($ctx, 'relabeling');
$codes = $state->get('codes', []);
$codes[] = $cleanCode;
$state->set('codes', $codes);

// setRelabelDirection
$state = ToolState::ns($ctx, 'relabeling');
$state->set('direction', 'DHL->DPD');

// submitRelabeling
$state = ToolState::ns($ctx, 'relabeling');
$direction = $state->get('direction');
$codes = $state->get('codes', []);
// ...CSV bauen...
$state->clear();   // sauberer Start fuer den naechsten Run

Beim Debuggen liegt das State-File unter config/tenants/{tenant}/tmp/state/relabeling_{chat_id}.json und sieht so aus:

{
  "expires_at": "2026-05-05T16:00:00+02:00",
  "data": {
    "direction": "DHL->DPD",
    "codes": ["00340434757211819883", "8806096235386"]
  }
}

Anti-Patterns

  • Keine generischen setState/getState-Tools an das LLM geben. Das Modell nutzt sie undiszipliniert — Schema und Lifecycle entgleiten dir. Stattdessen Domain-Tools (addTrackingCode, selectOption) bauen, die intern ToolState benutzen.
  • Keine grossen Daten reinpacken. Es ist ein Scratch-Pad, kein Ersatz-DB. Mehr als ein paar KB → was anderes nutzen.
  • Keine Secrets. Tenant-Config wird gesynct; State auch. Nichts reinschreiben, was nicht in den Logs landen darf.
Zuletzt aktualisiert: 2026-05-04