Resource library

QA How-To

Testing multi-agent systems (2026)

Learn testing multi-agent systems with graph invariants, agent doubles, handoff contracts, concurrency tests, evals, trace replay, and CI release gates.

26 min read | 3,194 words

TL;DR

Testing multi-agent systems requires separate evidence for the orchestration protocol and generative behavior. Verify typed handoffs, graph invariants, tool authorization, idempotent state, bounded termination, recovery, and semantic task outcomes, then preserve a trace that explains the earliest failure.

Key Takeaways

  • Model the agent system as a graph with explicit node, edge, state, and termination contracts.
  • Use scripted agents and stateful tool fakes for deterministic protocol and failure-path coverage.
  • Assert routing, permissions, state changes, and side effects in addition to the final answer.
  • Drive concurrency, retry, restart, cancellation, duplicate delivery, and loop scenarios deliberately.
  • Evaluate real models on versioned outcomes with hard safety gates and slice-level quality metrics.
  • Preserve sanitized traces and complete version manifests so every regression can be reproduced.

Testing multi-agent systems requires more than checking whether the final answer sounds correct. A release-grade strategy verifies delegation, message contracts, tool permissions, shared state, termination, recovery, cost, and the final outcome. The key is to make each handoff observable and to test deterministic protocol rules separately from probabilistic model behavior.

This guide gives QA and SDET engineers a practical 2026 approach. You will learn how to model an agent graph, build a risk-based test matrix, create controlled doubles, test concurrent execution, replay traces, evaluate outcomes, and design release gates that catch failures hidden by a polished final response.

TL;DR

Layer Main question Best test signal
Agent Did one agent follow its role and limits? input, output, tool calls, local assertions
Handoff Was the next agent selected with valid context? typed message and routing trace
Workflow Did the graph reach the right terminal state? state transitions and invariants
Outcome Was the user goal achieved safely? task rubric plus deterministic checks
Operations Can the system recover within its budget? retries, latency, tokens, cost, termination reason

Treat a multi-agent run as a distributed workflow with nondeterministic workers. Assert hard contracts around the workers, evaluate semantic quality with reviewed cases, and preserve a trace that lets a failure be reproduced.

1. Define Testing Multi-Agent Systems at the Right Boundaries

A multi-agent system contains two different things: an orchestration protocol and one or more generative decision makers. Testing becomes unreliable when those are collapsed into one final-answer assertion. Start by naming boundaries that can fail independently.

An agent boundary includes its role instructions, accepted message shape, available tools, output schema, timeout, and permission scope. A handoff boundary includes the routing decision, context selected for the recipient, correlation identifiers, and expected response. The workflow boundary includes graph state, stop conditions, retry policy, and final synthesis. The environment boundary includes model provider, tools, databases, queues, and external services.

Write one contract for each boundary. For example, a research agent may read approved sources but may not send email. A reviewer may reject or approve a draft but may not silently rewrite transaction values. An orchestrator may retry a transient tool error twice, but must stop on an authorization error. These are deterministic product rules even when a model chooses the next action.

Do not start with prompts. Start with the user goal and unacceptable outcomes. For a travel-planning crew, an acceptable outcome could require matching dates, budget, and traveler constraints. Unacceptable outcomes include booking without confirmation, exposing another user's itinerary, or continuing after the user cancels. This framing keeps tests stable when prompts or models change.

2. Model the Agent Graph, State, and Invariants

Draw the system as a directed graph. Nodes are agents or deterministic services. Edges are allowed handoffs. State is the minimum durable information required to resume or explain a run. The test model should distinguish planned topology from the path actually taken.

For each node, record inputs, outputs, tools, mutable state, failure modes, timeout, and owner. For each edge, record the routing condition, message schema, maximum visits, and whether the transition is reversible. Then define invariants that must hold across every path.

Useful invariants include:

  • every message has a run ID, sender, recipient, sequence number, and schema version,
  • only one agent owns a mutable field at a time,
  • an irreversible action requires an explicit authorization artifact,
  • tool results are treated as untrusted data, not new system instructions,
  • a completed or cancelled run cannot return to an active state,
  • the graph has a bounded number of steps and tool calls,
  • the final answer identifies uncertainty when required evidence is missing.

