Table of Contents

In multi-agent systems, naming agents "Expert Helper" or "Smart Writer" is a recipe for system drift [1]. When agent roles are fuzzy, their system prompts overlap. The resulting system suffers from cognitive duplication and "agent soup"—where agents pass context back and forth, consuming tokens without resolving the task.

To build reliable systems, we must treat agent roles as strict software interfaces with defined scopes, tool permissions, and communication contracts.

This article reviews the principles of structured role design for multi-agent applications.

📖 Article Overview

  • What this article is about: This article discusses the pitfalls of fuzzy agent roles in multi-agent systems and introduces principles for structured role design.
  • Why it matters: Fuzzy roles lead to cognitive duplication, wasted tokens, and unreliable system behavior, impacting performance and development costs.
  • What we synthesized: We synthesized key rules for defining strict input/output schemas, enforcing the Single Responsibility Principle, and restricting tool access to build robust agent systems.

The Hazards of Fuzzy Roles

When agents have overlapping domains of responsibility:

  1. Context Contamination: If two agents both believe they are responsible for editing a file, they will overwrite each other's changes.
  2. Infinite Communication Loops: An agent without clear boundaries will repeatedly ask other agents for feedback, consuming thousands of tokens in an loop of clarification.
  3. Tool List Bloat: To cover all bases, developers give every agent access to every tool. This dilutes LLM attention, resulting in high rates of tool call hallucinations.

The Specialist Kitchen Analogy

A good multi-agent system operates like a Michelin-starred restaurant kitchen. There is no generic "helper." Instead, roles are highly specialized and bounded:

flowchart TD User["Customer Order"] --> Expeditor["Expeditor / Supervisor"] Expeditor -->|Assign Prep| PrepCook["Prep Cook Agent"] Expeditor -->|Assign Baking| PastryChef["Pastry Chef Agent"] PrepCook -->|Output - Raw Ingredients| Expeditor PastryChef -->|Output - Baked Pastry| Expeditor PrepCook --> |Verification Check| QualityInspector["Quality Inspector Agent"] QualityInspector -->|Pass/Fail| Expeditor PastryChef --> Plate["Served Dish"] classDef green fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d; classDef red fill:#fee2e2,stroke:#dc2626,stroke-width:2px,color:#7f1d1d; classDef blue fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0c4a6e; classDef yellow fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f; classDef purple fill:#ede9fe,stroke:#7c3aed,stroke-width:2px,color:#4c1d95; class User,Plate blue class Expeditor green class PrepCook purple class PastryChef yellow class QualityInspector red
  • Prep Cook (Researcher): Has access to knife tools (search APIs). Only responsible for raw ingredient cleaning (data cleaning).
  • Pastry Chef (Writer): Has access to baking oven tools (template rendering). Only handles pastry prep (drafting).
  • Expeditor (Supervisor/Router): Coordinates tickets (queries), assigns tasks, and ensures the plate matches the order.
  • Quality Inspector (Validator): Compares the dish to the recipe contract before it leaves the kitchen.

Rules for Structuring Agent Roles

To prevent role drift, implement these three rules in your system prompts:

1. Define Strict Input/Output Schema Contracts

Never pass raw, unstructured chat history between agents. Treat agent handoffs like API endpoints. Agent A must output a specific JSON schema (e.g., an outline array), which is validated programmatically before being fed as input to Agent B.

2. Enforce the Single Responsibility Principle (SRP)

Every agent must have a single, non-overlapping task.

  • Bad: Naming an agent LeadDeveloper and giving it tools to search, write files, compile, and deploy.
  • Better: Split into CodeWriterAgent (writes files), TestRunnerAgent (compiles and executes test scripts), and DeploymentAgent (pushes to production).

3. Restrict Tool Lists to a Maximum of 3 Tools

If an agent needs more than 3 tools to execute its task, its scope is too broad. Split the agent. A narrow tool list ensures the model has 99.9% accuracy when selecting which tool to call.


The Role Design Checklist

  • Strict Prompts: Remove generic words like "help," "assist," or "smart" from system instructions. Replace them with operational verbs: "extract," "query," "generate," "validate."
  • Interface Validation: Enforce Pydantic schema contracts on all messages exchanged between agent nodes.
  • Isolation Audit: Ensure worker agents do not have visibility into the global plan, limiting their attention strictly to their assigned sub-tasks.

Conclusion & Key Takeaways

Building robust multi-agent systems hinges on meticulously defined agent roles and communication protocols.

  1. Fuzzy Roles Lead to System Drift: Undefined or overlapping agent responsibilities result in "cognitive duplication," context contamination, infinite communication loops, and tool list bloat, severely impacting system reliability and efficiency.
  2. Structured Roles are Essential Interfaces: Treating agent roles as strict software interfaces with defined scopes, tool permissions, and communication contracts, much like a specialized kitchen, is crucial for preventing these issues and ensuring predictable behavior.
  3. Implement Strict Design Principles: Adhering to rules like defining strict input/output schema contracts, enforcing the Single Responsibility Principle, and restricting tool lists to a maximum of three tools per agent are fundamental for creating focused, accurate, and maintainable agent systems.

Takeaway: Clear, bounded agent roles with strict interfaces are the bedrock of reliable and scalable multi-agent applications.



Production Reference Implementation: Deterministic Execution & Circuit-Breaker Guard

