Skip to main content

Why Multi-Agent Systems?

Complex problems often require multiple specialized agents working together:
  • Code generation: Architect designs, Developer implements, Reviewer validates
  • Research: Searcher finds sources, Analyst synthesizes, Writer drafts
  • Customer support: Classifier routes, Specialist handles, QA verifies
MoFA provides 7 built-in coordination patterns for multi-agent collaboration.

Coordination Architecture

Coordinator Trait

All coordination patterns implement the Coordinator trait:
mofa-kernel/src/agent/components/coordinator.rs
#[async_trait]
pub trait Coordinator: Send + Sync {
    /// Dispatch task to agents
    async fn dispatch(
        &self,
        task: Task,
        ctx: &AgentContext,
    ) -> AgentResult<Vec<DispatchResult>>;
    
    /// Aggregate results from multiple agents
    async fn aggregate(
        &self,
        results: Vec<AgentOutput>,
    ) -> AgentResult<AgentOutput>;
    
    /// Get coordination pattern
    fn pattern(&self) -> CoordinationPattern;
    
    /// Select agents for task
    async fn select_agents(
        &self,
        task: &Task,
        ctx: &AgentContext,
    ) -> AgentResult<Vec<String>>;
}

Coordination Patterns

Sequential Execution: Output of one agent becomes input to the next.Use Cases:
  • Data pipelines (fetch → process → store)
  • Multi-step reasoning (plan → execute → verify)
  • Content generation (research → draft → edit)
Example:
use mofa_kernel::agent::components::coordinator::{
    Task, CoordinationPattern
};

// Create task
let task = Task::new(
    "task-1",
    "Research Rust async programming"
)
.with_type(TaskType::Analysis);

// Sequential pattern: research → summarize → format
let pattern = CoordinationPattern::Sequential;
let coordinator = SequentialCoordinator::new(vec![
    "researcher".to_string(),
    "summarizer".to_string(),
    "formatter".to_string(),
]);

let results = coordinator.dispatch(task, &ctx).await?;
let final_output = coordinator.aggregate(
    results.into_iter().filter_map(|r| r.output).collect()
).await?;

Task Definition

Tasks are the units of work passed to coordinators:
use mofa_kernel::agent::components::coordinator::{
    Task, TaskType, TaskPriority
};

let task = Task::new("task-1", "Analyze security vulnerabilities")
    .with_type(TaskType::Analysis)
    .with_priority(TaskPriority::High)
    .for_agent("security_expert")  // Optional: target specific agent
    .with_param("scope", json!("authentication"))
    .with_timeout(30_000);  // 30 seconds
Task Fields:
FieldTypeDescription
idStringUnique task identifier
task_typeTaskTypeCategory (Analysis, Generation, etc.)
contentStringTask description/prompt
priorityTaskPriorityExecution priority (Low, Normal, High, Urgent)
target_agentOption<String>Specific agent to use (optional)
paramsHashMapAdditional parameters
timeout_msOption<u64>Maximum execution time

Aggregation Strategies

How to combine results from multiple agents:
use mofa_kernel::agent::components::coordinator::AggregationStrategy;

let strategy = AggregationStrategy::Concatenate {
    separator: "\n---\n".to_string(),
};

let aggregated = aggregate_outputs(results, &strategy)?;
Join all outputs with a separator:
AggregationStrategy::Concatenate {
    separator: "\n\n".to_string(),
}
Result: "Output 1\n\nOutput 2\n\nOutput 3"

Communication Patterns

Message Bus

Agents communicate via the microkernel message bus:
use mofa_kernel::bus::MessageBus;

// Send to specific agent
bus.send("agent-2", Message::text("Process this")).await?;

// Broadcast to all
bus.broadcast(Message::event("task_completed")).await?;

// Pub/sub topics
bus.subscribe("llm_responses").await?;
bus.publish("llm_requests", Message::json(request)).await?;

Agent Discovery

Find agents by capabilities:
use mofa_runtime::registry::AgentRegistry;

let registry = AgentRegistry::new();

// Find agents with specific capability
let llm_agents = registry.find_by_tag("llm").await?;

// Find by multiple criteria
let requirements = AgentRequirements::builder()
    .tag("code-generation")
    .supports_streaming(true)
    .build();

let agents = registry.find_matching(&requirements).await?;

Priority Scheduling

Tasks are executed based on priority:
pub enum TaskPriority {
    Low = 0,
    Normal = 1,
    High = 2,
    Urgent = 3,
}
Scheduler Behavior:
  • Higher priority tasks preempt lower priority
  • Same priority uses FIFO queue
  • Urgent tasks interrupt running tasks (if interruptible)
let urgent_task = Task::new("security-alert", "Analyze breach")
    .with_priority(TaskPriority::Urgent);

coordinator.dispatch(urgent_task, &ctx).await?;
// Interrupts current tasks and executes immediately

Secretary Agent Pattern

MoFA includes a specialized Secretary Agent for human-in-the-loop workflows: Features:
  • 🧠 Autonomous task planning and decomposition
  • 🔄 Intelligent agent scheduling
  • 👤 Human intervention at key nodes
  • 📊 Full process observability
  • 🔁 Closed-loop feedback

Secretary Agent Guide

Learn how to build human-in-the-loop workflows

Real-World Example

Scenario: Code review system
use mofa_kernel::agent::components::coordinator::*;

// Sequential: Static analysis → Security scan → Style check
let analysis_task = Task::new(
    "code-review-1",
    "Review authentication.rs"
);

let sequential = SequentialCoordinator::new(vec![
    "static_analyzer",
    "security_scanner",
    "style_checker",
]);

let analysis_results = sequential.dispatch(analysis_task, &ctx).await?;

// Parallel: Multiple reviewers give feedback simultaneously
let review_task = Task::new(
    "code-review-2",
    "Get expert opinions on design"
);

let parallel = ParallelCoordinator::new(
    vec!["senior_dev_1", "senior_dev_2", "architect"],
    AggregationStrategy::CollectAll,
);

let review_results = parallel.dispatch(review_task, &ctx).await?;

// Hierarchical: Lead decides if changes needed
let decision_task = Task::new(
    "code-review-3",
    "Approve or request changes"
);

let hierarchical = HierarchicalCoordinator::new(
    "tech_lead",
    vec!["reviewer_1", "reviewer_2"],
);

let final_decision = hierarchical.dispatch(decision_task, &ctx).await?;

Performance Considerations

Coordination Overhead: Each coordination pattern adds latency:
  • Sequential: Sum of all agent latencies
  • Parallel: Max of all agent latencies + aggregation
  • Debate: Multiple rounds × agent latency
Design workflows to minimize unnecessary coordination.

Next Steps

Workflow Engine

Build complex stateful workflows

Examples

See coordination patterns in action

Build docs developers (and LLMs) love