Multi-agent coordination
Patterns for coordinating multiple agents - event chains, shared state, fan-out, and sequencing.
Last updated: 2026-04-30
Multi-agent coordination
Most fund operations workflows involve more than one agent. A position reconciliation agent feeds its results to a reporting agent, which in turn triggers a distribution agent. Designing these inter-agent relationships correctly determines whether your system is robust or brittle.
Event-driven chaining
The most reliable coordination pattern: Agent A produces an output and emits an event. Agent B listens for that event and triggers.
# Agent A - position reconciliation
name: daily-position-recon
tasks:
- skill: core.reconcile.compare_positions
capture: recon_result
- skill: core.events.emit
args:
event: "recon.completed"
payload:
fund_id: "{{ event.fund_id }}"
as_of: "{{ event.as_of_date }}"
has_breaks: "{{ recon_result.discrepancies | length > 0 }}"
exception_count: "{{ recon_result.discrepancies | length }}"
# Agent B - exception report generator
name: recon-exception-report
trigger:
type: event
source: flexor
event: "recon.completed"
filter:
has_breaks: true
tasks:
- skill: get-exception-records
args:
fund_id: "{{ event.payload.fund_id }}"
as_of: "{{ event.payload.as_of_date }}"
capture: exceptions
- skill: format-exception-report
args:
exceptions: "{{ exceptions }}"
capture: report
- skill: distribute-report
args:
report: "{{ report }}"
recipients: "{{ vault.notifications.recon_recipients }}"
This pattern is preferred over direct agent-to-agent calls because:
- Agent A does not need to know Agent B exists
- New agents can listen to the same event without changing Agent A
- If Agent B fails, Agent A is unaffected and the event persists for replay
Fan-out (parallel agent execution)
When one trigger should fire multiple independent agents, use a coordinator agent that emits per-fund events:
# Coordinator agent - fires once, fans out to per-fund agents
name: eod-reconciliation-coordinator
trigger:
type: schedule
cron: "0 18 * * 1-5" # 18:00 ET weekdays
tasks:
- skill: plexifact.funds.get_active_list
capture: active_funds
- loop: "{{ active_funds }}"
as: fund
do:
- skill: core.events.emit
args:
event: "recon.trigger"
payload:
fund_id: "{{ fund.id }}"
as_of: "{{ today() }}"
Each recon.trigger event fires the per-fund reconciliation agent independently. Per-fund runs execute in parallel and are isolated from each other. One fund’s failure does not block others.
Sequencing with dependencies
When Agent B must complete before Agent C starts, and both must complete before Agent D:
# Agent D - final report after all upstream work completes
name: daily-ops-summary
trigger:
type: event
source: flexor
event: "daily.ops.all_checks_complete"
Agent B and Agent C each emit completion events. A coordinator pattern tracks which agents have completed and emits daily.ops.all_checks_complete only when all required upstreams are done.
Specification - implementation in progress. The built-in
core.coordination.wait_forskill that handles completion tracking is on the roadmap. Currently, sequencing with dependencies requires a coordinator agent that checks completion status in the vault.
Shared state via the vault
Agents can read from and write to vault documents to share state. Use this pattern when:
- An agent needs to know what another agent found in an earlier run
- State needs to persist between trigger cycles
- Multiple agents contribute to a shared record
# Agent A writes state
- skill: core.vault.write_document
args:
path: "vault://state/recon-status/{{ event.fund_id }}.json"
content:
last_recon_at: "{{ now() }}"
break_count: "{{ recon_result.discrepancies | length }}"
status: "{{ recon_result.discrepancies | length > 0 ? 'breaks' : 'clean' }}"
# Agent B reads state
- skill: core.vault.read_document
args:
path: "vault://state/recon-status/{{ event.fund_id }}.json"
capture: recon_status
Shared vault state is persistent and audited. Do not use it for temporary data that does not need a record - use capture variables for in-run state.
Coordination anti-patterns
| Anti-pattern | Problem | Fix |
|---|---|---|
| Agent A calls Agent B directly via webhook | Tight coupling; if B’s URL changes, A breaks | Use event-driven chaining |
| All agents triggered by the same schedule | Race conditions; agents compete for shared resources | Use a coordinator that fans out |
| State stored in local variables across agent runs | State lost on failure; no audit trail | Write state to vault documents |
| Agent B polls for Agent A’s completion | Fragile; polling interval creates latency and load | Use completion events |
Was this page helpful?
Previous
Building a custom agent
Next
Deployment