Strands Agents SDK
Build AI agents in Python using the Strands SDK (Apache-2.0, from AWS).
Validated against: strands-agents==1.23.0, strands-agents-tools==0.2.19
Prerequisites
# Install SDK + tools (via pipx for isolation โ recommended)
pipx install strands-agents-builder # includes strands-agents + strands-agents-tools + CLI
# Or install directly
pip install strands-agents strands-agents-tools
Core Concept: Bedrock Is the Default
Agent() with no model= argument defaults to Amazon Bedrock โ specifically us.anthropic.claude-sonnet-4-20250514-v1:0 in us-west-2. This requires AWS credentials. To use a different provider, pass model= explicitly.
Default model constant: strands.models.bedrock.DEFAULT_BEDROCK_MODEL_ID
Quick Start โ Local Agent (Ollama)
from strands import Agent
from strands.models.ollama import OllamaModel
# host is a required positional argument
model = OllamaModel("http://localhost:11434", model_id="qwen3:latest")
agent = Agent(model=model)
result = agent("What is the capital of France?")
print(result)
Note: Not all open-source models support tool-calling. Abliterated models often lose function-calling during the abliteration process. Test with a stock model (qwen3, llama3.x, mistral) first.
Quick Start โ Bedrock (Default Provider)
from strands import Agent
# No model specified โ BedrockModel (Claude Sonnet 4, us-west-2)
# Requires AWS credentials (~/.aws/credentials or env vars)
agent = Agent()
result = agent("Explain quantum computing")
# Explicit Bedrock model:
from strands.models import BedrockModel
model = BedrockModel(model_id="us.anthropic.claude-sonnet-4-20250514-v1:0")
agent = Agent(model=model)
Quick Start โ Anthropic (Direct API)
from strands import Agent
from strands.models.anthropic import AnthropicModel
# max_tokens is Required[int] โ must be provided
model = AnthropicModel(model_id="claude-sonnet-4-20250514", max_tokens=4096)
agent = Agent(model=model)
result = agent("Explain quantum computing")
Requires ANTHROPIC_API_KEY environment variable.
Quick Start โ OpenAI
from strands import Agent
from strands.models.openai import OpenAIModel
model = OpenAIModel(model_id="gpt-4.1")
agent = Agent(model=model)
Requires OPENAI_API_KEY environment variable.
Creating Custom Tools
Use the @tool decorator. Type hints become the schema; the docstring becomes the description:
from strands import Agent, tool
@tool
def read_file(path: str) -> str:
"""Read contents of a file at the given path.
Args:
path: Filesystem path to read.
"""
with open(path) as f:
return f.read()
@tool
def write_file(path: str, content: str) -> str:
"""Write content to a file.
Args:
path: Filesystem path to write.
content: Text content to write.
"""
with open(path, 'w') as f:
f.write(content)
return f"Wrote {len(content)} bytes to {path}"
agent = Agent(model=model, tools=[read_file, write_file])
agent("Read /tmp/test.txt and summarize it")
ToolContext
Tools can access agent state via ToolContext:
from strands import tool
from strands.types.tools import ToolContext
@tool
def stateful_tool(query: str, tool_context: ToolContext) -> str:
"""A tool that accesses agent state.
Args:
query: Input query.
"""
# Access shared agent state
count = tool_context.state.get("call_count", 0) + 1
tool_context.state["call_count"] = count
return f"Call #{count}: {query}"
Built-in Tools (46 available)
strands-agents-tools provides pre-built tools:
from strands_tools import calculator, file_read, file_write, shell, http_request
agent = Agent(model=model, tools=[calculator, file_read, shell])
Full list: calculator, file_read, file_write, shell, http_request, editor, image_reader, python_repl, current_time, think, stop, sleep, environment, retrieve, search_video, chat_video, speak, generate_image, generate_image_stability, diagram, journal, memory, agent_core_memory, elasticsearch_memory, mongodb_memory, mem0_memory, rss, cron, batch, workflow, use_agent, use_llm, use_aws, use_computer, load_tool, handoff_to_user, slack, swarm, graph, a2a_client, mcp_client, exa, tavily, bright_data, nova_reels.
Hot reload: Agent(load_tools_from_directory=True) watches ./tools/ for changes.
MCP Integration
Connect to any Model Context Protocol server. MCPClient implements ToolProvider โ pass it directly in the tools list:
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters
# MCPClient takes a callable that returns the transport
mcp = MCPClient(lambda: stdio_client(StdioServerParameters(
command="uvx",
args=["some-mcp-server@latest"]
)))
# Use as context manager โ MCPClient is a ToolProvider
with mcp:
agent = Agent(model=model, tools=[mcp])
agent("Use the MCP tools to do something")
SSE transport:
from mcp.client.sse import sse_client
mcp = MCPClient(lambda: sse_client("http://localhost:8080/sse"))
Multi-Agent Patterns
Agents as Tools
Nest agents โ inner agents become tools for the outer agent:
researcher = Agent(model=model, system_prompt="You are a research assistant.")
writer = Agent(model=model, system_prompt="You are a writer.")
orchestrator = Agent(
model=model,
tools=[researcher, writer],
system_prompt="You coordinate research and writing tasks."
)
orchestrator("Research quantum computing and write a blog post")
Swarm Pattern
Self-organizing agent teams with shared context and autonomous handoff coordination:
from strands.multiagent.swarm import Swarm
# Agents need name + description for handoff identification
researcher = Agent(
model=model,
name="researcher",
description="Finds and summarizes information"
)
writer = Agent(
model=model,
name="writer",
description="Creates polished content"
)
swarm = Swarm(
nodes=[researcher, writer],
entry_point=researcher, # optional โ defaults to first agent
max_handoffs=20, # default
max_iterations=20, # default
execution_timeout=900.0, # 15 min default
node_timeout=300.0 # 5 min per node default
)
result = swarm("Research AI agents, then hand off to writer for a blog post")
Swarm auto-injects a handoff_to_agent tool. Agents hand off by calling it with the target agent's name. Supports interrupt/resume, session persistence, and repetitive-handoff detection.
Graph Pattern (DAG)
Deterministic dependency-based execution via GraphBuilder:
from strands.multiagent.graph import GraphBuilder
builder = GraphBuilder()
research_node = builder.add_node(researcher, node_id="research")
writing_node = builder.add_node(writer, node_id="writing")
builder.add_edge("research", "writing")
builder.set_entry_point("research")
# Optional: conditional edges
# builder.add_edge("research", "writing",
# condition=lambda state: "complete" in str(state.completed_nodes))
graph = builder.build()
result = graph("Write a blog post about AI agents")
Supports cycles (feedback loops) with builder.reset_on_revisit(True), execution timeouts, and nested graphs (Graph as a node in another Graph).
A2A Protocol (Agent-to-Agent)
Expose a Strands agent as an A2A-compatible server for inter-agent communication:
from strands.multiagent.a2a import A2AServer
server = A2AServer(
agent=my_agent,
host="127.0.0.1",
port=9000,
version="0.0.1"
)
server.start() # runs uvicorn
Connect to A2A agents with the a2a_client tool from strands-agents-tools. A2A implements Google's Agent-to-Agent protocol for standardized cross-process/cross-network agent communication.
Session Persistence
Persist conversations across agent runs:
from strands.session.file_session_manager import FileSessionManager
session = FileSessionManager(session_file_path="./sessions/my_session.json")
agent = Agent(model=model, session_manager=session)
# Also available:
from strands.session.s3_session_manager import S3SessionManager
session = S3SessionManager(bucket_name="my-bucket", session_id="session-1")
Both Swarm and Graph support session managers for persisting multi-agent state.
Bidirectional Streaming (Experimental)
Real-time voice/text conversations with persistent audio streams:
from strands.experimental.bidi.agent import BidiAgent
from strands.experimental.bidi.models.nova_sonic import NovaSonicModel
# Supports: NovaSonicModel, GeminiLiveModel, OpenAIRealtimeModel
model = NovaSonicModel(region="us-east-1")
agent = BidiAgent(model=model, tools=[my_tool])
Supports interruption detection, concurrent tool execution, and continuous back-and-forth audio. Experimental โ API subject to change.
System Prompts
agent = Agent(
model=model,
system_prompt="You are Hex, a sharp and witty AI assistant.",
tools=[read_file, write_file]
)
Strands also supports list[SystemContentBlock] for structured system prompts with cache control.
Observability
Native OpenTelemetry tracing:
agent = Agent(
model=model,
trace_attributes={"project": "my-agent", "environment": "dev"}
)
Every tool call, model invocation, handoff, and lifecycle event is instrumentable.
Bedrock-Specific Features
- Guardrails:
guardrail_id+guardrail_versionin BedrockModel config โ content filtering, PII detection, input/output redaction - Cache points: System prompt and tool definition caching for cost optimization
- Streaming: On by default, disable with
streaming=False - Region: Defaults to
us-west-2, override viaregion_nameparam orAWS_REGIONenv - Cross-region inference: Model IDs prefixed with
us.use cross-region inference profiles
Scaffolding a New Agent
python3 {baseDir}/scripts/create-agent.py my-agent --provider ollama --model qwen3:latest
python3 {baseDir}/scripts/create-agent.py my-agent --provider anthropic
python3 {baseDir}/scripts/create-agent.py my-agent --provider bedrock
python3 {baseDir}/scripts/create-agent.py my-agent --provider openai --model gpt-4.1
Creates a ready-to-run agent directory with tools, config, and entry point.
Running an Agent
python3 {baseDir}/scripts/run-agent.py path/to/agent.py "Your prompt here"
python3 {baseDir}/scripts/run-agent.py path/to/agent.py --interactive
Model Providers Reference (11 total)
| Provider | Class | Init | Notes |
|---|---|---|---|
| Bedrock | BedrockModel |
BedrockModel(model_id=...) |
Default, eagerly imported |
| Ollama | OllamaModel |
OllamaModel("http://host:11434", model_id=...) |
host is positional |
| Anthropic | AnthropicModel |
AnthropicModel(model_id=..., max_tokens=4096) |
max_tokens required |
| OpenAI | OpenAIModel |
OpenAIModel(model_id=...) |
OPENAI_API_KEY |
| Gemini | GeminiModel |
GeminiModel(model_id=...) |
api_key in client_args |
| Mistral | MistralModel |
MistralModel(model_id=...) |
Mistral API key |
| LiteLLM | LiteLLMModel |
LiteLLMModel(model_id=...) |
Meta-provider (Cohere, Groq, etc.) |
| LlamaAPI | LlamaAPIModel |
LlamaAPIModel(model_id=...) |
Meta Llama API |
| llama.cpp | LlamaCppModel |
LlamaCppModel(...) |
Local server, OpenAI-compatible |
| SageMaker | SageMakerAIModel |
SageMakerAIModel(...) |
Custom AWS endpoints |
| Writer | WriterModel |
WriterModel(model_id=...) |
Writer platform |
All non-Bedrock providers are lazy-loaded โ dependencies imported only when referenced.
Import pattern: from strands.models.<provider> import <Class> (or from strands.models import <Class> for lazy-load).
Tips
Agent()withoutmodel=requires AWS credentials (Bedrock default)AnthropicModelrequiresmax_tokensโ omitting it causes a runtime errorOllamaModelhostis positional:OllamaModel("http://...", model_id="...")- Abliterated Ollama models often lose tool-calling support โ use stock models for tool-using agents
- Swarm agents need
name=anddescription=for handoff routing Agent(load_tools_from_directory=True)watches./tools/for hot-reloaded tool files- Use
agent.tool.my_tool()to call tools directly without LLM routing MCPClientis aToolProviderโ pass it directly intools=[mcp], don't calllist_tools_sync()manually when using with Agent- Session managers work with Agent, Swarm, and Graph
- Pin your
strands-agentsversion โ the SDK is young and APIs evolve between releases