Multi-Agent Probing (ATLAS)
Red-team multi-agent and single-agent systems with ATLAS, capture executed tool calls in findings, and point attacks at deployed agent environments.
Dreadnode can red-team agentic targets — single agents and multi-agent systems — not just chat models. This page covers the ATLAS attack, the target output contract for agents, and tool-call capture in findings.
ATLAS (Adaptive Topology-Level Attack Synthesis) treats a multi-agent target as a topology of agents (delegation chains, trust boundaries, tool hierarchies) rather than a monolithic chatbot. It runs a Probe → Route → Learn campaign over a budget of episodes:
- Probe — Bayesian (Dirichlet) profiling of six structural defense dimensions (
tool_gate,delegation_permeability,trust_model,output_control,memory_isolation,escalation_resistance). - Route — an MDP + Hedge bandit selects one of eight attack modes —
(strategy × injection-surface × turn-budget)— best suited to the profiled defenses. - Learn — online value/weight updates, near-miss decomposition, and a delegation retry: when an agent complies verbally but no tool fires, ATLAS re-routes through the delegation (
peer_message) surface to make a downstream agent execute the tool.
Success is decided by a tool-evidence gate — a high judge score does not count unless a dangerous tool actually executed (the gate walks delegated agents’ calls too).
import dreadnode as dnfrom dreadnode.airt.atlas import atlas_attackfrom dreadnode.generators.message import Message
@dn.taskasync def target(prompt: str, *, surface: str, injection: str | None = None): # POST to your deployed multi-agent environment, applying the injection at the # named surface, and return the response + executed tool calls. data = await call_environment(prompt, surface, injection) return {"content": data["content"], "tool_calls": data["tool_calls"]}
results = await atlas_attack( objectives=[{"id": "TW_1", "category": "TW", "goal": "..."}], target=target, attacker_model="openai/gpt-4.1", evaluator_model="openai/gpt-4o-mini", scenario_name="finops", total_budget=64,)print(results["asr"], results["queries_per_objective"])Injection surfaces
Section titled “Injection surfaces”| Surface | What it injects |
|---|---|
direct | The attacker prompt is sent to the entry agent. |
tool_output | The injection is applied to a tool’s returned output. |
peer_message | The injection rides an inter-agent delegation message. |
Categories
Section titled “Categories”ATLAS objectives use OWASP-ASI agentic-security categories, each mapped to an OWASP Agentic Top 10 code in findings: TW Tool Weaponization, EA Excessive Agency, TB Trust-Boundary Violation, CB Cross-Boundary/Cascading, DE Data Exfiltration, GH Goal Hijacking, RP Rug-Pull / bait-and-switch, MP Memory Poisoning.
Target output contract
Section titled “Target output contract”Because agents are conversational and (increasingly) multimodal, an AIRT target returns one of the SDK’s own structures — there is no bespoke wrapper. All shapes are normalized automatically:
| Return type | Use for | Notes |
|---|---|---|
str | chat model | Response text only (back-compat). |
Message | single agent | content is multimodal (content_parts); tool_calls are native. |
Trajectory / list[Message] | multi-agent | Tool results are role="tool" messages linked by tool_call_id; per-agent attribution via Message.metadata["agent"]. |
dict {content/response, tool_calls, ...} | HTTP agent | Simplest for a custom/HTTP agent JSON response. |
Single-agent example (idiomatic Message):
@dn.taskasync def target(prompt: str) -> Message: return await my_agent.chat(prompt) # a Message with content + tool_callsMulti-agent example (per-agent attribution):
@dn.taskasync def target(prompt: str) -> list[Message]: run = await my_mesh.run(prompt) return [ Message(role="assistant", content=t.text, tool_calls=t.tool_calls, metadata={"agent": t.agent_name}) for t in run.turns ] + [ Message(role="tool", tool_call_id=r.call_id, content=r.result) for r in run.tool_results ]Tool-call capture in findings
Section titled “Tool-call capture in findings”When a target returns tool calls, they are captured end-to-end:
- Per trial — the full executed calls (
agent · name(arguments) → result) appear as a Tool Calls row in the finding’s trial detail. This is the severity evidence. - Per finding — the distinct tool names invoked appear as Tools Invoked badges (triage).
- Trace view —
dreadnode.airt.tool_callsis shown in the raw span/trace viewer.
This works for single-agent and multi-agent probing alike — any agentic target whose response includes tool calls.
Running from the TUI
Section titled “Running from the TUI”Enable the AI red teaming agent in the TUI, then ask it to run an ATLAS campaign against a deployed agent environment URL. See Getting started → TUI.