AWS Bedrock AgentCore: Components, Architecture, and Implementation Guide¶
After working extensively with AWS Bedrock AgentCore to build containerized AI agents on a platform team, I want to break down its three core components — Runtime, Gateway, and Memory — how they work together, and practical patterns for deploying them with Terraform.
Overview¶
AWS Bedrock AgentCore consists of three main components that work together to enable containerized, stateful AI agents:
| Component | Purpose | AWS Resource | Analogy |
|---|---|---|---|
| Runtime | Hosts your agent code in a container | aws_bedrockagentcore_agent_runtime |
The "brain" — where your agent runs |
| Gateway | Exposes tools via MCP protocol | aws_bedrockagentcore_gateway |
The "toolbox" — what your agent can do |
| Memory | Stores conversation history and facts | aws_bedrockagentcore_memory |
The "memory" — what your agent remembers |
Unlike traditional AWS Bedrock Agents (which are fully managed services), AgentCore gives you:
- Full control over agent logic (you write the code)
- Containerized deployment (Docker-based)
- Built-in memory (short-term + long-term semantic storage)
- Tool integration via MCP protocol (not direct Lambda invocations)
Component 1: Runtime¶
What It Does¶
Runtime is a containerized execution environment for your agent. It runs your agent code (Strands SDK, LangGraph, custom Python, etc.) on AWS-managed serverless infrastructure.
Think of it as "AWS Lambda for AI agents" — but with:
- Persistent containers (not cold-start every time)
- ARM64 architecture (GPU-optimized)
- HTTP/MCP protocol support
- Built-in health checks
Architecture¶
┌─────────────────────────────────────────────────────┐
│ AgentCore Runtime │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Your Docker Container (ARM64) │ │
│ │ │ │
│ │ ┌──────────────────────────────────┐ │ │
│ │ │ FastAPI/Flask Server │ │ │
│ │ │ Port: 8080 │ │ │
│ │ │ │ │ │
│ │ │ Endpoints: │ │ │
│ │ │ - POST /invocations (required) │ │ │
│ │ │ - GET /ping (health check) │ │ │
│ │ └──────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────┐ │ │
│ │ │ Your Agent Logic │ │ │
│ │ │ (Strands / LangGraph / Custom) │ │ │
│ │ └──────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────┘ │
│ │
│ VPC Networking: ✅ ECR access, ✅ Bedrock API │
│ IAM Role: Bedrock, Memory, CloudWatch, X-Ray │
│ Protocol: HTTP, MCP, or A2A │
└─────────────────────────────────────────────────────┘
Terraform Resource¶
resource "aws_bedrockagentcore_agent_runtime" "agent" {
agent_runtime_name = "my_agent" # Name (letters, digits, underscores; max 48)
role_arn = "arn:aws:iam::..." # Execution role
environment_variables = { # Environment variables
AGENTCORE_MEMORY_ID = "mem_abc123"
AGENTCORE_GATEWAY_URL = "https://gateway..."
AWS_REGION = "us-east-1"
}
agent_runtime_artifact {
container_configuration {
container_uri = "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-agent:latest"
}
}
network_configuration {
network_mode = "VPC" # VPC mode (required for ECR/Bedrock)
network_mode_config {
security_groups = ["sg-abc123"]
subnets = ["subnet-abc", "subnet-def"]
}
}
protocol_configuration {
server_protocol = "HTTP" # HTTP, MCP, or A2A
}
}
Key Characteristics¶
| Aspect | Details |
|---|---|
| Container Architecture | ARM64 only (linux/arm64) — x86_64 will fail |
| Container Port | Must expose port 8080 |
| Required Endpoint | POST /invocations — receives user prompts |
| Health Check Endpoint | GET /ping or GET /health (200 OK required) |
| Networking | VPC mode — needs NAT Gateway or VPC endpoints for ECR/Bedrock |
| Protocol | HTTP (most common), MCP (tool-first), A2A (agent-to-agent) |
| Scaling | Serverless — AWS manages scaling automatically |
| Logs | CloudWatch Logs: /aws/bedrock-agentcore/runtimes/<runtime-id>-DEFAULT |
IAM Permissions Required¶
The Runtime execution role needs:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": "arn:aws:bedrock:*::foundation-model/*"
},
{
"Effect": "Allow",
"Action": [
"bedrock-agentcore:CreateEvent",
"bedrock-agentcore:ListEvents",
"bedrock-agentcore:RetrieveMemoryRecords"
],
"Resource": "arn:aws:bedrock-agentcore:*:*:memory/*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Resource": "*"
}
]
}
What Your Container Must Do¶
Your Docker image must:
- Start an HTTP server on port 8080
- Implement
/invocationsendpoint:- Accepts POST requests with JSON body:
{"prompt": "...", "session_id": "...", "actor_id": "..."} - Returns JSON:
{"response": "...", "session_id": "..."}
- Accepts POST requests with JSON body:
- Implement
/pingor/healthendpoint:- Returns 200 OK status
- Be built for ARM64 architecture (critical!)
Example: Minimal FastAPI Server with Strands SDK¶
from fastapi import FastAPI
from pydantic import BaseModel
from strands import Agent
from strands.models import BedrockModel
app = FastAPI()
# Initialize agent
model = BedrockModel(model_id="us.anthropic.claude-3-5-sonnet-20241022-v2:0")
agent = Agent(model=model, system_prompt="You are a helpful assistant.")
class InvokeRequest(BaseModel):
prompt: str
session_id: str = None
actor_id: str = None
class InvokeResponse(BaseModel):
response: str
session_id: str = None
@app.get("/ping")
@app.get("/health")
async def health_check():
return {"status": "healthy"}
@app.post("/invocations")
async def invoke_agent(request: InvokeRequest):
# Call your agent
response = agent(request.prompt)
return InvokeResponse(
response=str(response),
session_id=request.session_id
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8080)
Example: Dockerfile (ARM64)¶
FROM --platform=linux/arm64 public.ecr.aws/docker/library/python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy agent code
COPY src/ .
# Expose port
EXPOSE 8080
# Start server
CMD ["python", "server.py"]
Build command:
When to Use Runtime¶
✅ Use Runtime when:
- You need custom agent logic (Strands, LangGraph, custom frameworks)
- You want full control over conversation flow
- You need advanced memory patterns
- You want to integrate custom tools
- You need stateful agents with memory
❌ Don't use Runtime when:
- Simple Q&A with predefined tools (use Bedrock Agents instead)
- You need zero infrastructure management (use Bedrock Agents)
- You can't run Docker containers
Component 2: Gateway¶
What It Does¶
Gateway exposes tools (functions) to your agent via the MCP (Model Context Protocol). It acts as a tool registry and router, allowing your agent to:
- Discover available tools dynamically
- Invoke tools via standardized MCP requests
- Search for relevant tools semantically (optional)
Think of it as "API Gateway for AI tools" — but with:
- Automatic tool discovery (no hardcoding tool lists)
- Semantic search (LLM finds relevant tools based on query)
- AWS IAM authorization
- Lambda backend support
Architecture¶
┌─────────────────────────────────────────────────────────────┐
│ AgentCore Gateway (MCP) │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ MCP Server │ │
│ │ - Protocol: Model Context Protocol │ │
│ │ - Authorization: AWS IAM │ │
│ │ - Search: SEMANTIC (optional) │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Gateway Targets (Tools) │ │
│ │ │ │
│ │ ┌───────────────┐ ┌───────────────┐ │ │
│ │ │ Tool 1 │ │ Tool 2 │ ... │ │
│ │ │ Name: search │ │ Name: analyze │ │ │
│ │ │ Lambda ARN │ │ Lambda ARN │ │ │
│ │ │ MCP Schema │ │ MCP Schema │ │ │
│ │ └───────────────┘ └───────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Invoke Lambda Functions │
│ │
└─────────────────────────────────────────────────────────────┘
Terraform Resources¶
Gateway Resource¶
resource "aws_bedrockagentcore_gateway" "gateway" {
name = "my-tools-gateway"
description = "MCP Gateway for my agent's tools"
role_arn = "arn:aws:iam::..." # Execution role
protocol_type = "MCP" # Model Context Protocol
authorizer_type = "AWS_IAM" # AWS IAM authorization
exception_level = "DEBUG" # DEBUG or NONE (logs errors)
protocol_configuration {
mcp {
search_type = "SEMANTIC" # SEMANTIC or NONE
}
}
}
Gateway Target (Tool Definition)¶
resource "aws_bedrockagentcore_gateway_target" "search_data" {
gateway_identifier = aws_bedrockagentcore_gateway.gateway.id
name = "search_data"
description = "Search a data source for relevant records"
# Lambda backend
target_config {
lambda {
function_arn = aws_lambda_function.search_data.arn
}
}
# MCP tool schema
protocol_config {
mcp {
# Input schema (JSON Schema)
input_schema = jsonencode({
type = "object"
properties = {
query = {
type = "string"
description = "Search query"
}
max_results = {
type = "integer"
description = "Maximum number of results"
default = 10
}
}
required = ["query"]
})
# Output schema (JSON Schema)
output_schema = jsonencode({
type = "object"
properties = {
results = {
type = "array"
items = {
type = "object"
properties = {
id = { type = "string" }
title = { type = "string" }
score = { type = "number" }
}
}
}
}
})
}
}
}
Key Characteristics¶
| Aspect | Details |
|---|---|
| Protocol | MCP (Model Context Protocol) — standardized AI tool interface |
| Tool Discovery | Dynamic — agent queries Gateway for available tools |
| Tool Search | SEMANTIC (LLM-based) or NONE (name-based) |
| Authorization | AWS_IAM — Gateway uses IAM to invoke Lambda functions |
| Backend | Lambda functions (other backends possible in future) |
| Tool Schema | JSON Schema for input/output validation |
| Logs | CloudWatch Logs: /aws/bedrock-agentcore/gateways/<gateway-id> |
IAM Permissions Required¶
The Gateway execution role needs:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": "arn:aws:lambda:*:*:function:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
How Your Agent Uses Gateway¶
Your agent code connects to Gateway via MCP:
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp_proxy_for_aws.client import aws_iam_streamablehttp_client
# Create MCP client with AWS IAM auth
mcp_client = MCPClient(
lambda: aws_iam_streamablehttp_client(
endpoint="https://gateway-abc123.mcp.us-east-1.amazonaws.com",
aws_region="us-east-1",
aws_service="bedrock-agentcore",
)
)
# Create agent with MCP tools
agent = Agent(
model=BedrockModel(...),
tools=[mcp_client], # Agent auto-discovers tools from Gateway
)
# Agent can now use tools dynamically
response = agent("Search for diabetes clinical trials")
What happens under the hood:
- Agent sends query to LLM
- LLM decides it needs a tool (e.g.,
search_data) - Agent queries Gateway for tool schema
- Agent calls Gateway with tool name + arguments
- Gateway invokes Lambda function
- Lambda returns result to Gateway
- Gateway returns result to agent
- Agent continues reasoning with the result
Semantic Search (Optional)¶
When search_type = "SEMANTIC", the Gateway uses an LLM to:
- Match user queries to tool descriptions
- Rank tools by relevance
- Suggest tools the agent might not explicitly request
For example, a query like "Find information about drug side effects" would semantically match an openfda_search tool — even if the agent didn't name it explicitly.
Note
Each semantic search costs 1 Bedrock API call. Use NONE for name-based matching if cost is a concern.
When to Use Gateway¶
✅ Use Gateway when:
- You have multiple tools (2+ Lambda functions)
- Tools change frequently (dynamic discovery)
- You want semantic tool search
- You need standardized tool interface (MCP)
- You want to reuse tools across multiple agents
❌ Don't use Gateway when:
- Single tool only (direct Lambda invocation is simpler)
- Tools are hardcoded in agent (no discovery needed)
- You need non-Lambda backends (not supported yet)
Component 3: Memory¶
What It Does¶
Memory stores conversation history and extracted facts across sessions. It provides:
- Short-term memory: Recent conversation events (immediate)
- Long-term memory: Semantic facts extracted from conversations (5–10 min delay)
Think of it as "DynamoDB + Vector DB for conversations" — but with:
- Event-based storage (immutable conversation log)
- Semantic extraction (LLM-powered fact extraction)
- Actor-based isolation (per-user memory)
- Session-based organization (conversation threads)
Architecture¶
┌─────────────────────────────────────────────────────────────┐
│ AgentCore Memory │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Event Storage (Short-term Memory) │ │
│ │ │ │
│ │ SessionId: "conv-123" │ │
│ │ ActorId: "/agents/my_agent/users/user-456" │ │
│ │ │ │
│ │ Events: │ │
│ │ - [14:00] User: "Find diabetes trials..." │ │
│ │ - [14:01] Assistant: "I found 3 trials..." │ │
│ │ - [14:02] User: "Tell me more about..." │ │
│ │ │ │
│ │ Expiry: 30 days (configurable 3–365 days) │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ │ (5–10 min async) │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Semantic Extraction (Long-term Memory) │ │
│ │ │ │
│ │ Strategy: SEMANTIC │ │
│ │ Model: Bedrock (configured by AWS) │ │
│ │ │ │
│ │ Extracted Facts: │ │
│ │ - "User is researching Type 1 diabetes" │ │
│ │ - "User prefers clinical trial data" │ │
│ │ - "User is interested in pediatric studies" │ │
│ │ │ │
│ │ Namespace: /strategies/{strategyId}/actors/... │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Terraform Resources¶
Memory Resource¶
resource "aws_bedrockagentcore_memory" "memory" {
name = "my_agent_memory"
description = "Memory store for my agent"
# Events expire after N days (minimum 3, max 365)
event_expiry_duration = 30
# IAM role for memory operations
memory_execution_role_arn = "arn:aws:iam::..."
}
Memory Strategy Resource¶
resource "aws_bedrockagentcore_memory_strategy" "semantic" {
name = "semantic_facts"
memory_id = aws_bedrockagentcore_memory.memory.id
type = "SEMANTIC" # SEMANTIC or SUMMARY
description = "Extracts key facts from conversations"
# Namespace pattern for organizing extracted facts
namespaces = [
"/strategies/{memoryStrategyId}/actors/{actorId}/"
]
}
Key Characteristics¶
| Aspect | Details |
|---|---|
| Event Storage | Immutable log of conversation turns (user + assistant) |
| Event Expiry | 3–365 days (configurable) |
| Semantic Extraction | LLM-powered fact extraction (5–10 min delay) |
| Strategy Types | SEMANTIC (facts) or SUMMARY (summaries) |
| Actor-based Isolation | Separate memory per user (actorId) |
| Session Organization | Group conversations by sessionId |
| Namespace Pattern | Hierarchical organization (e.g., /agents/{agent}/users/{user}/) |
IAM Permissions Required¶
The Memory execution role needs:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel"
],
"Resource": "arn:aws:bedrock:*::foundation-model/*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "us-east-1"
}
}
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
How Your Agent Uses Memory¶
Your agent code interacts with Memory via boto3:
import boto3
from datetime import datetime, timezone
# Initialize memory client
memory_client = boto3.client("bedrock-agentcore", region_name="us-east-1")
memory_id = "mem_abc123"
# Save user message
memory_client.create_event(
memoryId=memory_id,
sessionId="conv-123",
actorId="/agents/my_agent/users/user-456",
eventTimestamp=datetime.now(timezone.utc).isoformat(),
payload=[
{
"conversational": {
"role": "USER",
"content": {"text": "Find diabetes clinical trials"}
}
}
]
)
# Retrieve recent conversation history (short-term memory)
response = memory_client.list_events(
memoryId=memory_id,
sessionId="conv-123",
actorId="/agents/my_agent/users/user-456",
maxResults=10
)
events = response.get("events", [])
# Search long-term memory (semantic facts)
memory_response = memory_client.retrieve_memory_records(
memoryId=memory_id,
namespace="/strategies/", # Prefix to search all strategies
searchCriteria={"searchQuery": "diabetes research interests"},
maxResults=5
)
facts = memory_response.get("memoryRecordSummaries", [])
# Build conversation context
context = ""
for event in events:
role = event["payload"][0]["conversational"]["role"]
text = event["payload"][0]["conversational"]["content"]["text"]
context += f"{role}: {text}\n"
for fact in facts:
context += f"Fact: {fact['content']['text']}\n"
# Use context in agent prompt
enhanced_prompt = f"""Context from previous conversations:
{context}
Current request: Find diabetes clinical trials
"""
Memory Strategies¶
| Strategy | Type | Description | Use Case | Extraction Delay |
|---|---|---|---|---|
| SEMANTIC | SEMANTIC |
Extracts key facts and user preferences | Long-term memory (user profile, interests) | 5–10 minutes |
| SUMMARY | SUMMARY |
Creates conversation summaries | Session summaries (recap long conversations) | 5–10 minutes |
Example extracted facts (SEMANTIC):
- "User is researching Type 1 diabetes"
- "User prefers pediatric studies"
- "User works in clinical research"
Example summary (SUMMARY):
- "Conversation about diabetes clinical trials. User asked for pediatric studies and requested trial locations in California."
Actor ID Pattern¶
The actorId identifies who the memory belongs to:
# Pattern: /agents/{agent_name}/users/{user_id}
actor_id = f"/agents/my_agent/users/user-456"
# Alternative patterns:
actor_id = f"/teams/research_team/users/user-456" # Team-based
actor_id = f"/organizations/acme_corp/users/user-456" # Org-based
Why hierarchical?
- Enables namespace-based queries (search all users in a team)
- Supports future access control patterns
- Matches AWS resource hierarchy conventions
Memory Timeline¶
User sends message
│
▼
[T+0s] create_event() saves user message
│
▼
Agent processes and responds
│
▼
[T+2s] create_event() saves assistant response
│
▼
[Immediate] list_events() retrieves recent conversation
│ ↓
│ Short-term memory available ✅
│
▼
[T+5-10 min] Semantic extraction processes events
│ ↓
│ LLM analyzes conversation
│ ↓
│ Facts extracted and stored
│
▼
[T+10 min] retrieve_memory_records() searches facts
│ ↓
│ Long-term memory available ✅
When to Use Memory¶
✅ Use Memory when:
- Multi-turn conversations (chatbots, assistants)
- User preferences should persist (personalization)
- Agent needs to recall past interactions
- Building a knowledge base from conversations
- Session continuity is important
❌ Don't use Memory when:
- Single-turn Q&A (no conversation history)
- Stateless operations (API-style requests)
- Privacy constraints prevent storing conversations
- You need immediate long-term memory (5–10 min delay is unacceptable)
How Components Work Together¶
Full Request Flow¶
Here's what happens end-to-end when a user sends a prompt to an AgentCore-powered agent:
┌──────────────────────────────────────────────────────────────────┐
│ 1. User sends prompt │
│ "Find diabetes clinical trials in California" │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 2. Runtime receives request │
│ POST /invocations │
│ {"prompt": "...", "session_id": "conv-123", ...} │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 3. Runtime loads conversation context from Memory │
│ list_events(sessionId="conv-123") │
│ retrieve_memory_records(query="diabetes") │
│ │
│ Returns: │
│ - Recent messages: "User asked about pediatric studies..." │
│ - Long-term facts: "User prefers CA-based trials" │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 4. Runtime builds enhanced prompt │
│ Context: [recent conversation + long-term facts] │
│ Request: "Find diabetes clinical trials in California" │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 5. Agent processes prompt with LLM │
│ LLM decides: "I need the search_data tool" │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 6. Agent queries Gateway for tool │
│ MCP Request: tools/list │
│ Gateway returns: search_data tool schema │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 7. Agent calls Gateway to invoke tool │
│ MCP Request: tools/call │
│ Tool: search_data │
│ Args: {query: "diabetes", location: "California"} │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 8. Gateway invokes Lambda function │
│ Lambda: search_data_handler(query, location) │
│ Returns: [list of results] │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 9. Gateway returns result to Agent │
│ Result: {results: [...]} │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 10. Agent generates final response │
│ LLM synthesizes answer with tool results │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 11. Runtime saves conversation to Memory │
│ create_event(role=USER, content="Find...") │
│ create_event(role=ASSISTANT, content="I found...") │
└────────────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 12. Runtime returns response to user │
│ {"response": "I found 3 trials in California...", ...} │
└──────────────────────────────────────────────────────────────────┘
Component Dependencies¶
Runtime
│
├── Depends on → Memory (optional, for conversation history)
│ - Reads: list_events, retrieve_memory_records
│ - Writes: create_event
│
├── Depends on → Gateway (optional, for tools)
│ - MCP protocol: tools/list, tools/call
│
└── Depends on → Bedrock (required, for LLM)
- InvokeModel, InvokeModelWithResponseStream
Gateway
│
├── Depends on → Lambda (required, for tool backends)
│ - InvokeFunction
│
└── Depends on → Bedrock (optional, for semantic search)
- InvokeModel (if search_type=SEMANTIC)
Memory
│
└── Depends on → Bedrock (required, for semantic extraction)
- InvokeModel (for SEMANTIC/SUMMARY strategies)
Component Lifecycle¶
Deployment Order¶
# 1. Deploy Memory (no dependencies)
terraform apply -target=module.agentcore_memory
# 2. Deploy Gateway + Lambda tools (no dependencies)
terraform apply -target=module.agentcore_gateway
terraform apply -target=aws_bedrockagentcore_gateway_target.search_data
# 3. Build and push Docker image
docker buildx build --platform linux/arm64 -t my-agent:latest --load .
aws ecr get-login-password | docker login --username AWS --password-stdin <ecr-repo>
docker tag my-agent:latest <ecr-repo>/my-agent:latest
docker push <ecr-repo>/my-agent:latest
# 4. Deploy Runtime (depends on Memory, Gateway, ECR image)
terraform apply -target=module.agentcore_runtime
Update Workflow¶
# Update agent code
vim src/agent/server.py
# Rebuild and push image
make docker-build docker-push
# Runtime will use new image on next cold start
# To force update:
terraform apply
Cold Start Behavior¶
| Component | Cold Start Time | Frequency | Mitigation |
|---|---|---|---|
| Runtime | 30–60s (pulls Docker image) | After ~15 min idle | Keep-alive requests, or accept cold starts |
| Gateway | <1s | Rare (serverless) | None needed |
| Memory | <1s | Never (always available) | None needed |
Configuration Examples¶
Minimal Setup (No Memory, No Tools)¶
# Just a Runtime with LLM
module "agentcore_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_name = "simple_agent"
image_uri = "123456789012.dkr.ecr.us-east-1.amazonaws.com/simple-agent:latest"
role_arn = module.runtime_role.role_arn
tags = local.standard_tags
}
Use case: Single-turn Q&A, no tools, no memory.
Runtime + Memory (Stateful Agent, No Tools)¶
# Memory
module "agentcore_memory" {
source = "../../modules/agentcore/memory"
name = "my_memory"
role_arn = module.memory_role.role_arn
strategies = [
{
name = "semantic"
type = "SEMANTIC"
description = "Extract user preferences"
namespaces = ["/strategies/{memoryStrategyId}/actors/{actorId}/"]
}
]
tags = local.standard_tags
}
# Runtime
module "agentcore_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_name = "stateful_agent"
image_uri = "123456789012.dkr.ecr.us-east-1.amazonaws.com/stateful-agent:latest"
role_arn = module.runtime_role.role_arn
agent_runtime_env_variables = {
AGENTCORE_MEMORY_ID = module.agentcore_memory.memory_id
}
tags = local.standard_tags
}
Use case: Chatbot with conversation history, no tools.
Runtime + Gateway (Tool-enabled Agent, No Memory)¶
# Gateway
module "agentcore_gateway" {
source = "../../modules/agentcore/gateway"
name = "my_gateway"
description = "Tools for my agent"
role_arn = module.gateway_role.role_arn
tags = local.standard_tags
}
# Tool: search_web
resource "aws_bedrockagentcore_gateway_target" "search_web" {
gateway_identifier = module.agentcore_gateway.gateway_id
name = "search_web"
description = "Search the web for information"
target_config {
lambda {
function_arn = aws_lambda_function.search_web.arn
}
}
protocol_config {
mcp {
input_schema = jsonencode({
type = "object"
properties = {
query = { type = "string", description = "Search query" }
}
required = ["query"]
})
output_schema = jsonencode({
type = "object"
properties = {
results = { type = "array" }
}
})
}
}
}
# Runtime
module "agentcore_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_name = "tool_agent"
image_uri = "123456789012.dkr.ecr.us-east-1.amazonaws.com/tool-agent:latest"
role_arn = module.runtime_role.role_arn
agent_runtime_env_variables = {
AGENTCORE_GATEWAY_URL = module.agentcore_gateway.gateway_url
}
tags = local.standard_tags
}
Use case: Stateless agent with web search, no memory.
Full Stack (Runtime + Gateway + Memory)¶
# Memory
module "agentcore_memory" {
source = "../../modules/agentcore/memory"
name = "agent_memory"
role_arn = module.memory_role.role_arn
strategies = [
{
name = "semantic"
type = "SEMANTIC"
description = "Extract research interests"
namespaces = ["/strategies/{memoryStrategyId}/actors/{actorId}/"]
}
]
tags = local.standard_tags
}
# Gateway
module "agentcore_gateway" {
source = "../../modules/agentcore/gateway"
name = "agent_tools"
description = "Research tools"
role_arn = module.gateway_role.role_arn
tags = local.standard_tags
}
# Tool: search_data
resource "aws_bedrockagentcore_gateway_target" "search_data" {
gateway_identifier = module.agentcore_gateway.gateway_id
name = "search_data"
description = "Search data sources for relevant records"
target_config {
lambda {
function_arn = aws_lambda_function.search_data.arn
}
}
protocol_config {
mcp {
input_schema = jsonencode({
type = "object"
properties = {
query = { type = "string", description = "Search query" }
}
required = ["query"]
})
output_schema = jsonencode({
type = "object"
properties = {
results = { type = "array" }
}
})
}
}
}
# Runtime
module "agentcore_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_name = "research_analyst"
image_uri = "123456789012.dkr.ecr.us-east-1.amazonaws.com/research-analyst:latest"
role_arn = module.runtime_role.role_arn
agent_runtime_env_variables = {
AGENTCORE_MEMORY_ID = module.agentcore_memory.memory_id
AGENTCORE_GATEWAY_URL = module.agentcore_gateway.gateway_url
}
tags = local.standard_tags
}
Use case: Full-featured agent with tools + memory.
Common Patterns¶
Pattern 1: Multi-Agent with Shared Gateway¶
# Shared Gateway
module "shared_gateway" {
source = "../../modules/agentcore/gateway"
name = "shared_tools"
# ... 10 tools registered
}
# Agent 1: Research Analyst
module "research_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_name = "research_analyst"
agent_runtime_env_variables = {
AGENTCORE_GATEWAY_URL = module.shared_gateway.gateway_url
}
}
# Agent 2: Financial Analyst
module "financial_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_name = "financial_analyst"
agent_runtime_env_variables = {
AGENTCORE_GATEWAY_URL = module.shared_gateway.gateway_url # Same Gateway!
}
}
Benefits:
- Reuse tools across agents
- Single Gateway to manage
- Lower cost (1 Gateway vs 2)
Considerations:
- All agents see all tools (use Gateway interceptors for filtering)
- Gateway becomes single point of failure
Pattern 2: Per-Agent Memory Isolation¶
# Agent 1 Memory
module "research_memory" {
source = "../../modules/agentcore/memory"
name = "research_memory"
}
# Agent 2 Memory
module "financial_memory" {
source = "../../modules/agentcore/memory"
name = "financial_memory"
}
# Agent 1 Runtime
module "research_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_env_variables = {
AGENTCORE_MEMORY_ID = module.research_memory.memory_id
}
}
# Agent 2 Runtime
module "financial_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_env_variables = {
AGENTCORE_MEMORY_ID = module.financial_memory.memory_id
}
}
Benefits:
- Complete memory isolation
- Independent scaling
- Easier compliance (separate data stores)
Considerations:
- Cannot share memory between agents
- Higher cost (2 Memory instances)
Pattern 3: Shared Memory with Actor Namespacing¶
# Shared Memory
module "shared_memory" {
source = "../../modules/agentcore/memory"
name = "org_memory"
}
# Both agents use same Memory ID, different actorId prefixes
module "research_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_env_variables = {
AGENTCORE_MEMORY_ID = module.shared_memory.memory_id
AGENT_NAME = "research_analyst" # Used to build actorId
}
}
module "financial_runtime" {
source = "../../modules/agentcore/runtime"
agent_runtime_env_variables = {
AGENTCORE_MEMORY_ID = module.shared_memory.memory_id
AGENT_NAME = "financial_analyst" # Different prefix
}
}
Agent code:
agent_name = os.environ["AGENT_NAME"]
actor_id = f"/agents/{agent_name}/users/{user_id}" # Isolates by agent name
Benefits:
- Share Memory infrastructure (lower cost)
- Logical isolation via actorId
- Could enable cross-agent memory queries (advanced)
Considerations:
- Must enforce actorId conventions in code
- Risk of actorId collision (namespace carefully)
Troubleshooting¶
Runtime Issues¶
| Symptom | Cause | Solution |
|---|---|---|
| 502 errors | Container crash, wrong architecture, or health check failing | 1. Check CloudWatch logs 2. Verify ARM64 architecture: docker inspect <image> --format='{{.Architecture}}' 3. Test locally: docker run -p 8080:8080 <image> 4. Verify /ping returns 200 OK |
| Empty CloudWatch logs | Container never started | 1. Check VPC has NAT Gateway (for ECR access) 2. Verify IAM role has ECR permissions 3. Check ECR image exists: aws ecr describe-images |
| Slow cold starts (60s+) | Large Docker image | 1. Reduce image size (multi-stage builds) 2. Remove unnecessary dependencies 3. Accept cold starts (or implement keep-alive) |
Debug commands:
# View Runtime logs
aws logs tail "/aws/bedrock-agentcore/runtimes/<runtime-id>-DEFAULT" --since 30m --follow
# Check Runtime status
aws bedrock-agentcore get-agent-runtime --agent-runtime-id <runtime-id>
# Test locally
docker run -p 8080:8080 -e AWS_REGION=us-east-1 <image>
curl http://localhost:8080/ping
curl -X POST http://localhost:8080/invocations -d '{"prompt":"hello"}'
Gateway Issues¶
| Symptom | Cause | Solution |
|---|---|---|
| Tools not discovered | MCP connection failed | 1. Check Gateway URL is correct 2. Verify IAM permissions for Gateway invocation 3. Check CloudWatch logs for Gateway |
| Lambda invocation fails | Missing IAM permissions | 1. Gateway role needs lambda:InvokeFunction 2. Lambda resource policy allows Gateway principal |
| Tool schema errors | Invalid JSON Schema | 1. Validate JSON Schema syntax 2. Check required fields match Lambda expectations |
Debug commands:
# View Gateway logs
aws logs tail "/aws/bedrock-agentcore/gateways/<gateway-id>" --since 30m --follow
# Test Lambda directly
aws lambda invoke --function-name <function> --payload '{"query":"test"}' output.json
# Verify Gateway targets
aws bedrock-agentcore list-gateway-targets --gateway-identifier <gateway-id>
Memory Issues¶
| Symptom | Cause | Solution |
|---|---|---|
| No events returned | Wrong sessionId/actorId | 1. Verify sessionId matches create_event calls 2. Verify actorId matches create_event calls 3. Check events haven't expired |
| No semantic facts | Extraction not complete yet | 1. Wait 10–15 minutes after create_event 2. Check Memory logs for extraction errors 3. Verify Memory role has Bedrock permissions |
| "memoryRecordSummaries" key error | Wrong API response key | 1. Use response.get("memoryRecordSummaries") not response.get("records") 2. Update boto3: pip install --upgrade boto3 |
Debug commands:
# View Memory logs
aws logs tail "/aws/bedrock-agentcore/memories/<memory-id>" --since 30m --follow
# List events (short-term memory)
aws bedrock-agentcore list-events \
--memory-id <memory-id> \
--session-id <session-id> \
--actor-id "/agents/my_agent/users/user-123"
# Search semantic memory (long-term)
aws bedrock-agentcore retrieve-memory-records \
--memory-id <memory-id> \
--namespace "/strategies/" \
--search-criteria '{"searchQuery":"diabetes"}'
Multi-Component Issues¶
| Symptom | Cause | Solution |
|---|---|---|
| Agent can't invoke tools | Runtime → Gateway auth fails | 1. Verify Runtime IAM role has bedrock-agentcore:InvokeGateway 2. Check Gateway authorizer is AWS_IAM 3. Verify MCP client uses correct AWS signing |
| Memory not persisting | create_event() not called | 1. Add logging before/after create_event() 2. Wrap create_event() in try/except 3. Verify AGENTCORE_MEMORY_ID is set |
| High latency | Sequential operations | 1. Parallelize Memory queries (short + long-term) 2. Cache Gateway tool schemas 3. Profile agent code for bottlenecks |
Summary¶
| Component | Core Function | Key Benefit | Critical Requirement |
|---|---|---|---|
| Runtime | Hosts containerized agent | Full control over agent logic | ARM64 Docker image |
| Gateway | Exposes tools via MCP | Dynamic tool discovery | Lambda backend |
| Memory | Stores conversations + facts | Stateful, personalized agents | Semantic extraction delay |