Building AI Agents with Amazon Bedrock Agents

AI Agents are the next evolution beyond simple chatbots. While a chatbot can only answer questions, an agent can take actions- query databases, call APIs, process transactions, and make decisions autonomously.
In this tutorial, we’ll build a Customer Service Agent that can check order status, process return requests, and look up product availability. The agent understands natural language, decides which action to take, extracts the required parameters, and executes the appropriate function, all without hardcoded if-else logic.
By the end of this guide, you’ll have a working AI agent that demonstrates the core concepts of agentic AI on AWS.
_____
Understanding AI Agents
What makes an agent different from a chatbot?
Chatbot Agent Answers questions from knowledge Takes actions in external systems Stateless responses Maintains context and memory Fixed response patterns Dynamic decision-making “What’s your return policy?” “Process a return for order #12345”
How Bedrock Agents Work:
- User Input → “What’s the status of my order 12345?”
- Agent Reasoning → Determines it needs to call
getOrderStatuswithorderId=12345 - Action Execution → Invokes Lambda function with extracted parameters
- Response Generation → Converts Lambda response into natural language
- Final Output → “Your order #12345 shipped on Dec 2nd and will arrive by Dec 5th.”
Key Components:
- Foundation Model: The LLM that powers reasoning (we’ll use Amazon Nova Lite)
- Instructions: Define the agent’s persona and behavior
- Action Groups: Define what actions the agent can perform
- Lambda Functions: Execute the actual business logic
_____
What We’re Building
A Customer Service Agent with three capabilities:
getOrderStatusCheck order shipping statusorderIdprocessReturnInitiate a return requestorderId,reasoncheckProductAvailabilityCheck if product is in stockproductId
Example Interactions:
User: "Where is my order 98765?"Agent: "Your order #98765 is currently in transit. It shipped on December 2nd and is expected to arrive by December 5th."
User: "I want to return order 98765 because it's damaged"Agent: "I've initiated a return for order #98765. Your return ID is RET-98765-1701234567. Please ship the item back within 14 days."
User: "Do you have product SKU-001 in stock?"Agent: "Yes! Product SKU-001 (Wireless Headphones) is in stock with 45 units available."
_____
Prerequisites
Before starting, ensure you have:
- An AWS account with permissions for Lambda, IAM, and Amazon Bedrock
- Basic understanding of AWS Lambda and IAM roles
- Approximately 1.5 hours for setup
______
Implementation Steps
Step 1: Verify Bedrock Access
Amazon Nova models are available immediately with no additional setup.
- Open the AWS Console and navigate to Amazon Bedrock
- Select the us-east-1 (N. Virginia) region
- Click Tests→ Chat/Text
- Select Amazon Nova Lite and send a test message
If you get a response, you’re ready to proceed.
___
Step 2: Create the Lambda Function
This Lambda function handles all three actions. The agent will invoke it with different parameters based on user intent.
2.1: Create the Function
- Navigate to AWS Lambda
- Click Create function
- Choose Author from scratch
- Function name:
customerServiceActions - Runtime: Python 3.11
- Architecture: x86_64
- Click Create function

