Agentic AIPHPverifiedVerified
Multi-Agent Orchestration Pattern in PHP
Coordinate a network of specialised AI agents under an orchestrator, where each agent owns a distinct capability or domain and agents communicate through structured messages.
How to Implement the Multi-Agent Orchestration Pattern in PHP
1Step 1: Define the agent interface and message types
class Message
{
public function __construct(
public readonly string $from,
public readonly string $to,
public readonly string $content,
) {}
}
interface Agent
{
public function getName(): string;
public function handle(Message $message): ?Message;
}2Step 2: Implement a simple multi-agent orchestrator
class Orchestrator
{
/** @var array<string, Agent> */
private array $agents = [];
public function register(Agent $agent): void
{
$this->agents[$agent->getName()] = $agent;
}
public function dispatch(Message $message): ?Message
{
$target = $this->agents[$message->to] ?? null;
if ($target === null) {
throw new \RuntimeException("Agent not found: {$message->to}");
}
return $target->handle($message);
}3Step 3: Run a conversation loop between agents
public function runConversation(Message $initial, int $maxTurns = 10): array
{
$history = [];
$current = $initial;
for ($i = 0; $i < $maxTurns; $i++) {
$response = $this->dispatch($current);
$history[] = $current;
if ($response === null) break;
$current = $response;
}
return $history;
}
}<?php
declare(strict_types=1);
// [step] Define enums, message types, and agent interfaces
enum MessageType: string
{
case Task = 'task';
case Result = 'result';
case Error = 'error';
case Delegate = 'delegate';
}
final readonly class AgentMessage
{
public function __construct(
public string $id,
public string $from,
public string $to,
public MessageType $type,
public string $content,
/** @var array<string, mixed> */
public array $metadata = [],
public float $timestamp = 0,
) {
// Trick: readonly allows setting in constructor
}
}
interface AgentInterface
{
public function getName(): string;
public function getCapabilities(): array;
public function handle(AgentMessage $message): AgentMessage;
}
interface LoggerInterface
{
public function info(string $message, array $context = []): void;
public function error(string $message, array $context = []): void;
}
// [step] Implement the multi-agent orchestrator with routing and error handling
final class MultiAgentOrchestrator
{
/** @var array<string, AgentInterface> */
private array $agents = [];
/** @var AgentMessage[] */
private array $messageLog = [];
public function __construct(
private readonly LoggerInterface $logger,
private readonly int $maxTurns = 20,
) {}
public function register(AgentInterface $agent): self
{
$this->agents[$agent->getName()] = $agent;
$this->logger->info("Registered agent: {$agent->getName()}", [
'capabilities' => $agent->getCapabilities(),
]);
return $this;
}
public function findAgent(string $capability): ?AgentInterface
{
foreach ($this->agents as $agent) {
if (in_array($capability, $agent->getCapabilities(), true)) {
return $agent;
}
}
return null;
}
/** @return AgentMessage[] */
public function run(AgentMessage $initial): array
{
$this->messageLog = [];
$current = $initial;
for ($turn = 0; $turn < $this->maxTurns; $turn++) {
$this->messageLog[] = $current;
$target = $this->agents[$current->to] ?? null;
if ($target === null) {
$this->logger->error("Agent not found: {$current->to}");
break;
}
try {
$response = $target->handle($current);
$this->messageLog[] = $response;
// If the response is a delegation, re-route
if ($response->type === MessageType::Delegate) {
$delegateTarget = $response->to;
if (!isset($this->agents[$delegateTarget])) {
$this->logger->error("Delegation target not found: {$delegateTarget}");
break;
}
$current = $response;
continue;
}
// If it's a final result, we're done
if ($response->type === MessageType::Result) {
break;
}
$current = $response;
} catch (\Throwable $e) {
$this->logger->error("Agent error", [
'agent' => $current->to,
'error' => $e->getMessage(),
]);
$this->messageLog[] = new AgentMessage(
id: uniqid('err_', true),
from: 'orchestrator',
to: $current->from,
type: MessageType::Error,
content: $e->getMessage(),
timestamp: microtime(true),
);
break;
}
}
return $this->messageLog;
}
}Multi-Agent Orchestration Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Multi-Agent Orchestration Pattern in the Real World
“A film director (Orchestrator) does not personally operate the camera, compose the score, or design costumes. Instead they delegate to specialist department heads — cinematographer, composer, costume designer — each expert in their domain. The director collects their work, gives feedback, and integrates it into a coherent film.”