A state-transition table exposes gaps early:

Current state Event Next state Required assertion
planning plan accepted executing steps and limits persisted
executing recoverable tool error retrying attempt count increments once
executing approval needed waiting no side effect occurs
waiting approval denied cancelled pending action discarded
reviewing quality accepted completed terminal result and trace saved

This model supports path coverage, model-based testing, and production monitoring. It also gives interviewers concrete evidence that you understand multi-agent quality beyond prompt evaluation.

3. Build a Risk-Based Multi-Agent Test Matrix

Combinatorial explosion is the central test-design problem. A graph with several agents, tools, model variants, and retry outcomes has too many possible runs for exhaustive testing. Partition by risk and interaction pattern.

Start with happy paths, then add routing errors, malformed messages, stale context, conflicting agent conclusions, partial tool failure, timeout, repeated delegation, duplicated delivery, cancellation, permission denial, and recovery after process restart. Include adversarial content in user messages and tool output. Include long histories that force context selection rather than assuming every token fits.

Use a matrix that pairs failure mode with the smallest test level capable of detecting it:

Risk Component test Contract test Workflow test Production check
wrong recipient router rule allowed edge trace path route distribution
lost context context selector message schema outcome case missing-field rate
unauthorized tool permission function tool gateway adversarial scenario denied-call alert
delegation loop step counter transition rule cycle scenario termination reason
conflicting writes state reducer ownership contract concurrent schedule conflict count
weak outcome local rubric none golden scenario sampled review

Prioritize irreversible side effects, privacy, financial or legal impact, and failures that a final-answer judge cannot see. A run can produce a correct summary after making an unauthorized call. Outcome-only scoring would pass it.

Connect scenarios to a versioned dataset. The approach in writing golden datasets for LLM evals helps keep user goals, expected constraints, and risk slices auditable.

4. Create Deterministic Agent Doubles and Fault Injection

Live-model end-to-end tests are useful, but they are a poor foundation for protocol coverage. Build controlled agent doubles that return scripted decisions. A double can request a tool, hand off to a named agent, produce malformed output, wait forever, or repeat the same action. This lets tests visit rare paths without hoping a model chooses them.

Keep doubles at the model or agent boundary, not inside the orchestration logic being tested. If production parses a structured agent decision, the double should return that same structure. Validate it through the real parser. Tool doubles should simulate success, slow response, rate limiting, authorization failure, partial data, duplicated response, and corrupted payloads.

Fault injection should be named and reproducible. Use a scenario seed or explicit schedule such as research succeeds, pricing times out, pricing retry succeeds, reviewer rejects once. Record the injected event in the trace so test diagnostics do not confuse it with an application defect.

Avoid broad mocks that return a universal successful answer. They remove the exact interactions that create multi-agent risk. Prefer narrow fakes with state. A fake booking service can store idempotency keys and reveal whether two agents attempted the same reservation. A fake message broker can redeliver one event to test deduplication.

Separate three suites: protocol tests use no model, agent contract tests run one real agent with controlled tools, and workflow evaluations run the assembled system on reviewed scenarios. This test pyramid gives fast feedback without pretending that deterministic doubles measure language quality.

5. Write a Runnable Asyncio Orchestrator Test

The following Python example uses only the standard library. It tests routing, trace order, bounded execution, and terminal state. Save it as test_crew.py and run python -m unittest -v test_crew.py on Python 3.11 or newer.

import asyncio
import unittest
from dataclasses import dataclass
from typing import Protocol


@dataclass(frozen=True)
class Decision:
    kind: str
    recipient: str | None = None
    content: str = ""


class Agent(Protocol):
    async def act(self, message: str) -> Decision: ...


class ScriptedAgent:
    def __init__(self, decisions: list[Decision]):
        self.decisions = iter(decisions)

    async def act(self, message: str) -> Decision:
        await asyncio.sleep(0)
        return next(self.decisions)