2.2: Add Function Code
Replace the default code with:
1import json
2from datetime import datetime, timedelta
3import random
4
5# Mock database - in production, this would be real database calls
6ORDERS_DB = {
7 "12345": {
8 "status": "delivered",
9 "shipped_date": "2024-11-28",
10 "delivery_date": "2024-12-01",
11 "items": ["Wireless Mouse", "USB Cable"],
12 "total": 45.99
13 },
14 "98765": {
15 "status": "in_transit",
16 "shipped_date": "2024-12-02",
17 "estimated_delivery": "2024-12-05",
18 "items": ["Bluetooth Speaker"],
19 "total": 79.99
20 },
21 "11111": {
22 "status": "processing",
23 "items": ["Laptop Stand", "Monitor Arm"],
24 "total": 124.99
25 }
26}
27PRODUCTS_DB = {
28 "SKU-001": {"name": "Wireless Headphones", "price": 89.99, "stock": 45},
29 "SKU-002": {"name": "Mechanical Keyboard", "price": 129.99, "stock": 0},
30 "SKU-003": {"name": "USB-C Hub", "price": 49.99, "stock": 120},
31 "SKU-004": {"name": "Webcam HD", "price": 69.99, "stock": 23}
32}
33
34def get_order_status(order_id):
35 """Retrieve the status of an order."""
36 order = ORDERS_DB.get(order_id)
37
38 if not order:
39 return {
40 "success": False,
41 "message": f"Order {order_id} not found. Please check the order ID and try again."
42 }
43
44 status = order["status"]
45 items = ", ".join(order["items"])
46
47 if status == "delivered":
48 return {
49 "success": True,
50 "orderId": order_id,
51 "status": "Delivered",
52 "message": f"Order {order_id} was delivered on {order['delivery_date']}.",
53 "items": items,
54 "total": order["total"]
55 }
56 elif status == "in_transit":
57 return {
58 "success": True,
59 "orderId": order_id,
60 "status": "In Transit",
61 "message": f"Order {order_id} shipped on {order['shipped_date']} and is expected to arrive by {order['estimated_delivery']}.",
62 "items": items,
63 "total": order["total"]
64 }
65 else: # processing
66 return {
67 "success": True,
68 "orderId": order_id,
69 "status": "Processing",
70 "message": f"Order {order_id} is currently being processed and will ship within 1-2 business days.",
71 "items": items,
72 "total": order["total"]
73 }
74
75def process_return(order_id, reason):
76 """Process a return request for an order."""
77 order = ORDERS_DB.get(order_id)
78
79 if not order:
80 return {
81 "success": False,
82 "message": f"Order {order_id} not found. Cannot process return."
83 }
84
85 if order["status"] == "processing":
86 return {
87 "success": False,
88 "message": f"Order {order_id} hasn't shipped yet. You can cancel it instead of returning."
89 }
90
91 # Generate a return ID
92 return_id = f"RET-{order_id}-{int(datetime.now().timestamp())}"
93
94 return {
95 "success": True,
96 "returnId": return_id,
97 "orderId": order_id,
98 "reason": reason,
99 "message": f"Return initiated for order {order_id}. Your return ID is {return_id}. Please ship the item back within 14 days. Refund of ${order['total']} will be processed within 5-7 business days after we receive the item.",
100 "refundAmount": order["total"]
101 }
102
103def check_product_availability(product_id):
104 """Check if a product is in stock."""
105 product = PRODUCTS_DB.get(product_id.upper())
106
107 if not product:
108 return {
109 "success": False,
110 "message": f"Product {product_id} not found in our catalog."
111 }
112
113 if product["stock"] > 0:
114 return {
115 "success": True,
116 "productId": product_id.upper(),
117 "productName": product["name"],
118 "inStock": True,
119 "quantity": product["stock"],
120 "price": product["price"],
121 "message": f"{product['name']} is in stock with {product['stock']} units available. Price: ${product['price']}"
122 }
123 else:
124 return {
125 "success": True,
126 "productId": product_id.upper(),
127 "productName": product["name"],
128 "inStock": False,
129 "quantity": 0,
130 "price": product["price"],
131 "message": f"{product['name']} is currently out of stock. Price: ${product['price']}. Would you like to be notified when it's back?"
132 }
133
134def lambda_handler(event, context):
135 """
136 Main handler for Bedrock Agent action group.
137 Routes to appropriate function based on the action.
138 """
139 print(f"Received event: {json.dumps(event)}")
140
141 # Extract action group and function from the event
142 action_group = event.get('actionGroup', '')
143 function_name = event.get('function', '')
144 parameters = event.get('parameters', [])
145
146 # Convert parameters list to dictionary
147 params = {}
148 for param in parameters:
149 params[param['name']] = param['value']
150
151 print(f"Action: {function_name}, Parameters: {params}")
152
153 # Route to appropriate function
154 if function_name == 'getOrderStatus':
155 order_id = params.get('orderId', '')
156 result = get_order_status(order_id)
157
158 elif function_name == 'processReturn':
159 order_id = params.get('orderId', '')
160 reason = params.get('reason', 'No reason provided')
161 result = process_return(order_id, reason)
162
163 elif function_name == 'checkProductAvailability':
164 product_id = params.get('productId', '')
165 result = check_product_availability(product_id)
166
167 else:
168 result = {
169 "success": False,
170 "message": f"Unknown function: {function_name}"
171 }
172
173 # Format response for Bedrock Agent
174 response = {
175 'messageVersion': '1.0',
176 'response': {
177 'actionGroup': action_group,
178 'function': function_name,
179 'functionResponse': {
180 'responseBody': {
181 'TEXT': {
182 'body': json.dumps(result)
183 }
184 }
185 }
186 }
187 }
188
189 print(f"Returning response: {json.dumps(response)}")
190 return response
Click Deploy to save.
2.3: Configure Function Settings
- Go to Configuration → General configuration
- Click Edit
- Set Timeout: 30 seconds
- Set Memory: 256 MB
- Click Save