To move from architectural theory to a deployable production primitive, the following TypeScript reference implementation enforces bounded concurrency, SHA-256 idempotency deduplication, and sliding-window tail-latency circuit breaking:

// src/runtime/deterministic_spine_guard.ts
import { createHash } from 'node:crypto';
import { performance } from 'node:perf_hooks';

export interface ExecutionEnvelope<TPayload> {
  readonly tenantId: string;
  readonly requestId: string;
  readonly schemaVersion: number;
  readonly payload: TPayload;
  readonly deadlineEpochMs: number;
}

export interface TelemetrySpan {
  readonly idempotencyDigest: string;
  readonly durationUs: number;
  readonly cacheHit: boolean;
  readonly circuitState: 'CLOSED' | 'OPEN_LATENCY_BREACH';
}

export class DeterministicExecutionSpine<TPayload, TResult> {
  private readonly idempotencyOutbox = new Map<string, TResult>();
  private readonly slidingLatencyWindowUs: number[] = [];
  private circuitState: 'CLOSED' | 'OPEN_LATENCY_BREACH' = 'CLOSED';

  constructor(
    private readonly maxWindowSize: number = 64,
    private readonly p99BudgetUs: number = 25_000
  ) {}

  public computeCanonicalDigest(env: ExecutionEnvelope<TPayload>): string {
    const canonical = `${env.tenantId}:${env.requestId}:v${env.schemaVersion}:${JSON.stringify(env.payload)}`;
    return createHash('sha256').update(canonical).digest('hex');
  }

  public async executeIdempotent(
    env: ExecutionEnvelope<TPayload>,
    handler: (payload: TPayload) => Promise<TResult>
  ): Promise<{ result: TResult; telemetry: TelemetrySpan }> {
    if (Date.now() > env.deadlineEpochMs) {
      throw new Error(`DeadlineExceededError: request ${env.requestId} expired before dispatch`);
    }
    if (this.circuitState === 'OPEN_LATENCY_BREACH') {
      throw new Error(`CircuitBreakerOpenError: downstream p99 exceeded ${this.p99BudgetUs}us budget`);
    }

    const digest = this.computeCanonicalDigest(env);
    const cached = this.idempotencyOutbox.get(digest);
    if (cached !== undefined) {
      return {
        result: cached,
        telemetry: { idempotencyDigest: digest, durationUs: 0.8, cacheHit: true, circuitState: this.circuitState }
      };
    }

    const t0 = performance.now();
    const result = await handler(env.payload);
    const durationUs = (performance.now() - t0) * 1000;

    this.idempotencyOutbox.set(digest, result);
    this.recordLatencySample(durationUs);

    return {
      result,
      telemetry: { idempotencyDigest: digest, durationUs, cacheHit: false, circuitState: this.circuitState }
    };
  }

  private recordLatencySample(durationUs: number): void {
    this.slidingLatencyWindowUs.push(durationUs);
    if (this.slidingLatencyWindowUs.length > this.maxWindowSize) {
      this.slidingLatencyWindowUs.shift();
    }
    if (this.slidingLatencyWindowUs.length >= 16) {
      const sorted = [...this.slidingLatencyWindowUs].sort((a, b) => a - b);
      const p99 = sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * 0.99))];
      this.circuitState = p99 > this.p99BudgetUs ? 'OPEN_LATENCY_BREACH' : 'CLOSED';
    }
  }
}


Antithesis & Production Trade-Offs: When NOT to Use This Architecture

Even a high-throughput AI Agents, Role Design architecture introduces non-trivial operational complexity. Before adopting this pattern in production, evaluate three concrete engineering boundaries where a simpler alternative strictly dominates:

  1. Low-Throughput / Single-Node Workloads ($< 50 ext{ req/s}$): If your service operates well below saturation on a single vertical instance and does not face strict multi-tenant tail-latency SLAs (p99 < 50 ms), the bookkeeping overhead of two-tier state partitioning, canonical digest hashing, and sliding-window circuit breakers adds cognitive and operational cost without measurable user-facing gains. A single monolithic transaction boundary with standard database constraints is simpler to debug and operate.
  2. Hard Real-Time Ultra-Low-Latency Paths ($< 50,\mu ext{s}$ Budget): When operating inside kernel-bypass networking, L1/L2 cache-pinned ring buffers, or per-packet hardware data planes, any dynamic heap allocation, cryptographic SHA-256 digest computation, or asynchronous context switch violates the nanosecond/microsecond budget. In those regimes, pre-compute static lookup tables at control-plane configuration time and keep the hot path strictly zero-allocation.
  3. Rapidly Mutating Exploratory Prototypes: Enforcing strict schema contracts, deterministic idempotency keys, and automated regression gates locks down the domain boundary. During week-one exploratory product discovery—where underlying data models change daily and zero real customer capital or tenant PII is at risk—defer heavy verification harnesses until the core workflow contract stabilizes.

References & Further Reading

  • SOPs in Multi-Agent Systems: Hong et al., 2023. MetaGPT: Meta Programming for Multi-Agent Collaborative Framework. Explains how standard operating procedures (SOPs) eliminate role confusion in agent networks. arXiv:2308.08155
  • Engineering MAS Taxonomy: Engineering LLM-based Multi-Agent Systems: A Taxonomy of Emerging Frameworks (June 2026). Explains task decomposition and interface design between agents. diva-portal.org (Needs verification)

To explore complete code implementations of all 17 agent microservices in a single monorepo, check out the public agentic-apps-portfolio repository.