async def run_crew(
    agents: dict[str, Agent], start: str, message: str, max_steps: int = 5
) -> tuple[str, list[tuple[str, Decision]]]:
    current = start
    trace: list[tuple[str, Decision]] = []

    for _ in range(max_steps):
        if current not in agents:
            raise ValueError(f"unknown agent: {current}")
        decision = await asyncio.wait_for(agents[current].act(message), timeout=0.2)
        trace.append((current, decision))

        if decision.kind == "finish":
            return decision.content, trace
        if decision.kind != "handoff" or decision.recipient is None:
            raise ValueError(f"invalid decision from {current}")

        current = decision.recipient
        message = decision.content

    raise RuntimeError("step budget exceeded")


class CrewTests(unittest.IsolatedAsyncioTestCase):
    async def test_research_is_reviewed_before_finish(self):
        agents = {
            "planner": ScriptedAgent([
                Decision("handoff", "researcher", "verify refund policy")
            ]),
            "researcher": ScriptedAgent([
                Decision("handoff", "reviewer", "policy requires approval")
            ]),
            "reviewer": ScriptedAgent([
                Decision("finish", content="Request approval before refunding.")
            ]),
        }

        answer, trace = await run_crew(agents, "planner", "Can I refund this?")

        self.assertEqual(
            [agent for agent, _ in trace],
            ["planner", "researcher", "reviewer"],
        )
        self.assertIn("approval", answer.lower())

    async def test_cycle_stops_at_budget(self):
        agents = {
            "a": ScriptedAgent([Decision("handoff", "b", "x")] * 3),
            "b": ScriptedAgent([Decision("handoff", "a", "x")] * 3),
        }
        with self.assertRaisesRegex(RuntimeError, "step budget exceeded"):
            await run_crew(agents, "a", "start", max_steps=4)


if __name__ == "__main__":
    unittest.main()

Production code will have richer events, but the test principle stays the same. Assert the path and safety invariants, not private chain-of-thought. Store concise decision reasons or evidence references designed for audit instead of requesting hidden reasoning.

6. Test Messages, Handoffs, and Context Selection

A handoff is an API call between agents. Give it a versioned schema and validate both producer and consumer. Required fields commonly include run ID, message ID, parent event ID, sender, recipient, task, selected evidence, constraints, deadline, and authorization state. Reject unknown recipients and incompatible schema versions before invoking the next model.

Test missing, blank, extra, oversized, and malicious fields. Verify that user-controlled text cannot overwrite sender, permissions, or system constraints. If agents communicate through a queue, test duplicate delivery, out-of-order messages, delayed messages, and consumer restart. Idempotency should be enforced by a durable message or action key, not by asking the model to remember.

Context selection deserves its own tests. A recipient rarely needs the entire transcript. Define mandatory context for each edge and assert its presence. Define forbidden context, such as credentials, unrelated tenant data, or internal reviewer notes, and assert its absence. Measure context size and truncation decisions.

Create metamorphic cases. Reordering irrelevant messages should not change the selected authorization. Adding a hostile sentence inside tool output should not change system permissions. Replacing a customer's name while keeping facts constant should not change the route. These relations find brittle routing without requiring one exact generated sentence.

Contract tests should run whenever an agent message schema changes. Maintain backward compatibility while old workers can still be active, or coordinate an atomic deployment. A model's flexible language handling is not a substitute for explicit protocol versioning.

7. Verify Tools, Side Effects, and Shared State

Tool use is where an agent run becomes operationally risky. Put a deterministic gateway between agents and tools. The gateway validates identity, arguments, authorization, idempotency, rate limits, and policy. Tests should prove that an agent cannot bypass this layer even if its prompt is manipulated.

Classify tools as read-only, reversible write, or irreversible write. Read-only calls can often run automatically with limits. Writes need stronger checks, previews, confirmation, or compensating actions. Irreversible actions should require a durable approval tied to exact parameters. Changing the amount or recipient invalidates approval.

For every write test, assert both positive and negative state. Confirm the intended record was created once, and confirm no extra record was created. Verify audit fields, tenant scope, and idempotency keys. Inject a timeout after the external service commits but before the agent receives the response. The retry must discover the previous success rather than repeat the action.

