Agent-Delegation (invoke_assistant / spawn_agent)
Master-Assistants delegieren an bestehende Assistants oder bauen Ad-hoc-Agenten on the fly. Sub-Chats via parent_chat_id verlinkt.
Ein Master-Assistant kann Teilaufgaben an andere Assistants delegieren oder Ad-hoc-Agenten mit selbst gewähltem Prompt + Tools bauen. Die Sub-Chats laufen unter eigener chat_id, sind via parent_chat_id + parent_message_id auf den Master verlinkt, und liefern ihre finale Reply als Tool-Result zurück.
Dahinter stehen zwei globale Tools, die in _shared/tools/ registriert sind. Nur bewusst freischalten — nicht in Public-Chat-Assistants aufnehmen.
Zwei Muster
| Tool | Wann |
|---|---|
invoke_assistant |
Es gibt bereits einen konfigurierten Assistant, der für die Teilaufgabe passt. Slug reicht — Prompt + Tools kommen aus dessen Config. |
spawn_agent |
Kein bestehender Assistant passt. Master gibt Prompt + Tool-Liste zur Laufzeit mit. Optional based_on: <slug> als Ausgangspunkt. |
Beide sind wegwerf-orientiert aus Master-Sicht: der Master sieht nur die finale Reply des Sub-Chats, kein Zwischen-Tool-Reasoning.
Config
Master-Assistant (z. B. agent_proto) bekommt die beiden Tools freigeschaltet:
'tools' => [
'invoke_assistant',
'spawn_agent',
// ... andere Tools des Masters
],
An allen anderen Assistants ändert sich nichts. Sie sind ohne weiteres Zutun via invoke_assistant("slug", ...) aus dem Master aufrufbar — der Slug ist ihr Config-Key.
Public-Chat-Assistants (public.tools) dürfen die zwei Tools nicht haben. Es gibt keinen zentralen Schutz — die Regel ist eine Convention: nicht in public.tools aufnehmen.
Aufrufe
invoke_assistant:
{
"assistant": "retoure",
"task": "Kunde will Bestellung 12345 zurücksenden, was ist der Status?",
"context": "Kunde hat vor 3 Wochen bestellt, ohne Retour-Label."
}
spawn_agent:
{
"task": "Fasse die letzten 5 Reklamationen in 3 Sätzen zusammen.",
"prompt": "Du bist ein knapper Report-Bot. Antworte in Bullet-Points.",
"tools": ["getSalesOrdersSince", "runPython"],
"based_on": "kpi",
"context": "Zeitraum: letzte 7 Tage."
}
context ist optional und wird an den System-Prompt des Sub-Chats gehängt. Der Master entscheidet aktiv, welche Fakten aus seinem Verlauf der Sub sehen soll — der Sub-Chat sieht sonst nichts vom Master-Verlauf.
Sub-Chat-Anatomie
- Eigene
chat_idim Formatsub_<parent-chat-id>_<8-hex>— sortierbar, Parent auf einen Blick erkennbar - Eigener
chat_threads-Row mit:parent_chat_id(Master-chat_id)parent_message_id(chat_messages.iddes auslösenden Tool-Calls, UUID;NULLwenn kein Parent-Message-Verweis)
- Eigene
chat_messages-Rows (system, user, assistant, ggf. tool)
Der Sub-Chat ist eigenständig wiederherstellbar — bei Bedarf kann der User ihn als eigenen Chat fortführen.
Recursion-Guard
Maximaler Delegations-Baum: Tiefe 2. Master → Sub → Sub-Sub darf noch, Master → Sub → Sub-Sub → weiter nicht. SubChatRunner::currentDepth folgt den parent_chat_id-Hops und bricht ab.
Provider- und Tool-Auflösung
AiProviderFactory::forChatwird für den Sub-Chat mit dessenassistantConfiggerufen — d. h. ein Sub-Assistant mit eigenemllm-Profil bekommt sein eigenes LLM/Modell, sonst fällt es auf den Tenant-Default zurück.- Ein eigener
ToolRunnerwird gestartet, mit einersetAllowedTools()-Whitelist aus dertools-Liste des Sub-Assistants (bzw. demtools-Argument beispawn_agent). - Der Master kann bei
spawn_agentnur Tools freischalten, die er selbst kennt — die Whitelist basiert auf dem Argument, nicht auf einer eigenen Master-Freigabe. Wer die Tools missbraucht, ist der Master (der eh Vollzugriff hat).
Sichtbarkeit für den Master
Der Master sieht als Tool-Result:
{
"success": true,
"sub_chat_id": "sub_abc123_4a1f",
"reply": "…finale Reply des Sub-Assistants…",
"tools_used": ["getPricingBySku"],
"iterations": 1,
"error": null
}
Das reicht, damit er entscheidet, wie er die Antwort in seine Response weiterverwebt. Der Sub-Chat ist per sub_chat_id referenzierbar (Preview-Card im Frontend, folgt separat).
Tests
# invoke_assistant CLI-Test
bin/cake test_tool --tenant bloomify --tool invoke_assistant \
--args='{"assistant":"preise","task":"Was kostet SKU B12345?"}'
# spawn_agent CLI-Test
bin/cake test_tool --tenant bloomify --tool spawn_agent \
--args='{"task":"Sag Hallo","prompt":"Antworte mit einem Satz.","tools":[]}'
chat_threads-Row per SQL prüfen:
SELECT chat_id, parent_chat_id, parent_message_id, status
FROM chat_threads
WHERE tenant='bloomify' AND parent_chat_id IS NOT NULL
ORDER BY id DESC LIMIT 5;
Verwandte Doks
- Slash-Commands — dieselbe Delegation, aber vom Nutzer getriggert (
/gutscheine 10€) - Agent-Mode — Plan-Exec-Reflect-Loop, Reference-Pattern, Cost-Cap
- Nested Tool-Calls — Tool ruft Tool (via
$ctx['tool']) - Assistants konfigurieren — Assistant-Configs,
tools-Whitelist