Skip to content

axon-llm::swarm Multi-Agent Orchestration

Applies to: axon-llm v0.6.0+ Status: Implemented (0.6.0 Workflow B + 0.11.0 Voting Consensus) Plan: docs/superpowers/plans/2026-07-18-axon-quant-0.6.0.md §3, docs/superpowers/specs/2026-08-01-axon-quant-0.11.0-multi-agent-design.md

SwarmOrchestrator chains 4 agents (Market / Risk / Execution / Audit) into a runnable pipeline, integrated with HarnessBridge for final adjudication, and TradingBackend for real order submission to MockTradingBackend or live exchanges. Python side can construct agents, start the loop, inject signals, and read stats directly.

1. 4-Agent Architecture

                 ┌────────────────────────────┐
                 │     SwarmOrchestrator       │
                 │  ┌──────────────────────┐   │
                 │  │   run_loop_arc       │   │◀──── inject_market_signal
                 │  │   dispatch()         │   │      inject_vote_response
                 │  └──────────────────────┘   │
                 │  ConsensusManager + Stats   │
                 └─────┬──────┬──────┬─────────┘
                       │      │      │      (mpsc inbox)
        ┌──────────────┘      │      └────────────┐
        │                     │                    │
   ┌────▼────┐         ┌─────▼─────┐         ┌────▼────┐
   │ Market  │  ───▶   │   Risk    │  ───▶   │Execution│  ──▶  PlaceOrderTool
   │ Agent   │ Market  │  Agent    │  ───▶   │ Agent   │      QueryPortfolioTool
   │         │ Signal  │           │  Risk   │         │      TradingBackend
   └─────────┘         └───────────┘  Assess└─────────┘
        │                                          │
        │                                          ▼
        │                                    ┌──────────┐
        │                                    │  Audit   │
        │                                    │  Agent   │
        │                                    └──────────┘
   MarketDataSource (Mock / WS / CSV)

Key design points: - Each agent holds Box<dyn DeclarativeAgentRunner>, reusing DeclarativeAgent abstraction - Inter-agent communication via tokio::mpsc (no shared state) - Orchestrator main loop listens to inbox, routes by MessageContent - HarnessBridge held by SwarmOrchestrator + shared with each agent as Arc

2. Message Routing Table (inside run_loop)

Incoming Message Action
MarketAnalysis(signal) Create TradeDecision vote, broadcast VoteRequest to Risk + Execution
RiskAssessment{approved=true} Forward to Execution agent for ExecutionRequest
RiskAssessment{approved=false} Broadcast to Audit for rejection record
ExecutionResult(result) Broadcast to Audit
VoteResult{passed=true} Call HarnessBridge.adjudicate() for final decision
VoteResponse(response) Cast into ConsensusManager, emit VoteResult on quorum
Shutdown Set shutdown_requested=true, exit main loop
Heartbeat / others Ignored

Voting Consensus + Harness Integration

VoteResponse → ConsensusManager
                    ▼ SimpleMajority (≥2 votes)
              VoteResult{passed=true}
        HarnessBridge.adjudicate(intent, ctx)
       ┌────────────┼────────────┬────────────┐
       ▼            ▼            ▼            ▼
   Approved     Rejected   CircuitBreak  NeedRevision
   (execute)   (audit)    (shutdown)      (re-analyze)

When no HarnessBridge is configured, fallback is Adjudication::Approved (zero-intrusion mode: vote passing ⇒ immediate approval).

3. Key Modules

3.1 DeclarativeAgentRunner trait

#[async_trait]
pub trait DeclarativeAgentRunner: Send + Sync {
    fn id(&self) -> &AgentId;
    fn role(&self) -> AgentRole;
    fn status(&self) -> AgentStatus;
    async fn handle_message(&mut self, msg: AgentMessage) -> Result<RunnerOutput, SwarmError>;
}
  • Object safety: satisfies object-safety, allows Arc<dyn DeclarativeAgentRunner> across tasks
  • Sync bound: Status returns Copy, handle_message takes &mut self + async, allows concurrency

3.2 Four Agents

Agent Input Output Config
MarketAgent MarketDataSource (tick) MarketSignal symbols + price_change_threshold
RiskAgent MarketSignal RiskAssessment RiskAgentConfig (default thresholds)
ExecutionAgent RiskAssessment{approved=true} ExecutionResult (via PlaceOrderTool) TradingTools { place_order, query_portfolio }
AuditAgent ExecutionResult (audit record) AuditAgentConfig (default)

3.3 PaperTradingBackend

