The direct answer

A multi-agent system uses two or more independently bounded agents to complete one product workflow. Use it when tasks require different permissions, owners, context, scaling, or failure isolation. If one agent with a few well-defined tools can do the job, that is usually simpler, cheaper, and easier to secure.

The most maintainable starting design is an orchestrator that owns the user request and delegates narrow tasks to specialists. Specialists return structured results and evidence. They do not share a giant chat transcript, and they do not gain one another's credentials.

A2A, the Agent2Agent Protocol, standardizes how a client agent discovers and communicates with a remote agent. The released A2A 1.0 specification defines Agent Cards, supported interfaces, tasks, messages, artifacts, streaming, and related security declarations. It is a protocol for interoperability, not a reason to split a working application into agents.

Single agent or multiple agents?

Start with one agent. Give it a bounded instruction, a small tool set, a maximum number of steps, and an evaluation suite. Split only when evidence identifies a boundary.

Use a single agent when:

  • the workflow uses one trust domain and one permission set;
  • tasks are short and sequential;
  • one context contains the necessary information;
  • the same team owns the whole service;
  • failures should cancel the whole operation;
  • model and tool latency already dominate the experience.

Consider multiple agents when:

  • finance research and code execution require different sandboxes;
  • one specialist is operated by another organization;
  • tasks can run independently and parallelism materially reduces latency;
  • each domain needs its own evaluation, model, data boundary, or release cycle;
  • a long workflow benefits from durable handoffs and resumability;
  • a compromised specialist must not control the rest of the system.

"The prompt is getting long" is not enough. First try clearer tools, retrieval, context compaction, or ordinary service boundaries.

The orchestrator and specialist pattern

The orchestrator is the control plane. It authenticates the request, decomposes allowed work, selects specialists, tracks deadlines and budgets, validates results, resolves conflicts, and produces the final response.

A specialist should have one crisp capability: retrieve policy evidence, calculate tax, inspect code, or draft a support response. Its narrow purpose makes permissions and evaluations understandable.

type WorkOrder = {
  taskId: string;
  parentTaskId: string;
  objective: string;
  inputRefs: string[];
  constraints: string[];
  outputSchema: string;
  deadline: string;
  budget: { maxCalls: number; maxTokens: number };
  caller: { tenantId: string; userId: string; scopes: string[] };
};

type WorkResult = {
  taskId: string;
  status: "completed" | "failed" | "needs_input";
  output?: unknown;
  evidenceRefs: string[];
  warnings: string[];
  usage: { calls: number; inputTokens: number; outputTokens: number };
};

This contract is more important than whether each component uses the same framework. The orchestrator can reject malformed output, enforce a deadline, and explain which specialist failed.

Handoffs are product transitions

A handoff transfers responsibility for a task. Treat it like an API call, not like one model mentioning another model in prose.

Every handoff needs:

  • a stable task ID and parent ID;
  • a narrow objective and explicit non-goals;
  • typed inputs or references to authorized data;
  • expected output schema;
  • deadline, retry policy, and idempotency key;
  • caller identity and delegated scopes;
  • cancellation and status behavior;
  • evidence and warnings in the result.

Avoid copying full conversation history. Pass the minimum context the specialist needs, with provenance. The orchestrator retains user-facing responsibility; "the other agent said so" is not validation.

Handoffs should be observable. A user waiting on a long task needs meaningful states such as queued, working, needs approval, completed, failed, and canceled—not a permanent spinner.

Parallelism: fork, join, and cancel

Parallel agents help when subtasks are truly independent. A research workflow might ask legal, security, and cost specialists to assess the same proposed vendor concurrently, then join their reports.

const controller = new AbortController();
const jobs = specialists.map((specialist) =>
  specialist.run(workOrder, { signal: controller.signal }),
);

const results = await Promise.allSettled(jobs);
const accepted = results
  .filter((result) => result.status === "fulfilled")
  .map((result) => validateWorkResult(result.value));

Real systems also need concurrency limits, per-specialist timeouts, and cancellation propagation. Do not launch ten agents because the model proposed ten categories. Estimate the expected value of each branch and enforce a fan-out ceiling.

Define join behavior before launch:

  • Must every branch succeed?
  • Can partial evidence produce an answer?
  • Which specialist is authoritative for a conflict?
  • Does a failed high-risk check block the action?
  • When does the orchestrator ask a human?

Without those rules, parallelism produces a pile of plausible opinions.

Shared state without shared confusion

Do not use a shared mutable transcript as the system of record. Agents will overwrite assumptions, repeat tasks, and consume instructions embedded in another agent's output.

