Multi-Agent System

ai

Definition and Overview

A multi-agent system (MAS) is a computational system where multiple autonomous agents interact, cooperate, compete, or negotiate to solve problems that exceed any individual agent's capabilities. Each agent in a MAS possesses its own knowledge, goals, and decision-making processes, and the system's emergent behavior arises from the interactions between agents rather than from centralized control. Multi-agent systems represent a fundamental shift from monolithic AI architectures to distributed, resilient, and scalable intelligence.

The theoretical foundations of multi-agent systems draw from distributed artificial intelligence, game theory, organizational theory, and complex adaptive systems. In a MAS, intelligence is not concentrated in a single entity but distributed across many specialized agents that collectively exhibit capabilities none possess individually. This mirrors how complex organizations function: a corporation's capabilities emerge from the interactions of specialized departments (engineering, sales, legal, operations), not from any single omniscient controller.

Multi-agent systems offer several advantages over single-agent architectures: they naturally decompose complex problems into manageable sub-problems, they provide fault tolerance through redundancy and graceful degradation, they scale horizontally by adding agents, and they can incorporate heterogeneous capabilities (different agents can use different algorithms, data sources, or reasoning methods). These properties make MAS particularly suitable for large-scale, real-world applications where centralized solutions are impractical.

The Prismatic Platform implements one of the largest production multi-agent systems in the Elixir ecosystem, with 530+ specialized agents operating across 16 domains, coordinated through OTP supervision trees and communicating via BEAM message passing. This is not a research prototype but a production system demonstrating that multi-agent architectures can operate at scale with the reliability guarantees required for real-world deployment.

Agent Properties and Taxonomy

Agents in a multi-agent system exhibit properties that distinguish them from simple software components or microservices. Understanding these properties is essential for designing effective multi-agent architectures.

PropertyDescriptionPrismatic Implementation
AutonomyOperates without direct human interventionAgents execute commands independently
ReactivityPerceives environment and responds to changesEvent-driven through telemetry
ProactivityTakes initiative toward goalsAuto-evolution and self-healing agents
Social AbilityInteracts with other agents through protocolsAIAD-compliant message passing
AdaptabilityModifies behavior based on experienceQuality DNA and pattern learning
Bounded RationalityMakes decisions within knowledge limitsDomain specialization and authority levels
VeracityDoes not intentionally communicate false informationNABLA Infinity axiom enforcement

Agent Classification

Agents can be classified along multiple dimensions. The Prismatic Platform uses a multi-dimensional classification that combines role, authority level, domain specialization, and operational mode.

defmodule Prismatic.MAS.AgentClassification do
  @moduledoc """
  Defines the multi-dimensional classification system for
  agents in the Prismatic multi-agent system.

  Each agent is classified by authority level, domain,
  operational mode, and specialization. This classification
  drives agent discovery, delegation, and coordination.
  """

  @type authority_level :: :l1_supreme | :l2_domain | :l3_team | :l4_specialist | :l5_worker
  @type domain :: :security | :quality | :osint | :devops | :infrastructure |
                  :agents | :storage | :ui | :documentation | :analytics |
                  :compliance | :testing | :performance | :strategic
  @type operational_mode :: :autonomous | :supervised | :reactive | :scheduled
  @type specialization :: String.t()

  @type agent_classification :: %{
    authority: authority_level(),
    domain: domain(),
    mode: operational_mode(),
    specialization: specialization(),
    capabilities: list(atom())
  }

  @spec classify(module()) :: {:ok, agent_classification()} | {:error, :unclassified}
  def classify(agent_module) do
    case agent_module.__agent_spec__() do
      %{} = spec ->
        {:ok, %{
          authority: parse_authority(spec),
          domain: parse_domain(spec),
          mode: parse_mode(spec),
          specialization: Map.get(spec, :specialization, "general"),
          capabilities: Map.get(spec, :capabilities, [])
        }}

      _ ->
        {:error, :unclassified}
    end
  rescue
    UndefinedFunctionError -> {:error, :unclassified}
  end

  @spec agents_by_domain(domain()) :: {:ok, list(module())}
  def agents_by_domain(domain) do
    agents =
      :prismatic_agents
      |> Application.get_env(:registered_agents, [])
      |> Enum.filter(fn agent ->
        case classify(agent) do
          {:ok, %{domain: ^domain}} -> true
          _ -> false
        end
      end)

    {:ok, agents}
  end

  @spec agents_by_authority(authority_level()) :: {:ok, list(module())}
  def agents_by_authority(level) do
    agents =
      :prismatic_agents
      |> Application.get_env(:registered_agents, [])
      |> Enum.filter(fn agent ->
        case classify(agent) do
          {:ok, %{authority: ^level}} -> true
          _ -> false
        end
      end)

    {:ok, agents}
  end

  defp parse_authority(%{level: level}) when level in [:l1, :L1], do: :l1_supreme
  defp parse_authority(%{level: level}) when level in [:l2, :L2], do: :l2_domain
  defp parse_authority(%{level: level}) when level in [:l3, :L3], do: :l3_team
  defp parse_authority(%{level: level}) when level in [:l4, :L4], do: :l4_specialist
  defp parse_authority(_), do: :l5_worker

  defp parse_domain(%{domain: domain}) when is_atom(domain), do: domain
  defp parse_domain(_), do: :unclassified

  defp parse_mode(%{mode: mode}) when is_atom(mode), do: mode
  defp parse_mode(_), do: :supervised
