Skip to content

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_for skill 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-patternProblemFix
Agent A calls Agent B directly via webhookTight coupling; if B’s URL changes, A breaksUse event-driven chaining
All agents triggered by the same scheduleRace conditions; agents compete for shared resourcesUse a coordinator that fans out
State stored in local variables across agent runsState lost on failure; no audit trailWrite state to vault documents
Agent B polls for Agent A’s completionFragile; polling interval creates latency and loadUse completion events

Was this page helpful?

Edit on GitHub