2.4: Note the Function ARN
- At the top of the Lambda page, copy the Function ARN
- It looks like:
arn:aws:lambda:us-east-1:123456789012:function:customerServiceActions - Save this- you’ll need it when creating the agent
___
Step 3: Create the Bedrock Agent
3.1: Navigate to Agents
- Go to Amazon Bedrock console
- In the left navigation, under Builder tools, click Agents
- Click Create Agent
3.2: Configure Agent Details
- Agent name:
CustomerServiceAgent - Description:
AI agent for handling customer service inquiries including order status, returns, and product availability - Click Create

3.3: Select Foundation Model
- In the Agent builder page, find the Model section
- Click Edit
- Select Amazon Nova Lite
- Click Save

3.4: Add Agent Instructions
In the Instructions section, enter:
1You are a friendly and helpful customer service agent for an e-commerce company. Your role is to assist customers with:
2
31. Order Status: Help customers track their orders by looking up order IDs
42. Returns: Process return requests when customers want to return items
53. Product Availability: Check if products are in stock
6Guidelines:
7- Always be polite and professional
8- When a customer asks about an order, use the getOrderStatus function
9- When a customer wants to return an item, ask for the order ID and reason, then use processReturn
10- When checking product availability, use checkProductAvailability with the product ID
11- If you don't have enough information, politely ask the customer for the missing details
12- Format responses in a clear, easy-to-read manner
13- If an order or product is not found, apologize and offer to help find the correct information
14Example order IDs for testing: 12345, 98765, 11111
15Example product IDs: SKU-001, SKU-002, SKU-003, SKU-004Click Save.
___
Step 4: Create the Action Group
4.1: Add Action Group
- Scroll down to the Action groups section
- Click Add
4.2: Configure Action Group
- Action group name:
CustomerServiceActions - Description:
Actions for order management and product inquiries - Action group type: Select Define with function details