end

MAS Architectures

The choice of MAS architecture determines how agents are organized, how decisions are made, and how the system scales. Different architectures are appropriate for different problem domains.

ArchitectureCoordinationScalabilityFault ToleranceUse Case
HierarchicalTop-down commandModerateLimited by hierarchy depthMilitary C2, corporate workflows
Flat/PeerNegotiationHighHigh (no single point of failure)Marketplaces, swarm intelligence
HybridBothHighHighEnterprise systems, Prismatic Platform
BlackboardShared knowledge baseModerateModerateExpert systems, collaborative problem solving
Contract NetTask announcement and biddingHighModerateResource allocation, logistics
HolonicRecursive hierarchyVery HighVery HighManufacturing, supply chains

Hierarchical Architecture

The Prismatic Platform uses a hybrid architecture with a strong hierarchical backbone. The hierarchy follows a military-inspired command structure that provides clear authority delegation while allowing lateral coordination between peers.

LevelRoleAuthorityAgent CountExamples
L1Supreme CommanderStrategic decisions, cross-domain authority5+Archer Supreme, Orchestrator
L2Domain CommanderTactical decisions within domain15+Red Commander, Blue Commander, QA Lead
L3Team LeadOperational decisions, team coordination30+OSINT Lead, Security Lead
L4SpecialistTask execution with domain expertise200+Code Reviewer, Data Collector, Drift Detector
L5WorkerAtomic operations, no delegation authority280+File Scanner, URL Fetcher, Report Generator

Communication Patterns

Multi-agent systems rely on well-defined communication protocols that enable agents to coordinate without tight coupling. The choice of communication pattern affects system latency, reliability, and complexity.

defmodule Prismatic.MAS.Communication do
  @moduledoc """
  Implements the communication patterns used by agents
  in the Prismatic multi-agent system.

  All inter-agent communication uses BEAM message passing,
  providing guaranteed delivery within a node and transparent
  routing across distributed nodes.
  """

  @type message :: %{
    from: pid() | atom(),
    to: pid() | atom(),
    type: :request | :response | :broadcast | :event,
    payload: term(),
    correlation_id: String.t(),
    timestamp: DateTime.t()
  }

  @doc """
  Direct point-to-point communication between agents.

  Used when one agent needs a specific response from
  another agent. Implements timeout to prevent indefinite
  waiting.
  """
  @spec request(GenServer.server(), term(), timeout()) ::
    {:ok, term()} | {:error, :timeout | term()}
  def request(agent, payload, timeout \\ 5_000) do
    correlation_id = generate_correlation_id()

    message = %{
      from: self(),
      type: :request,
      payload: payload,
      correlation_id: correlation_id,
      timestamp: DateTime.utc_now()
    }

    try do
      result = GenServer.call(agent, {:agent_message, message}, timeout)
      {:ok, result}
    catch
      :exit, {:timeout, _} -> {:error, :timeout}
      :exit, reason -> {:error, reason}
    end
  end

  @doc """
  Broadcast a message to all agents in a domain.

  Used for announcements, alerts, and status updates
  that all agents in a domain need to receive.
  """
  @spec broadcast(atom(), term()) :: :ok
  def broadcast(domain, payload) do
    message = %{
      from: self(),
      type: :broadcast,
      payload: payload,
      correlation_id: generate_correlation_id(),
      timestamp: DateTime.utc_now()
    }

    Registry.dispatch(Prismatic.MAS.Registry, domain, fn entries ->
      for {pid, _} <- entries do
        send(pid, {:agent_broadcast, message})
      end
    end)

    :ok
  end

  @doc """
  Publish an event to a topic for interested subscribers.

  Implements the publish-subscribe pattern where agents
  can subscribe to event topics without knowing the
  publishers.
  """
  @spec publish(String.t(), term()) :: :ok
  def publish(topic, payload) do
    message = %{
      from: self(),
      type: :event,
      payload: payload,
      correlation_id: generate_correlation_id(),
      timestamp: DateTime.utc_now()
    }

    Phoenix.PubSub.broadcast(
      Prismatic.PubSub,
      "agent:#{topic}",
      {:agent_event, message}
    )

    :ok
  end

  defp generate_correlation_id do
    :crypto.strong_rand_bytes(16) |> Base.encode16(case: :lower)
  end
