Agenten-Orchestrierung
Queuety eignet sich für Agentensysteme, wenn du adaptive Arbeit mit dauerhaftem Zustand, Wiederholungsversuchen, Prüfung und expliziter Semantik für Wartezeiten kombinieren möchtest.
Für die meisten Agentenabläufe brauchst du keine DAG-Engine, deren Abläufe sich zur Laufzeit bearbeiten lassen. In der Praxis lässt sich das meiste mit wenigen dauerhaften Orchestrierungsschritten modellieren. for_each() erweitert die Arbeit innerhalb eines Workflow-Laufs. start_agents() und wait_for_agents() übergeben zur Laufzeit entdeckte Aufgaben an unabhängige Workflows der obersten Ebene. wait_for_decision() und wait_for_input() pausieren den Ablauf für menschliche Eingaben. Mit wait_for_workflow(), wait_for_workflows() und wait_for_agent_group() kann ein Lauf der obersten Ebene auf einen anderen Lauf oder auf eine gestartete Anzahl von Aufgaben warten, die einen festgelegten Schwellenwert erreicht.
Ein hilfreiches Denkmodell
Die Orchestrierung von Agenten lässt sich am einfachsten in Ebenen verstehen. Ein Planer bestimmt, welche Aufgaben anstehen. Queuety macht daraus dauerhafte Jobs oder Workflows. Spezialisierte Läufe liefern Ergebnisse und speichern Artefakte. Eine überwachende Phase fasst diese Ergebnisse zusammen und entscheidet über die nächsten Schritte. Ein Mensch kann den Lauf jederzeit genehmigen, ablehnen oder korrigieren, bevor er fortgesetzt wird.
Entscheidend ist die Unterscheidung: Der Agent bestimmt, welche Aufgabe ansteht. Queuety steuert weiterhin, wie diese Arbeit gespeichert, wiederholt, fortgesetzt und zusammengeführt wird. Wenn die Orchestrierung eine langlebige Sitzung statt eines begrenzten Laufs betrifft, kombiniere dies mit Zustandsautomaten.
Muster 1: Planung und Ausführung mit unabhängigen Agentenläufen
Nutze start_agents(), wenn ein Planungsschritt zur Laufzeit Aufgaben entdeckt und jede davon zu einem eigenen Workflow der obersten Ebene werden soll.
use Queuety\Enums\WaitMode;
use Queuety\Queuety;
$agent_run = Queuety::workflow( 'agent_run' )
->then( FetchSourcesStep::class )
->then( DraftFindingStep::class );
Queuety::workflow( 'brief_research' )
->version( 'brief-research.v1' )
->idempotency_key( 'brief:42:research' )
->max_transitions( 20 )
->then( PlanResearchTasksStep::class )
->start_agents( 'agent_tasks', $agent_run, group_key: 'researchers' )
->wait_for_agent_group( 'researchers', WaitMode::Quorum, 2, 'agent_results' )
->then( SynthesizeBriefStep::class )
->dispatch( [ 'brief_id' => 42 ] );
Bei diesem Muster schreibt PlanResearchTasksStep die zur Laufzeit entdeckten Elemente in $state['agent_tasks']. start_agents() startet für jede Aufgabe einen dauerhaften Workflow, und wait_for_agent_group() hält den übergeordneten Workflow an, bis die gewählte Abschlussbedingung erfüllt ist. SynthesizeBriefStep erhält anschließend die abgeschlossenen untergeordneten Zustände unter $state['agent_results'].
Das ist der beste Ausgangspunkt für Systeme mit Planung und Ausführung, da sich jeder Agentenlauf einzeln prüfen und wiederholen lässt.
Muster 2: Menschliche Prüfung, bevor der Ablauf fortgesetzt wird
Nutze wait_for_decision(), wenn der Workflow für eine echte Ja-Nein-Entscheidung angehalten werden soll. Nutze wait_for_input(), wenn die verantwortliche Person strukturiertes Feedback in den Ablauf zurücksenden muss.
use Queuety\Queuety;
$workflow_id = Queuety::workflow( 'brief_review' )
->then( DraftBriefStep::class )
->wait_for_decision( result_key: 'review' )
->then( HandleReviewDecisionStep::class )
->wait_for_input( result_key: 'revision_notes' )
->then( ApplyRevisionNotesStep::class )
->dispatch( [ 'brief_id' => 42 ] );
Wenn die prüfende Person genehmigt:
Queuety::approve_workflow(
$workflow_id,
[
'reviewer' => 'editor@example.com',
'notes' => 'Looks good, publish after legal check',
],
'approved'
);
Wenn die prüfende Person ablehnt:
Queuety::reject_workflow(
$workflow_id,
[
'reviewer' => 'editor@example.com',
'reason' => 'Needs citations and a stronger conclusion',
],
'rejected'
);
Nach wait_for_decision() erhält der Workflow eine strukturierte Nutzlast mit dem Endergebnis, dem Signal, das den Lauf fortgesetzt hat, und allen mitgelieferten Prüferdaten. So erhältst du einen echten Human-in-the-Loop-Prüfpunkt, ohne einen PHP-Prozess offen zu halten, während der Lauf wartet.
Muster 3: Ein übergeordneter Workflow wartet auf einen anderen
Manchmal ist ein einzelner riesiger Workflow nicht die sauberste Lösung. Ein Workflow kann die Recherche abschließen, während ein anderer darauf wartet, bevor er die nächste Phase startet.
use Queuety\Enums\WaitMode;
use Queuety\Queuety;
$research_id = Queuety::workflow( 'research_brief' )
->then( PlanResearchTasksStep::class )
->start_agents( 'agent_tasks', $agent_run )
->wait_for_agents( mode: WaitMode::All, result_key: 'agent_results' )
->then( SynthesizeBriefStep::class )
->dispatch( [ 'brief_id' => 42 ] );
Queuety::workflow( 'publish_brief' )
->wait_for_workflow( $research_id, 'research' )
->wait_for_decision( result_key: 'editor_review' )
->then( PublishBriefStep::class )
->dispatch( [ 'brief_id' => 42 ] );
So entsteht eine dauerhafte Grenze zwischen Recherche- und Veröffentlichungsphase. Jeder Lauf hat seinen eigenen operativen Lebenszyklus, aber der zweite Workflow wird automatisch fortgesetzt, sobald die Abhängigkeit abgeschlossen ist.
Das passende Grundelement auswählen
for_each() ist das richtige Werkzeug, wenn die Arbeit zu einem Workflow-Lauf gehört und die Zweige lediglich parallele Ausführung auf Schritt-Ebene mit einem gemeinsamen Zustandsobjekt sind.
start_agents() eignet sich besser, wenn jede gefundene Aufgabe einen eigenen dauerhaften Workflow-Lauf auf oberster Ebene erhalten und separat geprüft, wiederholt oder exportiert werden soll.
wait_for_workflow() und wait_for_workflows() eignen sich, wenn ein Workflow auf oberster Ebene von einem anderen abhängt und du eine dauerhafte asynchrone Koordination zwischen diesen Orchestrierungsebenen möchtest.
wait_for_decision() und wait_for_input() eignen sich für Stellen, an denen ein Mensch den Lauf genehmigen, ablehnen oder lenken muss, während der Workflow pausiert, ohne Worker-Kapazität zu belegen.
Empfohlene Konventionen für Zustände
Agentensysteme lassen sich leichter debuggen, wenn die Zustandsschlüssel vorhersehbar sind. In der Praxis eignet sich agent_tasks gut für die Planerausgabe, agent_workflow_ids für gestartete Workflows auf oberster Ebene, agent_results für den abgeschlossenen Zustand von untergeordneten Workflows und review oder decision für Genehmigungs- oder Ablehnungsdaten. Größere Ausgaben wie Entwürfe, Quellenangaben oder Recherchemanifeste gehören in der Regel in den Artefaktspeicher statt in das Hauptzustandsobjekt des Workflows.
Empfohlene Schutzmaßnahmen für Agentenläufe
Agenten-Workflows profitieren mehr als fast jeder andere Anwendungsfall von Schutzmaßnahmen, da sie naturgemäß dynamisch sind.
Queuety::workflow( 'agent_research' )
->version( 'agent-research.v2' )
->idempotency_key( 'brief:42:agent-research' )
->max_transitions( 20 )
->max_for_each_items( 12 )
->max_state_bytes( 32768 );
Diese Kontrollen beantworten die praktischen Fragen, die im Produktivbetrieb zählen: Welche Definitionsversion hat einen Lauf erzeugt? Können doppelte externe Anfragen denselben Lauf zweimal starten? Kann ein Planer zu viele Zweige erzeugen? Kann der Workflow-Zustand unbegrenzt wachsen? Bei den meisten Agentensystemen solltest du zuerst diese Schutzmaßnahmen einrichten und dich danach mit der Anzahl der Worker oder der Warteschlangentopologie befassen.