Building AI Agents: From Local Prototypes to Cloud-Scale Orchestration
Introduction
Welcome to my journey through the April 2026 Builders Skill Sprint! In this sprint, I explored the world of AI agents using the Strands SDK, starting with lightweight, offline local models and culminating in a highly capable, tool-equipped, streaming agent in the cloud powered by Amazon Bedrock (Nova Pro).
Here is a look behind the scenes at what I built, how it works under the hood, and the key lessons I learned along the way.
The Progression: Challenges 1 to 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
graph TD
subgraph "Local Stack"
C1["Challenge 1: First Agent<br/>• Ollama & Llama 3.2<br/>• 100% Offline Chat"]
end
subgraph "Cloud & Tool Stack (Bedrock)"
C2["Challenge 2: Add Tools<br/>• wttr.in Weather API<br/>• Python datetime Age Calc<br/>• Built-in Calculator"]
end
subgraph "Stateful & Memory Stack"
C3["Challenge 3: Persistent Memory<br/>• FAISS Local Vector Store<br/>• mem0 Persistent Memories"]
end
subgraph "Full Orchestration"
C4["Challenge 4: Unified Agent<br/>• Tools + Memory + Streaming<br/>• Token-by-Token Callback"]
end
C1 --> C2
C2 --> C3
C3 --> C4📦 Challenge 1: Your First AI Agent (Local & Offline)
What Was Done
I created my first AI agent using the Strands SDK connected to Ollama running a local instance of
llama3.2:3b on my Mac.How It Works
The script initializes an
OllamaModel directing requests to localhost:11434 (the default Ollama host). It configures a basic Agent with a system prompt and no tools:1
2
3
4
5
6
7
8
ollama_model = OllamaModel(
model_id="llama3.2:3b"
)
agent = Agent(
model=ollama_model,
tools=[],
system_prompt="You are a helpful assistant..."
)When invoked, the agent processes the user's prompt locally and responds fully offline.
What I Learned
- Local-first flexibility: Local models are incredibly powerful for offline prototyping, development iterations, and data privacy.
- Bootstrapping agents: Understood the baseline anatomy of a Strands SDK agent.
Challenge 2: Adding Tools (AWS Bedrock & Nova Pro)
What Was Done
I graduated the agent to the cloud, powering it with Amazon Nova Pro via Bedrock, and equipped it with three distinct tools:
- A built-in mathematical calculator tool.
- A custom weather tool fetching current temperatures and conditions.
- A custom age calculator tool computing exact ages from dates.
How It Works
Using the
@tool decorator, I wrote Python functions that are exposed as tools to the LLM:- Weather: Fetches real-time weather details from the
wttr.inJSON API (endpoint:wttr.in/{city}?format=j1) using therequestslibrary. - Age Calculator: Parses birthdates in
YYYY-MM-DDformat and compares them dynamically todate.today()to return precise ages. - Tool Routing: When the user asks a question, the LLM reads the tool docstrings, reasons about which tool is appropriate, issues a tool request, runs the local Python function, and summarizes the results.
What I Learned
- Docstrings as APIs: The agent relies on clean, descriptive function docstrings and type annotations to understand when and how to call tools.
- Decoupled execution: The agent doesn't write code to run calculations or call endpoints; it outputs structured JSON indicating which tool to execute, and the SDK manages the execution loop locally.
Challenge 3: Persistent Memory (FAISS + Mem0)
What Was Done
I added a persistent memory layer to the agent using the Mem0 library backed by a local FAISS (Facebook AI Similarity Search) vector database.
How It Works
Unlike standard conversation history (which forgets everything once the terminal session restarts), persistent memory converts user facts into vector embeddings (using
amazon.titan-embed-text-v2:0) and saves them locally in a FAISS index:- When the user says "Remember that my name is Ravi and I love biryani", the agent uses
mem0_memoryto store this association. - If the program is closed and restarted, the memory remains. When queried "What is my name?", the agent retrieves the nearest neighbors from the FAISS database to answer.
🐛 Debugging the Internals (Bonus Learning!)
During implementation, I hit a complex compatibility issue. The
mem0 library's AWS Bedrock provider had a bug when interacting with the Bedrock Converse API:- It formatted user/assistant message contents as raw strings, but Bedrock's Converse API strictly requires content blocks
[{"text": "..."}]. - It passed system prompts inside the
messagespayload, whereas Bedrock Converse requires system prompts inside a dedicatedsystemparameter.
I successfully patched the library's internal
aws_bedrock.py implementation in the virtual environment to wrap messages correctly and route system prompts. This resolved the issue and allowed the Bedrock Converse API to execute memory writes/reads flawlessly.What I Learned
- Context Window vs. Persistent Storage: Sending entire chat logs scales poorly. Persistent vector databases allow agents to remember personal traits and settings without bloating the prompt context.
- Dependency Diagnostics: Modifying and fixing library code is sometimes part of the job when building on top of rapidly evolving open-source frameworks.
Challenge 4: The Full Agent (Unified Orchestration)
What Was Done
I combined everything into a single, cohesive agent:
- All custom tools (calculator, weather, age calculator)
- Persistent memory (
mem0_memoryvia FAISS) - Token-by-token streaming to output responses dynamically in real-time.
How It Works
I defined a streaming callback function:
1
2
3
4
5
def stream_callback(**kwargs):
if "data" in kwargs:
print(kwargs["data"], end="", flush=True)
elif "current_tool_use" in kwargs and kwargs["current_tool_use"].get("name"):
print(f"\n🔧 Using tool: {kwargs['current_tool_use']['name']}\n", flush=True)When running the interactive chat loop:
- The user asks a question.
- The agent streams its
<thinking>block. - If it needs to call a tool, the console prints a visual indicator (e.g.,
🔧 Using tool: weather). - The response streams back dynamically block-by-block.
🚀 Key Takeaways & Learnings
- Amazon Bedrock (Nova Pro): Highly capable of multi-step tool calls, structured reasoning (using thinking blocks), and fast performance.
- Persistent Context: Storing state locally via FAISS indexation is critical for creating agents that build a long-term relationship with users.
- Architecture of Modern Agents: Agent behavior is a loop of:
$$\text{Input} \rightarrow \text{Reasoning} \rightarrow \text{Tool Selection} \rightarrow \text{Execution} \rightarrow \text{Memory Storage} \rightarrow \text{Response}$$
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article