Shared state needs ownership and atomic update rules. Two agents may complete concurrently and attempt to update the same plan. Test both completion orders. Use compare-and-set versions, transactions, or a deterministic reducer. Last-write-wins is unsafe when one result can erase approvals or evidence.

Tool responses are untrusted. A retrieved document can contain instructions addressed to the agent. Tests should ensure those words remain data. For deeper tool-focused patterns, see testing an AI agent with LangSmith and adapt the trace assertions to your framework.

8. Exercise Concurrency, Recovery, and Termination

Multi-agent systems introduce distributed-system failures even when they run in one service. Parallel agents can race, a worker can crash after a side effect, messages can arrive twice, and a coordinator can resume from stale state. Test schedules intentionally instead of relying on random CI timing.

Use barriers or controllable futures in test doubles to pause agents at important points. Release them in different orders and assert the same valid invariant. Run the reviewer before one optional researcher finishes. Deliver the same completion event twice. Restart the coordinator between recording a tool request and receiving its result. Advance a fake clock past the deadline.

Termination is a product behavior. Define maximum steps, wall-clock deadline, per-agent timeout, tool-call budget, token or spend budget, repeated-state detector, and user cancellation. Each stop must produce a specific terminal reason. completed, cancelled, timed_out, budget_exhausted, policy_blocked, and failed are not interchangeable.

Detect loops from structured state, such as repeated agent, task, and normalized action, rather than exact generated text. A model can paraphrase the same delegation forever. Add tests where two agents repeatedly reject responsibility, one agent retries a permanent error, and a planner keeps expanding its plan.

Recovery tests should restore durable state and re-establish leases without replaying completed side effects. Verify which in-flight work is safe to retry. A trace that ends abruptly is not enough. The resumed run must link to the prior attempt and preserve the original authorization boundary.

9. Evaluate Semantic Outcomes Without Hiding Protocol Failures

After protocol tests pass, evaluate real model behavior on a versioned scenario set. Each case should include the user goal, initial state, available tools, expected outcome, required facts or actions, prohibited actions, risk, and acceptable variation. Capture the final answer, state changes, tool trace, handoffs, latency, usage, and terminal reason.

Use deterministic assertions first. Did the booking date match? Was approval obtained? Were all citations drawn from retrieved evidence? Was the protected tool never called? Then use rubric-based or reference-based evaluation for relevance, completeness, clarity, and groundedness. Human review remains necessary for high-impact samples and judge calibration.

Do not merge every signal into one average. Report a scorecard:

Signal Gate style Example release rule
authorization violation zero tolerance no critical case fails
task completion slice pass rate no material regression by slice
route validity deterministic every edge is allowed
grounded final answer calibrated rubric threshold set on reviewed examples
step efficiency distribution investigate significant path growth
recovery scenario pass all critical restart cases pass

Threshold values must come from your product risk and reviewed data, not from universal folklore. Calibrate judge prompts against independent human decisions, inspect disagreements, and version the metric. The guide to evaluating an LLM app with DeepEval metrics explains how to separate metrics by quality dimension.

10. Operate Testing Multi-Agent Systems in CI and Production

Use several feedback loops. Pull requests run deterministic component, schema, graph, and fault-injection tests. A smaller real-model canary suite detects prompt or model integration breaks. Scheduled evaluations run broader scenario and slice coverage. Preproduction exercises real tools in isolated accounts. Production monitoring checks traces, outcomes, budgets, and sampled quality.

Pin or record every relevant version: agent instructions, model identifier, parameters, tool schema, router, policy, dataset, metric, and code revision. Store sanitized traces with stable event names. Redact secrets and personal data before observability export. Trace retention and access must match data policy.

Watch distributions, not only failures. A sudden increase in reviewer rejections, tool calls per task, delegation depth, repeated routes, or budget_exhausted endings can reveal a regression before users report wrong answers. Compare like-for-like slices because workload mix changes.