PaperTradingBackend implements the Stage K TradingBackend trait, simulating real trading:

  • Slippage: slippage_bps (basis points, price up for buy / down for sell)
  • Commission: commission_bps (on notional)
  • State: cash + positions: HashMap<symbol, (qty, entry_price)> + last_prices
  • Price update: place_order updates last_prices[symbol] = fill_price
  • Cash flow: Buy deducts notional * (1 + commission_bps), Sell adds notional * (1 - commission_bps)

get_balance() returns cash + Σ(qty * last_price) (real-time NAV), get_positions() returns (symbol, qty, entry_price, current_price, unrealized_pnl).

3.4 SwarmOrchestrator::run_loop_arc

Arc<TokioMutex<SwarmOrchestrator>> shared across owners. Main loop:

pub async fn run_loop_arc(
    orchestrator: Arc<TokioMutex<Self>>,
    mut inbox_rx: mpsc::Receiver<AgentMessage>,
) {
    let tick = Duration::from_millis(guard.config.loop_tick_ms);
    loop {
        if guard.shutdown_requested { break; }
        let next = timeout(tick, inbox_rx.recv()).await;
        match next {
            Ok(Some(msg)) => dispatch(msg).await,
            Ok(None) => break,    // channel closed
            Err(_) => continue,   // timeout, recheck shutdown
        }
    }
}

Exit conditions: Shutdown message / request_shutdown() / inbox channel closed (all agent outboxes dropped).

4. Python Integration

4.1 Typical Usage

from axon_quant.llm import (
    SwarmConfig, SwarmOrchestrator, MarketSignal, SignalType,
    TradingTools,
)
from axon_quant.trading import (
    MockTradingBackend, PlaceOrderTool, QueryPortfolioTool, RiskLimits,
)

# 1. Construct orchestrator
config = SwarmConfig(vote_timeout_ms=5000, loop_tick_ms=100)
orch = SwarmOrchestrator(config)

# 2. Register 4 agents (register_*_agent auto-starts run_loop)
orch.register_market_agent(agent_id="m0", symbols=["BTC-USDT"])
orch.register_risk_agent(agent_id="r0")

# ExecutionAgent must receive tools (otherwise mock mode)
backend = MockTradingBackend()
risk = RiskLimits(allowed_symbols=["BTC-USDT"])
place = PlaceOrderTool(backend=backend, mode="dry_run", risk=risk)
query = QueryPortfolioTool(backend=backend)
tools = TradingTools(place_order=place, query_portfolio=query)
orch.register_execution_agent(agent_id="e0", tools=tools)

orch.register_audit_agent(agent_id="a0")

# 3. Inject MarketSignal → orchestrator triggers vote + forward
orch.inject_market_signal(MarketSignal(
    symbol="BTC-USDT",
    signal_type=SignalType.Buy,
    confidence=0.9,
    reasoning="momentum breakout",
))

# 4. Read stats
import time; time.sleep(0.5)
stats = orch.stats()
print(stats["market_signals"], stats["votes_created"])

# 5. Shutdown
orch.stop()

4.2 Full Pipeline Demo

See examples/18_harness/swarm_demo.py.

5. Test Coverage

Test File Count Content
python/tests/test_swarm_pipeline_e2e.py 25/25 ✅ Enums/structs + 4 agent register + lifecycle + inject + stats
crates/axon-llm/src/swarm/ lib unittests all pass DeclarativeAgentRunner / Orchestrator / Vote / 4 agents / market_data
crates/axon-llm/src/trading/paper_backend.rs lib unittests all pass place_order / balance / position with slippage + commission

Total: 322 lib unittests + 74 integration + 3 doctests + 25 Python E2E = 424 all pass.

6. Current Status and Unimplemented Items (as of 0.6.0)

Completed Roadmap Items (0.3.x / 0.4.x)

  • PlaceOrderTool three modes: DryRun / TwoPhase / Direct all implemented as SafetyMode variants.
  • DryRun is the default safety mode (intentional design, prevents LLM from sending orders directly).
  • TwoPhase uses pending: Mutex<HashMap<token, PendingOrder>> to track unconfirmed orders; 4 e2e tests cover: first call returns confirm_token / second call with token sends order / unknown token rejected / token consumed once.
  • Direct calls backend without interception.
  • RiskAgent basic limits: max_order_notional + quantity > 0 checks implemented; compliant orders get approved=true + risk_score=0.1 + empty violations, non-compliant orders include violation list.
  • Live exchange integration: ExchangeTradingBackend (crates/axon-llm/src/trading/exchange.rs) fully implemented; adapts ExchangeAdapter (Binance / OKX) to TradingBackend; gated by trading-exchange feature, with SymbolMap providing bidirectional LLM symbol ↔ exchange symbol mapping. Note: 8 unimplemented!() sites in the same file are stubs inside a #[cfg(test)] MockAdapter (not invoked by tests), not production code gaps.