end

Communication Pattern Comparison

PatternCouplingLatencyReliabilityUse Case
Direct MessagingHighLowHigh (local)Request-response, delegation
BroadcastLowMediumBest-effortAnnouncements, status updates
Publish-SubscribeVery LowMediumConfigurableEvents, notifications
Request-ReplyHighLowHighSynchronous coordination
NegotiationMediumHighProtocol-dependentConsensus, task allocation

Coordination Mechanisms

Coordination is the central challenge in multi-agent systems. Agents must work together effectively without centralized control, handling conflicts, resource contention, and emergent behavior.

MechanismDescriptionPrismatic Implementation
CooperationAgents work toward shared goalsOSINT collection across 120+ sources
CompetitionAgents compete for resources or solutionsRed vs Blue team adversarial operations
NegotiationAgents reach agreements through exchangeTask allocation and resource bidding
DelegationAgents assign tasks to subordinatesSupreme Commander to Domain Commander
MediationCentral agent coordinates interactionsPurple team synthesis of Red/Blue findings
StigmergyIndirect coordination through environmentShared knowledge base updates

Color-Team Operations

The Prismatic Platform's color-team security operations represent one of the most sophisticated multi-agent coordination patterns in the system. Six teams of specialized agents engage in adversarial-defensive synthesis.

defmodule Prismatic.MAS.ColorTeam do
  @moduledoc """
  Orchestrates color-team security operations across
  six specialized agent teams.

  The signal flow creates an adversarial-defensive loop:
  Gray discovers boundaries, Red exploits them, Blue defends,
  Purple synthesizes, White verifies, and Black models threats.
  """

  @type team :: :gray | :red | :blue | :purple | :white | :black
  @type operation_status :: :planning | :active | :synthesis | :complete
  @type finding :: %{
    team: team(),
    severity: :critical | :high | :medium | :low,
    description: String.t(),
    evidence: list(map()),
    timestamp: DateTime.t()
  }

  @spec initiate_campaign(String.t(), keyword()) ::
    {:ok, String.t()} | {:error, term()}
  def initiate_campaign(target_description, opts \\ []) do
    campaign_id = generate_campaign_id()
    teams = Keyword.get(opts, :teams, [:gray, :red, :blue, :purple])

    with {:ok, _} <- validate_authorization(opts),
         {:ok, _} <- activate_teams(campaign_id, teams),
         {:ok, _} <- configure_signal_flow(campaign_id, teams) do
      {:ok, campaign_id}
    end
  end

  @spec get_campaign_findings(String.t()) :: {:ok, list(finding())}
  def get_campaign_findings(campaign_id) do
    findings =
      [:gray, :red, :blue, :purple, :white, :black]
      |> Enum.flat_map(fn team ->
        case get_team_findings(campaign_id, team) do
          {:ok, team_findings} -> team_findings
          {:error, _} -> []
        end
      end)
      |> Enum.sort_by(& &1.timestamp, {:desc, DateTime})

    {:ok, findings}
  end

  defp validate_authorization(opts) do
    case Keyword.get(opts, :authorization) do
      nil -> {:error, :authorization_required}
      auth -> {:ok, auth}
    end
  end

  defp activate_teams(_campaign_id, _teams), do: {:ok, :activated}
  defp configure_signal_flow(_campaign_id, _teams), do: {:ok, :configured}
  defp get_team_findings(_campaign_id, _team), do: {:ok, []}
  defp generate_campaign_id, do: "campaign-#{System.unique_integer([:positive])}"
end

Signal Flow Architecture

The color-team signal flow creates a continuous adversarial-defensive loop:

Gray (boundary seeds) --> Red (adversarial scenarios) --> Purple (synthesis) --> Blue (defense)
                                    ^                          |       ^           |
                                    |                          v       |           v
                               Black (threat models)     White (proofs)    Platform Defense

Emergent Behavior

One of the most powerful properties of multi-agent systems is emergence: the system exhibits capabilities that no individual agent possesses. These capabilities arise from the interactions between agents and cannot be predicted by examining any single agent in isolation.

Emergent CapabilityContributing AgentsMechanism
Cross-Domain IntelligenceOSINT + Security + DevelopmentFindings from one domain inform decisions in others
Self-HealingQuality + Evolution + TestingQuality agents detect issues, evolution agents fix them
Adversarial ResilienceRed + Blue + Purple teamsAttacks strengthen defenses through synthesis
Knowledge SynthesisAll domain specialistsMultiple perspectives combined through NABLA Infinity
Predictive MaintenancePerformance + Analytics + InfrastructurePattern detection across subsystems
Autonomous EvolutionEvolution + Quality + TestingPlatform improves itself between sessions

Emergence Through Agent Interaction

defmodule Prismatic.MAS.EmergentCapability do
  @moduledoc """
  Demonstrates how emergent capabilities arise from
  agent interactions in the multi-agent system.

  No single agent can perform cross-domain intelligence
  synthesis. The capability emerges when OSINT agents
  feed findings to security agents, which inform
  development agents, creating intelligence that none
  could produce alone.
  """

  @type intelligence_report :: %{
    sources: list(atom()),
    confidence: float(),
    findings: list(map()),
    synthesis: String.t(),
    recommendations: list(String.t())
  }

  @spec synthesize_cross_domain(list(atom()), String.t()) ::
    {:ok, intelligence_report()} | {:error, term()}
  def synthesize_cross_domain(domains, query) do
    domain_findings =
      domains
      |> Enum.map(fn domain ->
        Task.async(fn ->
          gather_domain_intelligence(domain, query)
        end)
      end)
      |> Task.await_many(30_000)
      |> Enum.filter(&match?({:ok, _}, &1))
      |> Enum.map(fn {:ok, findings} -> findings end)

    case domain_findings do
      [] ->
        {:error, :no_intelligence_gathered}

      findings ->
        report = %{
          sources: domains,
          confidence: calculate_confidence(findings),
          findings: List.flatten(findings),
          synthesis: synthesize_findings(findings),
          recommendations: generate_recommendations(findings)
        }

        {:ok, report}
    end
  end

  defp gather_domain_intelligence(_domain, _query), do: {:ok, []}
  defp calculate_confidence(findings), do: min(1.0, length(findings) * 0.2)
  defp synthesize_findings(_findings), do: "Cross-domain synthesis"
  defp generate_recommendations(_findings), do: ["Review findings"]
end

OTP as MAS Foundation

The BEAM virtual machine and OTP provide an ideal foundation for multi-agent systems that no other mainstream platform can match.

OTP FeatureMAS Benefit
Process per AgentEach agent runs as an isolated BEAM process (~2KB memory)
Supervision TreesHierarchical fault tolerance for agent populations
Message PassingNative asynchronous communication between agents
Hot Code UpgradeAgents can be upgraded without system downtime
DistributionAgents can span multiple nodes in a cluster
GenServerStandard behavior for stateful agents
RegistryAgent discovery and name resolution
DynamicSupervisorRuntime agent creation and destruction

The key insight is that OTP behaviors (GenServer, Supervisor, GenStage) are themselves formalizations of agent patterns. A GenServer is an agent that maintains state and processes messages. A Supervisor is a meta-agent that manages agent lifecycles. A DynamicSupervisor enables runtime agent creation, the "Create" capability of the actor model.

Agent Lifecycle Management

Multi-agent systems must manage agent lifecycles: creation, initialization, operation, upgrade, degradation, and termination. OTP provides robust lifecycle management through supervision trees.

