DEV Community

Mindy Jen
Mindy Jen

Posted on

From Zero to Agent (Part-I)

Build production-ready agents via Strands SDK, an open-source framework on AWS

The Strands SDK is deceptively simple on the surface: you initialize an Agent, give it tools, and communicate with it. But the depth underneath, covering model flexibility, tool abstraction, MCP integration, guardrails, memory, and multi-agent orchestration, makes it one of the most complete agentic frameworks I've worked with on AWS.

Step 1: Your First Strands Agent on Cloud

In just a few lines of Python you can stand up an agent backed by Claude Sonnet 4 on Amazon Bedrock:

from strands import Agent
agent = Agent(
    system_prompt="You are a helpful assistant that provides concise responses."
)
agent("Tell me a joke.")
Enter fullscreen mode Exit fullscreen mode

No infrastructure to provision. No API gateway to configure. The default model provider is Amazon Bedrock, and the default model is Claude Sonnet 4 in your current AWS region. From there, you start adding tools, both built-in ones from the strands-agents-tools package (like calculator) and custom ones decorated with @tool. You also learn how to invoke tools directly from code (agent.tool.calculator(...)), configure logging, and swap model providers or adjust inference parameters via BedrockModel.

Below is an example showing you how to build a RecipeBot, a cooking assistant that uses DuckDuckGo web search as a tool, all wired up in under 50 lines of code.


import warnings
warnings.filterwarnings(action="ignore", message=r"datetime.datetime.utcnow") 

from strands import Agent
# Initialize your agent
agent = Agent(
    model="us.anthropic.claude-sonnet-4-5-20250929-v1:0",  # Optional: Specify the model ID
    system_prompt="You are a helpful assistant that provides concise responses."
)

# Send a message to the agent
response = agent("Hello! Tell me a joke.")
Enter fullscreen mode Exit fullscreen mode

Key Takeaway: The Strands Agent abstracts away the entire request-response-tool-loop. You think in terms of what the agent should do, not how to wire API calls. Three lines are all Strands takes to have a working agent on Bedrock.

Step 2: Personal Agent Assistant on Local Machine

Not every use case can or should hit a cloud endpoint. This step shows you how to swap out Amazon Bedrock for Ollama, the local LLM runtime, using Strands’ OllamaModel provider. The agent runs entirely on your machine without having any data leave your local environment.


from strands.models.ollama import OllamaModel
ollama_model = OllamaModel(
    model_id="llama3.2:3b",
    host="http://localhost:11434",
    temperature=0.7,
)
agent = Agent(model=ollama_model, tools=[file_read, file_write, list_directory])
Enter fullscreen mode Exit fullscreen mode

The use case is a local file-operations assistant: read files (including PDFs), write files, list directory contents. The agent can summarize a shareholder letter, scaffold a README, or create files on your behalf, all powered by a 3B-parameter model running in your own process.

Key Takeaway: The Strands model provider abstraction is genuinely portable. Switching from Claude on Bedrock to Llama on Ollama is a one-line change. This matters for offline scenarios, privacy-sensitive workloads, and cost-optimized local development.

Step 3: Connecting Agents to AWS Services

This is where things get practically useful. This step builds a Restaurant Assistant that connects to two AWS managed services: an Amazon Bedrock Knowledge Base for RAG over restaurant menus, and an Amazon DynamoDB table for reservation management.

The step demonstrates three distinct ways to define tools:

  • Inline with @tool decorator - define the function right next to your agent code
  • Standalone file with @tool decorator - import it as a module
  • TOOL_SPEC dictionary — Bedrock Converse API-style schema definition, useful when you need fine-grained control over required/optional parameters and error vs. success return shapes
from strands_tools import current_time, retrieve  # built-in tools
from strands import Agent
agent = Agent(
    model=model,
    system_prompt=system_prompt,
    tools=[retrieve, current_time, get_booking_details, create_booking, delete_booking],
)
Enter fullscreen mode Exit fullscreen mode

The retrieve built-in tool reads from your Bedrock Knowledge Base automatically when you set the KNOWLEDGE_BASE_ID environment variable, no custom RAG plumbing needed.

Key Takeaway: Strands agents can be connected to any AWS service through custom tools and boto3 calls. The retrieve built-in tool gives you RAG in one line. The TOOL_SPEC pattern gives you schema-level control for production tool definitions.

Step 4: MCP Tools and Multiple Servers

The Model Context Protocol (MCP) is the emerging standard for connecting AI agents to external tool servers. Here shows how Strands natively integrates with MCP servers, both via stdio for local CLI-style servers and Streamable HTTP for networked, stateless servers.

from strands.tools.mcp import MCPClient
from mcp import StdioServerParameters, stdio_client
mcp_client = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"])
    )
)
with mcp_client:
    tools = mcp_client.list_tools_sync()
    agent = Agent(tools=tools)
    agent("What is Amazon Bedrock's pricing model?")
Enter fullscreen mode Exit fullscreen mode

This step also covers creating your own MCP server with FastMCP, direct tool invocation via call_tool_sync, timeout configuration with read_timeout_seconds, and connecting multiple MCP servers to a single agent simultaneously.

Key Takeaway: MCP turns the tool ecosystem into a plug-and-play marketplace. Any MCP-compatible server, whether AWS documentation, CDK best practices, databases, or APIs, becomes a tool your agent can use without any glue code. You can combine multiple servers in a single agent call.

Step 5: Advanced Response Processing, Streaming and Callbacks

Real applications don’t just call an agent and wait. They stream tokens, render partial results, pipe events to dashboards, and serve responses over HTTP. This step demonstrates two patterns for this:

  • Method 1Async Iterators (stream_async): Ideal for async frameworks like FastAPI. You iterate over agent events as they arrive:
async for event in agent.stream_async("Calculate 2+2"):
    if "data" in event:
        print(event["data"])
    if "current_tool_use" in event:
        print(f"Tool: {event['current_tool_use']['name']}")
Enter fullscreen mode Exit fullscreen mode

Events carry structured lifecycle signals: init_event_loop, start_event_loop, message, current_tool_use, data, force_stop, and result. This gives you fine-grained control to build streaming UIs, progress indicators, or custom logging.

  • Method 2Callback Handlers: For synchronous contexts, pass a callback_handler function to the agent. It fires on every event, letting you intercept model output and tool calls in real time without restructuring your code around async.

Key Takeaway: stream_async paired with FastAPI is the production pattern for streaming agent APIs. Callback handlers are the lightweight alternative for scripts, CLIs, or any synchronous environment. Both give you visibility into every step of the agent's reasoning and tool usage.

(To Be Cont'd)

Top comments (0)