Use a task ledger with append-only events and versioned artifacts:

{
  "eventId": "evt_108",
  "taskId": "task_44",
  "type": "artifact.created",
  "actor": "security-specialist",
  "artifact": {
    "id": "artifact_73",
    "mediaType": "application/json",
    "schema": "risk-report.v2",
    "version": 1
  },
  "createdAt": "2026-09-16T09:15:00Z"
}

Agents read authorized snapshots. Writes use optimistic concurrency or append-only events. Large documents remain in an object store and travel by reference. Secrets stay in a broker or vault and are exchanged for short-lived, task-scoped credentials.

Keep these separate:

  • task state: lifecycle and ownership;
  • artifacts: durable outputs;
  • messages: communication between participants;
  • operational logs: timing, errors, and policy decisions;
  • memory: reusable facts governed by a separate lifecycle.

What A2A standardizes

A2A provides a common application-level contract for one agent to interact with another across process, framework, vendor, or organizational boundaries. In A2A 1.0, the normative protocol data objects and request/response messages are defined by the specification's Protocol Buffers source.

Core concepts include:

  • A2A Client: initiates requests on behalf of a user or system;
  • A2A Server: exposes one or more agent capabilities;
  • Agent Card: metadata describing identity, capabilities, skills, interfaces, and security requirements;
  • message: communication containing one or more parts;
  • task: a stateful unit of work with a lifecycle;
  • artifact: output produced by a task;
  • streaming and push notifications: ways to receive progress for long-running work.

A2A does not standardize your internal reasoning, prompt, memory database, or orchestration policy. It does not make an untrusted remote agent safe. Authentication, authorization, network policy, data classification, and result validation remain application responsibilities.

Agent Cards and discovery

An A2A 1.0 Agent Card is a JSON metadata document. Domain-based discovery uses:

https://agent.example.com/.well-known/agent-card.json

The card describes fields such as the agent name, description, version, capabilities, supported interfaces, default input and output modes, skills, and security requirements. In 1.0, endpoint details live in ordered supportedInterfaces entries, including the URL, protocol binding, and protocol version.

A simplified illustration:

{
  "name": "Policy Research Agent",
  "description": "Finds and cites approved policy documents.",
  "version": "2.1.0",
  "supportedInterfaces": [
    {
      "url": "https://agent.example.com/a2a",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    }
  ],
  "capabilities": {
    "streaming": true,
    "extendedAgentCard": true
  },
  "defaultInputModes": ["text/plain", "application/json"],
  "defaultOutputModes": ["application/json"],
  "skills": [
    {
      "id": "policy-search",
      "name": "Policy search",
      "description": "Returns relevant passages from approved policy collections.",
      "tags": ["policy", "retrieval"]
    }
  ]
}

Use the official schema for implementation; the sample is intentionally abbreviated. Validate cards, cache them for a bounded period, and re-check compatibility when versions change.

Public cards should reveal only what is safe for unauthenticated discovery. A2A supports an extended card flow for additional details after authentication when the card declares that capability. Do not advertise internal hostnames, sensitive data collections, privileged tools, or operational secrets.

A setup and design workflow

Step 1: prove the workflow with one agent

Write representative tasks and evaluate one bounded agent. Capture failures, latency, calls, accepted-result cost, and tool permissions. This baseline tells you whether decomposition helps.

Step 2: draw trust and ownership boundaries

Mark users, orchestrator, specialists, tools, data stores, identity provider, and external networks. Record which team and tenant owns each. A process boundary without a trust reason may be unnecessary complexity.

Step 3: define specialists as contracts

For each specialist, write:

  • one-sentence purpose;
  • accepted input schema and size;
  • output schema and evidence requirements;
  • allowed data classes and tools;
  • maximum runtime and cost;
  • failure and cancellation behavior;
  • owner and evaluation set.

Step 4: build deterministic routing first

Route by task type, tenant policy, or explicit user selection. Add model-based routing only if fixed rules cannot handle measured cases. Validate the model's route against an allowlist.

const routes = {
  policy_search: policyAgent,
  cost_analysis: costAgent,
} as const;

const route = routingSchema.parse(modelProposal);
const specialist = routes[route.kind];
if (!specialist) throw new Error("Unsupported specialist");

Step 5: make tasks durable

Persist state transitions with idempotency keys. A retry after a timeout must not purchase twice, send two emails, or recreate a task with no relationship to the first. Support cancellation from the user through every active branch.

Step 6: expose A2A at real boundaries