defmodule Prismatic.MAS.AgentLifecycle do
  @moduledoc """
  Manages the lifecycle of agents in the multi-agent system.

  Handles agent creation, health monitoring, graceful
  degradation, and controlled shutdown. Uses DynamicSupervisor
  for runtime agent management.
  """

  use GenServer

  @type agent_state :: :initializing | :active | :degraded | :shutting_down | :terminated
  @type agent_info :: %{
    pid: pid(),
    module: module(),
    state: agent_state(),
    started_at: DateTime.t(),
    health_checks: non_neg_integer()
  }

  @spec start_agent(module(), keyword()) ::
    {:ok, pid()} | {:error, term()}
  def start_agent(agent_module, opts \\ []) do
    spec = {agent_module, opts}

    case DynamicSupervisor.start_child(Prismatic.MAS.AgentSupervisor, spec) do
      {:ok, pid} ->
        register_agent(pid, agent_module)
        {:ok, pid}

      {:error, reason} ->
        {:error, reason}
    end
  end

  @spec stop_agent(pid(), timeout()) :: :ok | {:error, term()}
  def stop_agent(pid, timeout \\ 5_000) do
    case DynamicSupervisor.terminate_child(Prismatic.MAS.AgentSupervisor, pid) do
      :ok ->
        unregister_agent(pid)
        :ok

      {:error, :not_found} ->
        {:error, :agent_not_found}
    end
  end

  @spec agent_health(pid()) :: {:ok, agent_info()} | {:error, :not_found}
  def agent_health(pid) do
    case Process.alive?(pid) do
      true ->
        info = %{
          pid: pid,
          module: get_agent_module(pid),
          state: :active,
          started_at: get_start_time(pid),
          health_checks: get_health_check_count(pid)
        }

        {:ok, info}

      false ->
        {:error, :not_found}
    end
  end

  defp register_agent(_pid, _module), do: :ok
  defp unregister_agent(_pid), do: :ok
  defp get_agent_module(_pid), do: nil
  defp get_start_time(_pid), do: DateTime.utc_now()
  defp get_health_check_count(_pid), do: 0
end

Scalability Patterns

Multi-agent systems must scale as problem complexity and data volume grow. The Prismatic Platform employs several scalability patterns.

PatternDescriptionWhen to Use
Horizontal ScalingAdd more agent instancesStateless or partitioned workloads
Domain PartitioningAssign agents to non-overlapping data domainsLarge datasets with natural partitions
Hierarchical DelegationHigher-level agents delegate to specialistsComplex multi-step tasks
Agent PoolingMaintain pools of interchangeable agentsHigh-throughput uniform processing
Dynamic ScalingCreate/destroy agents based on demandVariable workloads

Prismatic Platform Implementation

AIAD Agent Architecture

The Prismatic Platform implements one of the largest multi-agent systems in the Elixir ecosystem with 530+ specialized agents:

DomainAgent CountKey Capabilities
Security20Color-team operations (Gray/Red/Blue/Purple/White/Black)
OSINT45+Intelligence collection from 120+ sources
Quality30+Code analysis, testing, compliance
Development50+Code generation, refactoring, documentation
Infrastructure25+Deployment, monitoring, scaling
Evolution15+Self-improvement, pattern detection

Emergent Capabilities in Production

The multi-agent architecture enables capabilities that no single agent possesses:

  • Cross-Domain Intelligence: OSINT agents feed findings to security agents, which inform development agents
  • Self-Healing: Quality agents detect issues that evolution agents fix automatically
  • Adversarial Resilience: Red team attacks strengthen Blue team defenses through Purple team synthesis
  • Knowledge Synthesis: Multiple specialist perspectives combined through NABLA Infinity framework

Challenges and Mitigations

ChallengeDescriptionPrismatic Mitigation
Coordination OverheadMore agents means more communicationHierarchical delegation reduces message volume
Emergent FailuresUnexpected interactions between agentsColor-team adversarial testing
State ConsistencyDistributed state can divergeNABLA Infinity provenance tracking
Agent ConflictsAgents with competing objectivesAuthority levels and domain boundaries
Debugging ComplexityHard to trace causality in MASStructured logging with correlation IDs
Resource ContentionAgents competing for shared resourcesBackpressure and rate limiting

Historical Context

YearMilestone
1973Carl Hewitt's actor model provides theoretical foundation
1986Distributed AI research formalizes multi-agent concepts
1995FIPA (Foundation for Intelligent Physical Agents) established
1997FIPA Agent Communication Language (ACL) standardized
2000sAgent-based modeling becomes mainstream in simulation
2004JADE (Java Agent Development Framework) reaches maturity
2010sMicroservices architecture echoes MAS principles
2020sLLM-based agents create new MAS possibilities
2023AutoGPT and BabyAGI demonstrate LLM multi-agent potential
2024-2026Prismatic Platform demonstrates production-scale MAS with 530+ agents

Connect & Contribute

Created by Tomas Korcak (korczis) | Open Source under GHL