4.3: Add Functions
Click Add function and create three functions:
Function 1: getOrderStatus
Field Value Name getOrderStatus Description Retrieves the current status of a customer order including shipping information
Parameters:
Name:orderId
Type: Required
Description:The unique order identifier (e.g., 12345
String: Yes
Click Add.

Function 2: processReturn
Field Value Name processReturn Description Initiates a return request for a customer order
Parameters:
Name:orderId
Type: Required
Description:The order ID to returnreason
String: Yes
Name: reason
String: Yes
Description:The reason for returning the item
Type: Required
Click Add.

Function 3: checkProductAvailability
Field Value Name checkProductAvailability Description Checks if a product is currently in stock and returns availability information
Parameters:
Name:productId
Type: Required
Description:The product SKU to check (e.g., SKU-001)
String: Yes
Click Add.

4.4: Configure Lambda Invocation
- In Action group invocation, select Select an existing Lambda function
- Choose
customerServiceActionsfrom the dropdown - For version, select
DRAFTor the latest version

Press enter or click to view image in full size
4.5: Create Action Group
Click Create to save the action group.
___
Step 5: Add Lambda Permissions
The agent needs permission to invoke your Lambda function.
5.1: Add Resource-Based Policy to Lambda
- Go to AWS Lambda console
- Open your
customerServiceActionsfunction - Go to Configuration → Permissions
- Scroll to Resource-based policy statements

5. Click Add permissions
6. Choose AWS Service
7. Configure:
- Service:
Other - Statement ID:
AllowBedrockAgent - Principal:
bedrock.amazonaws.com - Source ARN:
arn:aws:bedrock:us-east-1:YOUR_ACCOUNT_ID:agent/* - Action:
lambda:InvokeFunction
8. Click Save
Press enter or click to view image in full size
Note: Replace YOUR_ACCOUNT_ID with your actual AWS account ID.
___
Step 6: Prepare and Test the Agent
6.1: Prepare the Agent
- Go back to your agent in Amazon Bedrock
- Click Prepare at the top of the page
- Wait for status to show Prepared

6.2: Test in Console
- On the right side, you’ll see a Test panel
- If not visible, click Test button
Test Queries:
Test 1 — Order Status:
What's the status of order 98765?
Expected: Agent should call getOrderStatus and return shipping information.

Test 2 — Process Return:
I want to return order 12345 because the item was damaged
Expected: Agent should call processReturn and provide a return ID.

Test 3 — Product Availability:
Is SKU-001 in stock?
Expected: Agent should call checkProductAvailability and return stock info.

Test 4 — Out of Stock:
Check if SKU-002 is available
Expected: Agent should indicate the product is out of stock.

Test 5 — Not Found:
Where is my order 99999?
Expected: Agent should indicate order not found.

Test 6 — Conversation:
Hi, I need help with my order
Expected: Agent should ask for the order ID.

___
Step 7: Invoke Agent from Code (Optional)
Here’s how to call your agent programmatically:
1import boto3
2import json
3import uuid
4
5def invoke_agent(prompt):
6 client = boto3.client('bedrock-agent-runtime', region_name='us-east-1')
7
8 response = client.invoke_agent(
9 agentId='YOUR_AGENT_ID', # From agent overview
10 agentAliasId='YOUR_ALIAS_ID', # From alias (e.g., 'prod')
11 sessionId=str(uuid.uuid4()),
12 inputText=prompt
13 )
14
15 # Process streaming response
16 result = ""
17 for event in response['completion']:
18 if 'chunk' in event:
19 chunk = event['chunk']
20 if 'bytes' in chunk:
21 result += chunk['bytes'].decode('utf-8')
22
23 return result
24
25# Test the agent
26if __name__ == "__main__":
27 queries = [
28 "What's the status of order 98765?",
29 "I want to return order 12345 because it arrived damaged",
30 "Is SKU-003 in stock?"
31 ]
32
33 for query in queries:
34 print(f"\nUser: {query}")
35 response = invoke_agent(query)
36 print(f"Agent: {response}")___
Understanding the Agent Flow
What happens when you ask “Where is order 98765?”
- Input Processing
- Agent receives: “Where is order 98765?”
2. Intent Recognition
- Nova Lite analyzes the request
- Determines user wants order status
- Identifies
98765as the order ID
3. Function Selection
- Agent matches intent to
getOrderStatusfunction - Extracts parameter:
orderId = "98765"
4. Lambda Invocation
- Agent invokes Lambda with:
{ "function": "getOrderStatus", "parameters": [{"name": "orderId", "value": "98765"}]}
5. Response Processing
- Lambda returns order details
- Agent converts JSON to natural language
6. Final Response
- “Your order #98765 is currently in transit. It shipped on December 2nd and is expected to arrive by December 5th.”
_____
Production Enhancements
For real-world deployment, consider:
- Connect to Real Data Sources
- Replace mock data with DynamoDB or RDS queries
- Integrate with your order management system
2. Add Authentication
- Verify customer identity before showing order details
- Integrate with Cognito for user sessions
3. Add Guardrails
- Attach Bedrock Guardrails to prevent misuse
- Filter PII in responses
4. Enable Memory
- Configure session memory for multi-turn conversations
- Remember customer context across interactions
5. Add Knowledge Base
- Attach a knowledge base for FAQ-style questions
- “What’s your return policy?” → Retrieved from docs
6. Multi-Agent Collaboration
- Create specialized sub-agents
- Supervisor agent routes to appropriate specialist
______
Troubleshooting
1.“Agent not responding”
- Ensure agent is in Prepared status
- Check Lambda permissions (resource-based policy)
- Verify Lambda function works independently
2.“Function not found”
- Check function names match exactly (case-sensitive)
- Verify action group is properly saved
- Re-prepare agent after changes
3.“Access Denied on Lambda”
- Add resource-based policy to Lambda
- Ensure principal is
bedrock.amazonaws.com - Check source ARN matches your agent
4.“Timeout errors”
- Increase Lambda timeout (30+ seconds)
- Check Lambda function logs in CloudWatch
- Verify mock data exists for test cases
5.Agent gives wrong responses
- Refine instructions with more specific guidance
- Add examples in the instructions
- Check function descriptions are clear
______
Conclusion
You’ve built a fully functional AI agent that can:
- Understand natural language requests
- Decide which action to take autonomously
- Extract required parameters from conversation
- Execute business logic via Lambda
- Respond in natural, helpful language
______
Resources
Want to learn more?
Check out these related courses to dive deeper into this topic