When triaging, locate the earliest violated contract. The final answer may be weak because context selection dropped a constraint three handoffs earlier. Replay deterministic events and replace live agents with recorded decisions to isolate orchestration. Then rerun the responsible real agent against captured, sanitized input.

A release gate should be explainable: which cases ran, which versions were used, which hard invariants passed, what changed by slice, who reviewed exceptions, and when the exception expires. Never waive an entire suite because one nondeterministic assertion is noisy. Fix the assertion, isolate the variable, or move that signal to a calibrated evaluation lane.

Interview Questions and Answers

Q: How is testing a multi-agent system different from testing one chatbot?

A chatbot test often focuses on one input, one response, and perhaps retrieval or tools. A multi-agent test must also cover routing, typed handoffs, shared state, concurrency, termination, permissions, and recovery. I evaluate the outcome, but I also assert protocol invariants because a good final answer can hide an unsafe intermediate action.

Q: How do you make multi-agent tests deterministic?

I separate orchestration from model behavior. Scripted agents and stateful tool fakes drive exact routes, errors, and schedules for protocol tests. Real-model evaluations then measure semantic decisions on versioned scenarios. I record seeds and versions, but I do not claim that a seed makes a remote model fully deterministic.

Q: What should an agent trace contain?

It should contain stable event IDs, run and parent IDs, agent identity, message schema version, state transition, tool request and result metadata, timing, retries, terminal reason, and relevant evidence references. Sensitive content is minimized or redacted. I capture concise decision metadata intended for audit, not hidden chain-of-thought.

Q: How do you test delegation loops?

I define step and time budgets plus a repeated-state detector based on structured action, agent, and task. Tests drive two agents into repeated handoffs and verify a specific terminal reason with no further tool use. Production monitoring tracks loop-related endings and unusual path depth.

Q: How do you test concurrent agents?

I use controllable fakes or barriers to pause operations and release completions in different orders. Assertions focus on invariants, ownership, idempotency, and allowed terminal states. I also restart the coordinator at persistence boundaries to verify recovery without duplicated side effects.

Q: What metrics matter for multi-agent quality?

I use a scorecard rather than one composite number. It includes task success, hard safety violations, route validity, groundedness, required state changes, recovery, latency, tool calls, and cost or token usage. Results are segmented by scenario risk and relevant product slices.

Q: How do you test tool permissions?

Permissions are enforced by deterministic code at a tool gateway. I test allowed and denied identities, malformed arguments, tenant isolation, changed approval parameters, prompt injection in tool data, and direct bypass attempts. The agent prompt provides guidance, but it is not the security boundary.

Q: How would you debug a correct final answer with an unexpectedly high cost?

I compare the trace with a known efficient path, then inspect delegation depth, repeated state, retries, context size, and unnecessary tool calls. I identify the earliest path divergence and reproduce it with recorded inputs. Efficiency has its own regression signals even when outcome quality passes.

Common Mistakes

  • Checking only the final response. This misses unauthorized calls, leaked context, invalid routes, and duplicate writes.
  • Using live models for every branch test. Scripted decisions provide reliable protocol coverage; real models belong in semantic evaluation suites.
  • Treating prompts as access control. Enforce identity, authorization, argument validation, and approval in code.
  • Mocking every tool as success. Exercise partial failure, timeout after commit, duplicated delivery, and permanent denial.
  • Ignoring event order. Concurrency and retry defects appear only under particular schedules.
  • Asserting private reasoning. Test observable decisions, evidence, actions, and outcomes instead.
  • Having no explicit stop reason. A generic failure hides whether policy, budget, timeout, cancellation, or code ended the run.
  • Using one overall score. Critical safety failures and weak slices disappear inside an average.
  • Editing eval cases without versioning. Score trends become impossible to interpret.
  • Saving raw traces indefinitely. Observability must respect privacy, security, access, and retention requirements.

Conclusion

Testing multi-agent systems works when QA treats the product as both an AI application and a distributed workflow. Define agent and handoff contracts, assert graph invariants, drive failure paths with deterministic doubles, and evaluate real models on reviewed outcomes. Keep side effects behind enforceable permissions and make every run terminate with an explainable reason.

