Architecture patterns for PascalAI multi-agent systems — supervisor, flows, pub/sub, routing, parallel, goals
ppm install agent-patterns
ppm skill install-claude
The second command activates the skill in Claude Code
(copies it into .claude/skills/).
You are a PascalAI agent architecture expert. Guide users toward patterns that are maintainable, observable, and correct.
One supervisor agent coordinates multiple specialized workers via Blackboard.
agent Supervisor;
behaviour oneshot;
begin
BB.Set('task', 'Analyze Q4 sales data');
BB.Set('supervisor_done', 'false');
// Spawn workers
Fetcher_Spawn;
Analyzer_Spawn;
Reporter_Spawn;
// Wait for pipeline to complete
while BB.Get('reporter_done') <> 'true' do
Sleep(200);
BB.Set('supervisor_done', 'true');
end;
end;
agent Fetcher;
behaviour oneshot;
begin
var Data := LLM_Complete('Fetch: ' + BB.Get('task'));
BB.Set('raw_data', Data);
BB.Set('fetcher_done', 'true');
end;
end;
Sequential processing with flow graphs — nodes run in order, with optional parallel branches.
flow DataPipeline;
node Ingest -> Clean -> Enrich -> Store;
// Parallel enrichment
node Clean -> [EnrichA, EnrichB] -> Merge -> Store;
end;
agent Ingest;
on 'run' do
begin
BB.Set('raw', LoadData());
end;
end;
Agents communicate via named channels without direct coupling.
agent Publisher;
behaviour cyclic;
begin
var Event := BB.Get('new_event');
if Event <> '' then
begin
Channel.Publish('events', Event);
BB.Set('new_event', '');
end;
Sleep(100);
end;
end;
agent Subscriber;
on Channel.Listen('events') do
begin
var Msg := BB.Get('channel_events_last');
ProcessEvent(Msg);
end;
end;
Route LLM calls to different providers based on cost, availability, or capability.
agent CostAwareAgent;
providers begin
primary: 'claude-opus-4-6'; // best quality
fallback: 'claude-haiku-4-5'; // cheap fallback
strategy: 'fallback'; // try primary first, fallback on error
budget: 10000; // token budget
end;
behaviour cyclic;
begin
var Used := LLM_BudgetUsed('CostAwareAgent');
var Left := LLM_BudgetLeft('CostAwareAgent');
WriteLn('Used: ' + IntToStr(Used) + ' / Left: ' + IntToStr(Left));
Sleep(5000);
end;
end;
Persist typed records to survive restarts without manual serialization.
type
TAgentState = record
LastProcessed: string;
Count: Integer;
Score: Double;
end;
var State: TAgentState;
// Restore on startup
BB.Restore(State);
// Modify
State.Count := State.Count + 1;
State.LastProcessed := Now;
// Persist atomically
BB.Persist(State);
Fire multiple LLM calls simultaneously and collect results.
var Handles: array[0..2] of Int64;
Handles[0] := LLM_Async('Summarize section 1');
Handles[1] := LLM_Async('Summarize section 2');
Handles[2] := LLM_Async('Summarize section 3');
LLM_AwaitAll;
var Summary :=
LLM_Await(Handles[0]) + #10 +
LLM_Await(Handles[1]) + #10 +
LLM_Await(Handles[2]);
Expose agent capabilities as callable goals from other agents or programs.
agent ShoppingCart;
goal AddItem(Name: string; Price: Double): Int64;
goal GetTotal: Double;
goal Checkout: string;
behaviour oneshot;
begin
BB.Set('item_count', 0);
BB.Set('total', 0.0);
end;
end;
// Caller:
var ItemId := ShoppingCart.AddItem('Widget', 9.99);
var Total := ShoppingCart.GetTotal;
| Scenario | Pattern |
|---|---|
| Sequential ETL pipeline | Flow graph |
| Multiple independent agents | Supervisor + Workers |
| Loose coupling, event-driven | Publish/Subscribe |
| Cost-sensitive LLM calls | Provider routing |
| Stateful agent across restarts | BB.Persist |
| Batch LLM processing | Parallel Async |
| Cross-agent function calls | Goals |