The era of AI agents is here. Unlike simple chatbots that only respond to queries, AI agents can take actions, use tools, access external data, and make decisions autonomously. But building production-ready agents has been complex—until now.
In this comprehensive guide, I'll walk you through building your own AI agent using three powerful modern tools:
Kiro – An Agentic Integrated Development Environment (IDE)
Strands Agents SDK – A lightweight framework for building agent workflows
Amazon Bedrock AgentCore – AWS's managed service for deploying scalable agents
By the end, you'll understand how to connect LLMs to tools and external data sources, and how to move closer to deploying production-ready AI agents.
What Are AI Agents?
Before diving into the tools, let's clarify what makes an AI agent different from a standard LLM:
| Standard LLM | AI Agent |
|---|---|
| Responds based on training data | Takes actions in real-world systems |
| Cannot use external tools | Can call APIs, databases, and functions |
| Static knowledge cutoff | Accesses live data sources |
| Single-turn or multi-turn chat | Multi-step reasoning and planning |
| No memory of past interactions | Maintains context across sessions |
An AI agent combines an LLM's reasoning capabilities with tool use and memory to accomplish complex tasks.
The Modern AI Agent Stack
For this tutorial, we'll use three complementary tools:
1. Kiro: The Agentic IDE
Kiro is a revolutionary development environment built specifically for AI agent development. It provides:
Visual workflow design for agent logic
Built-in testing sandbox
Integration with multiple LLM providers
Version control for agent configurations
Debugging tools with step-by-step execution tracing
2. Strands Agents SDK
Strands is a lightweight, open-source SDK that simplifies:
Tool definition and registration
Agent orchestration and handoffs
Memory and state management
Streaming responses
Multi-agent collaboration
3. Amazon Bedrock AgentCore
Bedrock AgentCore is AWS's managed service that handles:
Scalable agent deployment
Automatic prompt engineering
Action group management
Knowledge base integration
Security and compliance
Cost optimization
Prerequisites
To follow along, you'll need:
Node.js 18+ or Python 3.9+ (depending on your preference)
AWS Account (for Bedrock AgentCore)
Kiro IDE – Download from kiro.dev
API Keys for at least one LLM provider (OpenAI, Anthropic, or AWS Bedrock)
Basic understanding of JavaScript/Python and REST APIs
Step 1: Setting Up Kiro IDE
Kiro provides a visual environment for designing agent workflows.
1. Install Kiro
Download and install Kiro from the official website. Once installed, create a new project:
# Kiro CLI (if available) kiro new my-ai-agent cd my-ai-agent kiro dev
2. Configure Your LLM Provider
In Kiro's settings panel, add your API keys:
OpenAI (GPT-4, GPT-3.5)
Anthropic (Claude)
AWS Bedrock (Titan, Claude, Llama)
3. Create Your First Agent
Using Kiro's visual designer:
Create a new agent called
CustomerSupportAgentSet the system prompt: "You are a helpful customer support agent for an e-commerce store. You can check order status, process returns, and answer product questions."
Select your preferred LLM model
Step 2: Defining Tools with Strands SDK
Now let's create actual tools our agent can use. We'll use Strands SDK for this.
Install Strands SDK:
# Using npm npm install @strands/agents-sdk # Using pip pip install strands-agents-sdk
Define Tools (JavaScript/TypeScript):
// tools/orderTools.js import { tool } from '@strands/agents-sdk'; // Tool to check order status export const checkOrderStatus = tool({ name: 'check_order_status', description: 'Check the status of a customer order', parameters: { orderId: { type: 'string', description: 'The order ID to check', required: true }, email: { type: 'string', description: 'Customer email for verification', required: true } }, execute: async ({ orderId, email }) => { // In production, this would call your actual order system // This is a mock implementation const mockOrders = { 'ORD-12345': { status: 'shipped', estimatedDelivery: '2026-03-15' }, 'ORD-67890': { status: 'processing', estimatedDelivery: '2026-03-20' }, 'ORD-54321': { status: 'delivered', deliveredDate: '2026-03-05' } }; const order = mockOrders[orderId]; if (!order) { return { error: 'Order not found' }; } return { orderId, status: order.status, estimatedDelivery: order.estimatedDelivery || order.deliveredDate }; } }); // Tool to initiate return export const initiateReturn = tool({ name: 'initiate_return', description: 'Start a return process for an order', parameters: { orderId: { type: 'string', description: 'The order ID to return', required: true }, reason: { type: 'string', description: 'Reason for return', required: true }, email: { type: 'string', description: 'Customer email', required: true } }, execute: async ({ orderId, reason, email }) => { // Mock return initiation const returnId = `RET-${Math.random().toString(36).substring(7)}`; return { returnId, status: 'initiated', instructions: 'Please pack the item and drop it at any nearby post office. Shipping label will be emailed within 24 hours.' }; } });
Python Version:
# tools/order_tools.py from strands import tool @tool( name="check_order_status", description="Check the status of a customer order", parameters={ "orderId": {"type": "string", "description": "The order ID to check", "required": True}, "email": {"type": "string", "description": "Customer email for verification", "required": True} } ) async def check_order_status(orderId: str, email: str): mock_orders = { 'ORD-12345': {'status': 'shipped', 'estimatedDelivery': '2026-03-15'}, 'ORD-67890': {'status': 'processing', 'estimatedDelivery': '2026-03-20'}, 'ORD-54321': {'status': 'delivered', 'deliveredDate': '2026-03-05'} } order = mock_orders.get(orderId) if not order: return {"error": "Order not found"} return { "orderId": orderId, "status": order["status"], "estimatedDelivery": order.get("estimatedDelivery", order.get("deliveredDate")) }
Step 3: Connecting to External Data Sources
Modern AI agents need access to real-time data. Let's connect our agent to external data sources.
1. Database Connection (PostgreSQL Example)
// tools/databaseTools.js import { Pool } from 'pg'; import { tool } from '@strands/agents-sdk'; const pool = new Pool({ connectionString: process.env.DATABASE_URL }); export const queryProducts = tool({ name: 'query_products', description: 'Search for products in the inventory', parameters: { category: { type: 'string', description: 'Product category to filter by', required: false }, minPrice: { type: 'number', description: 'Minimum price', required: false }, maxPrice: { type: 'number', description: 'Maximum price', required: false }, searchTerm: { type: 'string', description: 'Text to search in product names/descriptions', required: false } }, execute: async (params) => { let query = 'SELECT * FROM products WHERE 1=1'; const values = []; let paramIndex = 1; if (params.category) { query += ` AND category = $${paramIndex++}`; values.push(params.category); } if (params.minPrice) { query += ` AND price >= $${paramIndex++}`; values.push(params.minPrice); } if (params.maxPrice) { query += ` AND price <= $${paramIndex++}`; values.push(params.maxPrice); } if (params.searchTerm) { query += ` AND (name ILIKE $${paramIndex++} OR description ILIKE $${paramIndex++})`; const searchPattern = `%${params.searchTerm}%`; values.push(searchPattern, searchPattern); } query += ' LIMIT 10'; const result = await pool.query(query, values); return result.rows; } });
2. REST API Integration
// tools/apiTools.js import { tool } from '@strands/agents-sdk'; import axios from 'axios'; export const getWeather = tool({ name: 'get_weather', description: 'Get current weather for a location', parameters: { location: { type: 'string', description: 'City name or coordinates', required: true } }, execute: async ({ location }) => { const apiKey = process.env.WEATHER_API_KEY; const response = await axios.get( `https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${apiKey}&units=metric` ); return { location: response.data.name, temperature: response.data.main.temp, conditions: response.data.weather[0].description, humidity: response.data.main.humidity, windSpeed: response.data.wind.speed }; } });
Step 4: Orchestrating the Agent with Strands
Now let's bring everything together using Strands Agents SDK.
// agent/customerSupportAgent.js import { Agent, Runner } from '@strands/agents-sdk'; import { checkOrderStatus, initiateReturn } from '../tools/orderTools'; import { queryProducts } from '../tools/databaseTools'; import { getWeather } from '../tools/apiTools'; // Create the agent const customerSupportAgent = new Agent({ name: 'Customer Support Agent', instructions: ` You are a helpful customer support agent for an e-commerce store. You have access to these tools: - check_order_status: Check where an order is - initiate_return: Help customers return items - query_products: Find products in inventory - get_weather: Check weather (useful for delivery questions) Always be polite and helpful. If you don't know something, be honest. For returns, always ask for the reason before proceeding. `, tools: [ checkOrderStatus, initiateReturn, queryProducts, getWeather ], model: 'gpt-4-turbo', // or 'claude-3-opus', etc. temperature: 0.2, // Lower temperature for consistent responses maxTokens: 1000 }); // Create a runner to execute the agent const runner = new Runner({ agent: customerSupportAgent, stream: true // Enable streaming responses }); // Function to handle user queries export async function handleUserQuery(userMessage, sessionId) { const response = await runner.run({ input: userMessage, sessionId, onToolCall: (toolName, params) => { console.log(`Agent called tool: ${toolName} with params:`, params); }, onToolResult: (toolName, result) => { console.log(`Tool ${toolName} returned:`, result); } }); return response; }
Using the Agent:
// app.js import { handleUserQuery } from './agent/customerSupportAgent.js'; // Example conversation async function main() { const sessionId = 'user-123'; const query1 = await handleUserQuery( "Where is my order ORD-12345? I used email [email protected]", sessionId ); console.log('Agent:', query1.output); const query2 = await handleUserQuery( "I want to return it. The product is damaged.", sessionId ); console.log('Agent:', query2.output); } main();
Step 5: Deploying to Production with Amazon Bedrock AgentCore
Now that our agent works locally, let's deploy it to production using Amazon Bedrock AgentCore.
1. Set Up AWS Credentials
aws configure # Enter your AWS Access Key ID, Secret Access Key, and region
2. Package Your Agent for Bedrock
Create a deployment configuration:
// bedrock-config.js export const bedrockConfig = { agentName: 'customer-support-agent', description: 'E-commerce customer support agent', foundationModel: 'anthropic.claude-3-sonnet-20240229-v1:0', // Bedrock model ID instruction: ` You are a helpful customer support agent for an e-commerce store. You can check order status, process returns, and answer product questions. Always be polite and professional. `, actionGroups: [ { name: 'OrderManagement', description: 'Tools for managing orders', tools: ['check_order_status', 'initiate_return'] }, { name: 'ProductCatalog', description: 'Tools for product queries', tools: ['query_products'] } ], knowledgeBase: { id: 'KB-12345', // Optional: Link to a Bedrock Knowledge Base description: 'Product catalog and FAQ database' }, idleSessionTTLInSeconds: 1800, // 30 minutes };
3. Deploy Using AWS CLI
# Create the agent aws bedrock-agent create-agent \ --agent-name customer-support-agent \ --agent-resource-role-arn arn:aws:iam::123456789012:role/bedrock-agent-role \ --foundation-model anthropic.claude-3-sonnet-20240229-v1:0 \ --instruction "You are a helpful customer support agent..." # Associate action groups aws bedrock-agent create-agent-action-group \ --agent-id YOUR-AGENT-ID \ --action-group-name OrderManagement \ --action-group-executor custom \ --api-schema file://order-tools-schema.json # Create an alias for deployment aws bedrock-agent create-agent-alias \ --agent-id YOUR-AGENT-ID \ --agent-alias-name prod-v1
4. Using Strands with Bedrock AgentCore
Strands SDK can also work directly with Bedrock:
import { BedrockAgentRuntime } from '@aws-sdk/client-bedrock-agent-runtime'; import { StrandsBedrockAdapter } from '@strands/bedrock-adapter'; const bedrockClient = new BedrockAgentRuntime({ region: 'us-east-1', credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY } }); const adapter = new StrandsBedrockAdapter({ client: bedrockClient, agentId: 'YOUR-AGENT-ID', agentAliasId: 'YOUR-ALIAS-ID' }); const response = await adapter.invoke({ inputText: "Where's my order ORD-12345?", sessionId: 'user-123', enableTrace: true }); console.log(response.completion);
Step 6: Connecting to External Knowledge Sources
For production agents, you'll want to connect to knowledge bases for RAG (Retrieval-Augmented Generation).
Using Amazon Bedrock Knowledge Bases:
// knowledge-base-setup.js import { BedrockAgent } from '@aws-sdk/client-bedrock-agent'; const bedrockAgent = new BedrockAgent({ region: 'us-east-1' }); // Create a knowledge base from your data sources const kb = await bedrockAgent.createKnowledgeBase({ name: 'product-catalog-kb', description: 'Product catalog and documentation', roleArn: 'arn:aws:iam::123456789012:role/bedrock-kb-role', knowledgeBaseConfiguration: { type: 'VECTOR', vectorKnowledgeBaseConfiguration: { embeddingModelArn: 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v2:0' } }, storageConfiguration: { type: 'OPENSEARCH_SERVERLESS', opensearchServerlessConfiguration: { collectionArn: 'arn:aws:aoss:us-east-1:123456789012:collection/your-collection', vectorIndexName: 'bedrock-knowledge-base-index', fieldMapping: { metadataField: 'metadata', textField: 'text' } } } }); // Sync your data await bedrockAgent.startIngestionJob({ knowledgeBaseId: kb.knowledgeBaseId, dataSourceId: 'your-data-source-id' });
Then attach to your agent:
await bedrockAgent.associateAgentKnowledgeBase({ agentId: 'YOUR-AGENT-ID', knowledgeBaseId: kb.knowledgeBaseId, description: 'Product catalog knowledge base' });
Step 7: Testing and Debugging with Kiro
Kiro provides powerful debugging tools for your agent:
1. Run the Agent in Kiro's Sandbox
Open your agent in Kiro
Click "Test in Sandbox"
Enter test queries and watch step-by-step execution
See exactly which tools were called and why
2. Trace Execution
Kiro's tracing feature shows:
LLM reasoning steps
Tool selection process
Tool execution results
Final response generation
3. Performance Monitoring
Token usage per query
Response latency
Tool success rates
Cost estimates
Step 8: Advanced Patterns
Pattern 1: Multi-Agent Collaboration
import { Agent, Runner, handoff } from '@strands/agents-sdk'; const orderAgent = new Agent({ name: 'Order Agent', instructions: 'Handle all order-related queries', tools: [checkOrderStatus, initiateReturn] }); const productAgent = new Agent({ name: 'Product Agent', instructions: 'Handle product inquiries', tools: [queryProducts] }); const triageAgent = new Agent({ name: 'Triage Agent', instructions: 'Route queries to appropriate specialized agents', tools: [ handoff({ agent: orderAgent, description: 'For order-related questions' }), handoff({ agent: productAgent, description: 'For product-related questions' }) ] });
Pattern 2: Memory and Context
import { MemoryStore } from '@strands/agents-sdk'; const memory = new MemoryStore({ type: 'redis', url: process.env.REDIS_URL, ttl: 3600 // 1 hour }); const agentWithMemory = new Agent({ name: 'Agent with Memory', instructions: 'Remember user preferences and past conversations', memory, tools: [...] });
Production Considerations
| Aspect | Consideration | Tool/Solution |
|---|---|---|
| Scalability | Handle thousands of concurrent users | Bedrock AgentCore auto-scaling |
| Latency | Keep responses under 2 seconds | Strands streaming, Edge deployment |
| Cost | Optimize token usage | Kiro monitoring, Bedrock cost controls |
| Security | Protect sensitive data | Bedrock IAM, VPC, encryption |
| Observability | Monitor agent behavior | CloudWatch, Kiro analytics |
| Versioning | Manage agent updates | Bedrock agent aliases, CI/CD |
| Fallback | Handle LLM failures | Strands retry logic, fallback models |
Complete Example: Customer Support Agent
Here's a complete, production-ready example combining everything:
// index.js - Complete Customer Support Agent import express from 'express'; import { Agent, Runner, MemoryStore } from '@strands/agents-sdk'; import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime'; import { checkOrderStatus, initiateReturn, queryProducts, getWeather, trackShipment } from './tools/index.js'; import { connectDatabase } from './database.js'; const app = express(); app.use(express.json()); // Initialize database await connectDatabase(); // Create memory store const memory = new MemoryStore({ type: 'redis', url: process.env.REDIS_URL }); // Create the agent const agent = new Agent({ name: 'Ecommerce Support Agent', instructions: ` You are a customer support agent for an online store. TOOLS AVAILABLE: - check_order_status: Check order status - initiate_return: Process returns - query_products: Search products - get_weather: Check weather for delivery - track_shipment: Get real-time tracking GUIDELINES: 1. Always verify email before sharing order details 2. Be empathetic with returns 3. Suggest alternatives if products are out of stock 4. Ask clarifying questions when needed 5. End with a clear summary of actions taken `, tools: [ checkOrderStatus, initiateReturn, queryProducts, getWeather, trackShipment ], memory, model: 'gpt-4-turbo', temperature: 0.2 }); // Create runner const runner = new Runner({ agent, stream: true }); // API endpoint app.post('/api/chat', async (req, res) => { const { message, sessionId } = req.body; try { const response = await runner.run({ input: message, sessionId, onToolCall: (toolName, params) => { console.log(`Tool called: ${toolName}`, params); } }); res.json({ response: response.output, toolCalls: response.toolCalls, metrics: response.metrics }); } catch (error) { console.error('Agent error:', error); res.status(500).json({ error: 'Failed to process request' }); } }); // Start server app.listen(3000, () => { console.log('Agent API running on port 3000'); });
Conclusion
Building production-ready AI agents is now accessible to every developer thanks to modern tools like Kiro, Strands SDK, and Amazon Bedrock AgentCore.
Key Takeaways:
Kiro provides an intuitive visual environment for designing and testing agent workflows
Strands SDK simplifies tool definition, agent orchestration, and memory management
Bedrock AgentCore handles production deployment, scaling, and security
External data sources (databases, APIs, knowledge bases) make agents truly useful
Modern tooling abstracts away complexity so you can focus on agent behavior
Your Agent Development Roadmap:
Start Simple – Build a basic agent with 2-3 tools in Kiro
Add Real Data – Connect to your actual databases and APIs
Test Thoroughly – Use Kiro's sandbox for edge cases
Deploy to Bedrock – Scale with AWS managed services
Monitor & Improve – Use analytics to refine agent behavior
Resources
Sample GitHub Repository (Link to complete example code)