Use A2A when a specialist must interoperate as a remote agent, especially across teams or vendors. Publish a minimal Agent Card, choose a supported binding, implement task and message handling, and test with an independent client. Inside one codebase, a typed function or queue may remain the better interface.

Step 7: evaluate the whole graph

Unit-test specialists, then test routing, handoffs, joins, retries, partial failure, and security policy as a system. A 95% pass rate for each of five sequential steps does not imply a 95% workflow pass rate. Measure the completed workflow.

Failure modes to design for

Routing loop. Agent A delegates to B, which delegates back to A. Carry a hop count and visited-agent set; reject cycles and enforce a global step budget.

Lost task. The caller times out while the server keeps working. Use durable task IDs, status lookup, idempotency, and cancellation.

Partial join. Two branches finish and one stalls. Apply the predeclared deadline and partial-result policy.

Conflicting specialists. Do not ask another model to average incompatible facts. Use source authority, freshness, and domain ownership, or escalate.

Schema drift. A specialist deploys a new output version. Negotiate supported versions, validate at the boundary, and roll out compatibly.

Cascading retries. Orchestrator, SDK, gateway, and server all retry. Choose one retry owner per boundary, use exponential backoff with jitter, and cap total attempts.

Orphaned cost. Cancellation reaches the orchestrator but not remote work. Propagate cancellation, expire leases, and meter every task against the root budget.

Security boundaries

Treat every agent message, artifact, and card as untrusted input. A specialist may be compromised, misconfigured, or manipulated by indirect prompt injection.

Minimum controls:

  • authenticate both caller and server using mechanisms appropriate to the deployment;
  • authorize each task and tool action, not merely the connection;
  • delegate short-lived, least-privilege scopes rather than forwarding user tokens;
  • enforce tenant and data-class boundaries outside the model;
  • validate URLs to prevent server-side request forgery;
  • constrain outbound network access;
  • scan or sandbox active artifacts;
  • sign or otherwise verify trusted cards where your ecosystem supports it;
  • record task lineage, policy decisions, and artifact hashes;
  • require human approval for high-impact irreversible actions.

Do not trust an Agent Card because it appears at a well-known path. DNS, account, registry, or server compromise can change metadata. Maintain an approved-agent registry for sensitive workflows and pin expected identities or keys according to your security model.

Cost and latency controls

Multi-agent fan-out can multiply model calls, retrieval, and duplicated context. Track budgets at the root task and charge every child against it.

Measure:

  • total model and tool calls per completed workflow;
  • input duplicated across agents;
  • p50 and p95 end-to-end latency;
  • branch timeout and cancellation rate;
  • cost per accepted result, not per call;
  • quality gain over the single-agent baseline.

Use cheap deterministic code for decomposition and validation where possible. Cache safe, stable specialist outputs by tenant and source version. Stop launching branches once the answer has enough evidence. A larger graph is not automatically a better system.

When not to use multi-agent or A2A

Do not use multiple agents when a function call, queue worker, or ordinary microservice expresses the boundary more clearly. Do not use A2A solely because two prompts exist. A2A earns its place when you need agent-level discovery and task interaction across a genuine interoperability boundary.

Stay with one agent if the team cannot yet trace one run, cap its tools, evaluate its outputs, or recover from failure. Multiplying an unobservable agent multiplies uncertainty.

Implementation checklist

  • [ ] Single-agent baseline proves the need for decomposition
  • [ ] Each specialist has one purpose, owner, schema, budget, and eval set
  • [ ] Orchestrator owns routing, deadlines, joins, and user-facing outcome
  • [ ] Handoffs carry identity, scope, task IDs, constraints, and evidence
  • [ ] Shared state uses versioned tasks and artifacts, not one mutable transcript
  • [ ] Parallel branches have limits, cancellation, and partial-result rules
  • [ ] A2A implementation targets released 1.0 and validates official schemas
  • [ ] Agent Cards expose no sensitive operational detail
  • [ ] Remote agents are authenticated, authorized, and treated as untrusted
  • [ ] Workflow evaluation covers loops, drift, retries, and partial failures
  • [ ] Cost and quality are compared with the simpler baseline

Continue learning

Official sources

Takeaways

Split an agent only at a real boundary: permissions, ownership, context, scaling, or failure isolation. Keep the orchestrator accountable, pass structured work orders, and make shared state durable and inspectable.

A2A 1.0 gives remote agents a standard way to advertise capabilities and exchange tasks, messages, and artifacts. It solves interoperability, not trust. The simpler system wins until measured workflow results show that multiple agents earn their added security, cost, and operational load.