Unimplemented (0.5.0+ Roadmap)

  • RiskAgent advanced risk logic: RiskAgentConfig defines max_position / max_drawdown fields but does not check them; risk_score is currently binary (0.1 / 0.9); volatility / VaR / historical drawdown window / position concentration not implemented. Planned for 0.5.0: integrate axon-risk::DefaultRiskEngine (12ns check chain, circuit breaker, VaR 95/99, real-time metrics) to replace the happy-path logic.
  • 4-Agent cross-process coordination (distributed swarm): Current SwarmOrchestrator uses single-process mpsc channels; cross-process coordination, ConsensusManager state-machine persistence, and Ray Actor wrapping via axon-distributed not implemented. Planned for 0.6.0+ roadmap.
  • Single-agent cross-process reuse: MarketAgent / RiskAgent / AuditAgent currently run as independent tokio::task::spawn instances; cross-process scheduling / shared LLM client / global prompt cache design still TBD.

7. 0.11.0 Multi-Agent Voting Consensus Layer

Added in v0.11.0 Location: crates/axon-llm/src/swarm/consensus.rs

0.11.0 adds a voting consensus decision layer on top of the existing 4-Agent pipeline, implementing the full flow of independent trader decisions → weighted vote aggregation → risk veto.

7.1 Decision Flow

Bar arrives
    ├──→ Trader A (ReAct/rule) ──→ AgentVote{Buy, 0.8}
    ├──→ Trader B (ReAct/rule) ──→ AgentVote{Buy, 0.6}
    └──→ Trader C (ReAct/rule) ──→ AgentVote{Hold, 0.0}
    ┌─────────────────────────┐
    │  VotingStrategy          │
    │  weighted majority:      │
    │  Buy = 0.8+0.6 = 1.4    │
    │  Hold = 0.0             │
    │  → aggregated: Buy(0.7) │
    └────────────┬────────────┘
    ┌─────────────────────────┐
    │  ConsensusRiskAgent     │
    │  checks:                 │
    │  - current position      │
    │  - consecutive losses    │
    │  - max position per bar  │
    │  → approve / veto       │
    └────────────┬────────────┘
         Final Action (Buy 0.7) or Hold (vetoed)

7.2 Core Types

// Vote
pub struct AgentVote {
    pub agent_id: String,
    pub action: TraderAction,    // Buy / Sell / Hold
    pub confidence: f64,
    pub reasoning: String,
}

// Voting strategy trait
pub trait VotingStrategy: Send + Sync {
    fn aggregate(&self, votes: &[AgentVote]) -> ConsensusDecision;
}

// Built-in strategies
// 1. WeightedMajorityVote — confidence-weighted, wins above threshold (0.5)
// 2. UnanimousVote — all votes must agree (conservative mode)

// Risk review (pure rules, no LLM)
pub struct ConsensusRiskAgent {
    pub max_position: f64,
    pub max_consecutive_loss: u32,
    pub max_drawdown: f64,
}

// Orchestrator
pub struct VotingOrchestrator {
    traders: Vec<Box<dyn TraderCallback>>,
    risk_agent: ConsensusRiskAgent,
    voting: Box<dyn VotingStrategy>,
}

7.3 Python Integration

from axon_quant._native import PyVotingOrchestrator

orch = PyVotingOrchestrator(
    traders=[trader_a, trader_b, trader_c],
    risk_config={"max_position": 0.5, "max_consecutive_loss": 3},
    voting="weighted_majority",
)
decision = orch.on_bar(bar_dict)
# decision: {"action": ..., "votes": [...], "risk_verdict": ..., "confidence": ...}

7.4 Relationship with 0.6.0 SwarmOrchestrator

Dimension 0.6.0 SwarmOrchestrator 0.11.0 VotingOrchestrator
Purpose 4-Agent async message pipeline Bar-by-bar synchronous voting
Communication tokio mpsc async Synchronous callback (TraderCallback)
Risk RiskAgent (LLM may intervene) ConsensusRiskAgent (pure rules)
Use case Production async trading Backtest / evaluation / multi-strategy comparison

Both coexist in the swarm/ module without mutual dependency.

7.5 Trajectory Schema v0.11.0

Extended from 0.10.0 schema: - Added votes array: each trader's action + confidence + reasoning - Added aggregation field: voting strategy name + aggregated result - Added risk_verdict field: approve/veto + reason - Added token_usage field: per-bar cumulative input/output tokens