Start with one critical workflow. Map its graph, identify three unacceptable outcomes, write protocol tests for every edge, and add a small golden scenario set. Once the trace can explain any failure, expand coverage across concurrency, recovery, model variants, and production slices.

Interview Questions and Answers

How is testing a multi-agent system different from testing one chatbot?

A multi-agent system adds routing, handoffs, shared state, permissions, concurrency, termination, and recovery to the normal language-quality surface. I test those protocol layers independently with deterministic doubles. I then evaluate the assembled system on reviewed user outcomes.

How would you design a multi-agent test strategy?

I map the graph, message contracts, tools, state owners, and unacceptable outcomes first. Component and contract tests cover deterministic rules, fault-injection tests cover workflow paths, and real-model evaluations cover semantic decisions. Production monitoring uses the same stable events and terminal reasons.

What belongs in an agent trace?

I capture run, event, and parent IDs, agent identity, schema version, state transition, tool metadata, timing, retry, evidence references, and terminal reason. Content is minimized or redacted according to policy. I record concise auditable decisions, not private chain-of-thought.

How do you test delegation loops?

I drive scripted agents into repeated handoffs and assert bounded termination. Detection uses structured repeated state plus step, time, tool, and spend limits, since text can be paraphrased. The trace must state why the run stopped and show no later tool use.

How do you verify tool safety in a multi-agent system?

I put a deterministic gateway in front of tools and test identity, tenant, authorization, argument validation, approval binding, and idempotency. I inject timeouts after commit and duplicated events to detect repeated writes. Prompt instructions guide the agent but are not the security boundary.

How do you test race conditions between agents?

I use controllable futures or barriers to force important completion orders. Assertions focus on state ownership, atomic updates, allowed transitions, and exactly-once business effects. I also restart the coordinator at persistence boundaries to test durable recovery.

How do you evaluate a multi-agent outcome?

Each case defines the user goal, required facts and state changes, prohibited actions, tools, and risk. Deterministic checks verify hard facts and side effects, while calibrated rubrics assess open-ended quality. I report dimensions and slices separately so critical failures cannot disappear in an average.

How would you triage an expensive but correct agent run?

I compare its trace with a known efficient path and locate the first extra delegation, retry, context expansion, or tool call. Then I reproduce the path with captured inputs and controlled decisions. Efficiency is a release signal even when final-answer quality passes.

Frequently Asked Questions

What should be tested in a multi-agent system?

Test agent inputs and outputs, routing, message schemas, context selection, tool permissions, shared state, retries, concurrency, termination, recovery, and the final outcome. Also measure operational signals such as step count, latency, token use, and explicit terminal reasons.

How can multi-agent tests be made deterministic?

Replace model decisions with scripted agent doubles for orchestration tests and use stateful fakes for tools. This lets the suite force rare routes, failures, and event orders. Run separate real-model evaluations for semantic behavior.

Should QA test only the final multi-agent answer?

No. A correct final answer can hide an unauthorized tool call, leaked context, duplicate side effect, or invalid handoff. Assert observable protocol events and state changes as well as user-visible quality.

How do you test agent handoffs?

Validate a versioned message contract at both producer and consumer boundaries. Exercise missing fields, oversized context, hostile content, unknown recipients, duplicate delivery, and required or forbidden context for each edge.

How do you detect loops in a multi-agent workflow?

Use bounded steps, time, tool calls, and spend, plus a repeated-state detector based on structured agent, task, and action data. Test repeated delegation and permanent-error retries. The run should end with a specific loop or budget terminal reason.

How do you test multi-agent concurrency?

Pause controlled agent or tool operations with barriers, then release them in different orders. Verify invariants, state ownership, idempotency, and recovery after coordinator restart. Do not rely on random CI timing to expose races.

Which metrics are useful for multi-agent evaluation?

Use a scorecard covering task success, critical safety violations, route validity, groundedness, required state changes, recovery, latency, step count, tool calls, and usage. Segment results by risk and scenario slice instead of combining everything into one average.

Related Guides