Amazon Bedrock AgentCore multi-agent orchestration with Agent-to-Agent (A2A) protocol. Supervisor-worker patterns, agent collaboration, and hierarchical delegation. Use when building multi-agent systems, orchestrating specialized agents, or implementing complex workflows.
/plugin marketplace add adaptationio/Skrillz/plugin install skrillz@skrillzThis skill is limited to using the following tools:
Build sophisticated multi-agent systems using AgentCore's Agent-to-Agent (A2A) protocol. Implement supervisor-worker patterns where a routing agent delegates to specialized domain experts, maintaining context across the agent hierarchy.
Purpose: Orchestrate multiple AI agents for complex, multi-domain tasks
Pattern: Workflow-based (3 orchestration patterns)
Key Principles (validated by AWS December 2025):
Quality Targets:
Use bedrock-agentcore-multi-agent when:
When NOT to Use:
┌─────────────────┐
│ User Query │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Supervisor │
│ (Router) │
└────────┬────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Orders │ │ Returns │ │ Support │
│ Agent │ │ Agent │ │ Agent │
└─────────────┘ └─────────────┘ └─────────────┘
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Intake │ ──▶ │ Analyze │ ──▶ │ Resolve │ ──▶ │ Close │
│ Agent │ │ Agent │ │ Agent │ │ Agent │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
┌─────────────────┐
│ Executive │
│ Supervisor │
└────────┬────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Sales │ │ Support │ │ Billing │
│ Supervisor │ │ Supervisor │ │ Supervisor │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
│B2B│ │B2C│ │L1 │ │L2 │ │Pay│ │Inv│
└───┘ └───┘ └───┘ └───┘ └───┘ └───┘
Time: 15-30 minutes Automation: 80% Purpose: Build the routing/orchestration agent
Supervisor Agent Code:
# supervisor_agent.py
from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent
import boto3
import json
app = BedrockAgentCoreApp()
client = boto3.client('bedrock-agentcore')
# Define collaborator agents
COLLABORATORS = {
'orders': {
'arn': 'arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/orders-agent',
'description': 'Handles order status, tracking, and modifications',
'triggers': ['order', 'tracking', 'delivery', 'shipment', 'status']
},
'returns': {
'arn': 'arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/returns-agent',
'description': 'Processes returns, refunds, and exchanges',
'triggers': ['return', 'refund', 'exchange', 'damaged', 'wrong item']
},
'support': {
'arn': 'arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/support-agent',
'description': 'Technical support and product questions',
'triggers': ['help', 'problem', 'issue', 'how to', 'broken', 'not working']
},
'billing': {
'arn': 'arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/billing-agent',
'description': 'Payment issues, invoices, and account billing',
'triggers': ['payment', 'charge', 'invoice', 'bill', 'subscription']
}
}
# Routing agent
router = Agent(
model="anthropic.claude-sonnet-4-20250514-v1:0",
system_prompt=f"""You are a customer service supervisor. Your job is to:
1. Understand the customer's intent
2. Route to the appropriate specialist
3. Handle escalations and complex multi-domain issues
Available specialists:
{json.dumps({k: v['description'] for k, v in COLLABORATORS.items()}, indent=2)}
Respond with JSON:
{{"route": "orders|returns|support|billing|self", "reason": "...", "context": "..."}}
Use "self" only for greetings or if truly unclear.
"""
)
@app.entrypoint
def invoke(payload):
user_message = payload.get('prompt', '')
session_id = payload.get('session_id', 'default')
conversation_history = payload.get('history', [])
# Step 1: Route the request
routing_prompt = f"""
Customer message: {user_message}
Previous context: {json.dumps(conversation_history[-3:]) if conversation_history else 'None'}
Determine the appropriate specialist and respond with routing JSON.
"""
routing_response = router(routing_prompt)
try:
routing = json.loads(routing_response.message)
except json.JSONDecodeError:
# Fallback to keyword matching
routing = keyword_route(user_message)
route = routing.get('route', 'self')
# Step 2: Handle routing
if route == 'self':
# Handle directly
return {
'response': "Hello! I'm here to help. What can I assist you with today?",
'routed_to': None
}
# Step 3: Delegate to specialist
collaborator = COLLABORATORS.get(route)
if not collaborator:
return {'response': "I apologize, let me connect you with support.", 'routed_to': 'support'}
# Invoke collaborator with context
try:
response = client.invoke_agent_runtime(
agentRuntimeArn=collaborator['arn'],
runtimeSessionId=f"{session_id}-{route}",
payload={
'prompt': user_message,
'context': routing.get('context', ''),
'supervisor_notes': routing.get('reason', ''),
'history': conversation_history
}
)
return {
'response': response['payload'].get('response', ''),
'routed_to': route,
'routing_reason': routing.get('reason', '')
}
except Exception as e:
# Fallback: handle ourselves or escalate
app.logger.error(f"Collaborator failed: {e}")
return {
'response': "I apologize for the inconvenience. Let me help you directly.",
'routed_to': None,
'error': str(e)
}
def keyword_route(message):
"""Fallback keyword-based routing"""
message_lower = message.lower()
for route, config in COLLABORATORS.items():
if any(trigger in message_lower for trigger in config['triggers']):
return {'route': route, 'reason': 'keyword_match'}
return {'route': 'self', 'reason': 'no_match'}
if __name__ == "__main__":
app.run()
Time: 10-20 minutes per agent Automation: 85% Purpose: Build domain-specific worker agents
Orders Agent:
# orders_agent.py
from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent
app = BedrockAgentCoreApp()
# Orders specialist
agent = Agent(
model="anthropic.claude-sonnet-4-20250514-v1:0",
system_prompt="""You are an orders specialist. You handle:
- Order status inquiries
- Delivery tracking
- Order modifications
- Shipping questions
You have access to these tools:
- get_order_status(order_id) - Get order details
- track_shipment(tracking_number) - Track delivery
- modify_order(order_id, changes) - Update order
Be helpful, accurate, and efficient. If a request is outside your domain
(returns, billing, technical support), indicate that clearly.
"""
)
@app.entrypoint
def invoke(payload):
user_message = payload.get('prompt', '')
context = payload.get('context', '')
supervisor_notes = payload.get('supervisor_notes', '')
history = payload.get('history', [])
# Build context-aware prompt
prompt = user_message
if context:
prompt = f"Context from supervisor: {context}\n\nCustomer request: {user_message}"
result = agent(prompt)
# Check if we need to hand back to supervisor
needs_handoff = check_domain_boundary(result.message)
return {
'response': result.message,
'needs_handoff': needs_handoff,
'handoff_reason': needs_handoff if needs_handoff else None
}
def check_domain_boundary(response):
"""Check if response indicates need for different specialist"""
handoff_indicators = [
('return', 'returns'),
('refund', 'returns'),
('billing', 'billing'),
('payment', 'billing'),
('technical', 'support')
]
response_lower = response.lower()
for indicator, domain in handoff_indicators:
if f"need to contact {indicator}" in response_lower or \
f"transfer to {indicator}" in response_lower:
return domain
return None
if __name__ == "__main__":
app.run()
Returns Agent:
# returns_agent.py
from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent
app = BedrockAgentCoreApp()
agent = Agent(
model="anthropic.claude-3-haiku-20240307-v1:0", # Faster model for simple tasks
system_prompt="""You are a returns specialist. You handle:
- Return requests
- Refund processing
- Exchanges
- Damaged item claims
Tools available:
- initiate_return(order_id, reason) - Start return process
- check_return_eligibility(order_id) - Verify return policy
- process_refund(order_id) - Issue refund
- create_exchange(order_id, new_item) - Process exchange
Be empathetic and solution-oriented. Follow return policy strictly.
"""
)
@app.entrypoint
def invoke(payload):
result = agent(payload.get('prompt', ''))
return {'response': result.message}
if __name__ == "__main__":
app.run()
Time: 10-15 minutes Automation: 90% Purpose: Enable secure agent-to-agent communication
Enable A2A for Agents:
import boto3
control = boto3.client('bedrock-agentcore-control')
# Configure supervisor to call collaborators
response = control.update_agent_runtime(
agentRuntimeId='supervisor-agent',
collaborationConfig={
'enabled': True,
'collaborators': [
{
'agentRuntimeArn': 'arn:...:agent-runtime/orders-agent',
'alias': 'orders',
'description': 'Order management specialist'
},
{
'agentRuntimeArn': 'arn:...:agent-runtime/returns-agent',
'alias': 'returns',
'description': 'Returns and refunds specialist'
},
{
'agentRuntimeArn': 'arn:...:agent-runtime/support-agent',
'alias': 'support',
'description': 'Technical support specialist'
},
{
'agentRuntimeArn': 'arn:...:agent-runtime/billing-agent',
'alias': 'billing',
'description': 'Billing and payments specialist'
}
],
'routingMode': 'SUPERVISOR_WITH_ROUTING',
'contextSharing': {
'shareConversationHistory': True,
'maxHistoryTurns': 10
}
}
)
IAM Policy for A2A:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "bedrock-agentcore:InvokeAgentRuntime",
"Resource": [
"arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/orders-agent",
"arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/returns-agent",
"arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/support-agent",
"arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/billing-agent"
],
"Condition": {
"StringEquals": {
"bedrock-agentcore:CallerAgentRuntimeArn": "arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/supervisor-agent"
}
}
}
]
}
Time: 15-30 minutes Automation: 70% Purpose: Handle smooth transitions between agents
Context-Preserving Handoff:
class AgentHandoff:
"""Manages handoffs between agents"""
def __init__(self, client, supervisor_arn):
self.client = client
self.supervisor_arn = supervisor_arn
self.context_store = {}
def initiate_handoff(self, from_agent, to_agent, session_id, context):
"""Hand off from one agent to another"""
# Store context for receiving agent
handoff_context = {
'from_agent': from_agent,
'reason': context.get('reason', ''),
'summary': context.get('summary', ''),
'customer_sentiment': context.get('sentiment', 'neutral'),
'conversation_history': context.get('history', []),
'pending_actions': context.get('pending_actions', [])
}
self.context_store[session_id] = handoff_context
# Notify receiving agent
response = self.client.invoke_agent_runtime(
agentRuntimeArn=to_agent,
runtimeSessionId=session_id,
payload={
'type': 'HANDOFF_RECEIVE',
'context': handoff_context,
'prompt': f"Customer transferred from {from_agent}. Context: {handoff_context['summary']}"
}
)
return response
def escalate_to_supervisor(self, agent_arn, session_id, reason):
"""Escalate back to supervisor"""
return self.client.invoke_agent_runtime(
agentRuntimeArn=self.supervisor_arn,
runtimeSessionId=session_id,
payload={
'type': 'ESCALATION',
'from_agent': agent_arn,
'reason': reason,
'needs_human': reason.get('needs_human', False)
}
)
Multi-Turn Handoff Example:
# Conversation flow with handoffs
# Turn 1: User -> Supervisor
# "I want to return an order and also change my payment method"
# Supervisor detects multi-domain request
# Routes to returns first (primary intent)
# Turn 2: Returns Agent handles return
# Initiates return process
# Turn 3: Returns Agent hands off to Billing
# With context: "Return initiated for order #123, customer also needs payment update"
# Turn 4: Billing Agent receives handoff
# "I see you've started a return. Let me help you update your payment method."
# Handles payment update
# Turn 5: Billing -> Supervisor
# "Both requests handled. Return initiated, payment updated."
Time: 15-20 minutes Automation: 85% Purpose: Monitor the multi-agent system
Trace Multi-Agent Calls:
import boto3
import json
cloudwatch = boto3.client('cloudwatch')
logs = boto3.client('logs')
# CloudWatch Metrics for Multi-Agent
cloudwatch.put_metric_data(
Namespace='AgentCore/MultiAgent',
MetricData=[
{
'MetricName': 'RoutingDecisions',
'Dimensions': [
{'Name': 'SupervisorAgent', 'Value': 'customer-service-supervisor'},
{'Name': 'TargetAgent', 'Value': 'orders-agent'}
],
'Value': 1,
'Unit': 'Count'
},
{
'MetricName': 'HandoffLatency',
'Dimensions': [
{'Name': 'FromAgent', 'Value': 'returns-agent'},
{'Name': 'ToAgent', 'Value': 'billing-agent'}
],
'Value': 150, # milliseconds
'Unit': 'Milliseconds'
}
]
)
# Log Insights Query for Multi-Agent Traces
query = '''
fields @timestamp, @message
| filter @message like /agent-runtime/
| parse @message '"agentRuntimeArn":"*"' as agent
| parse @message '"runtimeSessionId":"*"' as session
| stats count() by agent, session
| sort count desc
'''
Dashboard for Multi-Agent System:
dashboard_body = {
"widgets": [
{
"type": "metric",
"properties": {
"title": "Routing Distribution",
"metrics": [
["AgentCore/MultiAgent", "RoutingDecisions", "TargetAgent", "orders-agent"],
[".", ".", ".", "returns-agent"],
[".", ".", ".", "support-agent"],
[".", ".", ".", "billing-agent"]
],
"period": 3600,
"stat": "Sum",
"view": "pie"
}
},
{
"type": "metric",
"properties": {
"title": "Handoff Latency",
"metrics": [
["AgentCore/MultiAgent", "HandoffLatency"]
],
"period": 300,
"stat": "p99"
}
},
{
"type": "metric",
"properties": {
"title": "Escalation Rate",
"metrics": [
["AgentCore/MultiAgent", "Escalations", "Reason", "out_of_scope"],
[".", ".", ".", "customer_request"],
[".", ".", ".", "agent_failure"]
],
"period": 3600,
"stat": "Sum"
}
}
]
}
cloudwatch.put_dashboard(
DashboardName='MultiAgentOrchestration',
DashboardBody=json.dumps(dashboard_body)
)
# Good: Clear separation
DOMAINS = {
'orders': ['status', 'tracking', 'modify', 'cancel'],
'returns': ['return', 'refund', 'exchange', 'damaged'],
'billing': ['payment', 'invoice', 'subscription']
}
# Bad: Overlapping domains
# 'orders': ['status', 'refund'] # Refund overlaps with returns
async def invoke_with_fallback(primary_agent, fallback_agent, payload):
"""Try primary, fall back to backup"""
try:
return await invoke_agent(primary_agent, payload)
except Exception:
return await invoke_agent(fallback_agent, payload)
def compress_history(history, max_turns=10):
"""Keep relevant context, compress old turns"""
if len(history) <= max_turns:
return history
# Keep first turn (initial context) and recent turns
return [history[0]] + history[-(max_turns-1):]
references/routing-strategies.md - Advanced routing patternsreferences/context-management.md - Cross-agent context handlingreferences/failure-handling.md - Error recovery patternsActivates when the user asks about Agent Skills, wants to find reusable AI capabilities, needs to install skills, or mentions skills for Claude. Use for discovering, retrieving, and installing skills.
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.