AWS Bedrock AgentCore Async Agents
27th February 2026
AWS Bedrock AgentCore lets you deploy AI agents as managed microVMs with built-in health checks, session management, and async task support. The async pattern is the interesting part — your agent responds immediately, runs work in the background, and the client polls for results. Here’s how the architecture works.
MicroVMs and Runtime Sessions
Each runtimeSessionId gets its own isolated microVM. This is the most important concept to understand:
WITHOUT runtimeSessionId:
─────────────────────────────────────────────────────────
Call 1 (no sessionId) → MicroVM #1 (random session)
Call 2 (no sessionId) → MicroVM #2 (random session)
Call 3 (no sessionId) → MicroVM #3 (random session)
Each call creates a NEW microVM. Tasks are isolated.
WITH runtimeSessionId:
─────────────────────────────────────────────────────────
Call 1 (sessionId="my-session") ┐
Call 2 (sessionId="my-session") ├─→ SAME MicroVM
Call 3 (sessionId="my-session") ┘
All calls reuse the SAME microVM. Tasks share the instance.
The lifecycle timers control how long the microVM stays alive:
Default Settings:
├─ idleRuntimeSessionTimeout: 900s (15 minutes)
└─ maxLifetime: 28800s (8 hours)
Timeline for a 40-minute task:
─────────────────────────────────────────────────────────
00:00 Task starts (Status: BUSY)
00:00-40:00 MicroVM alive, doing work
40:00 Task completes (Status: Healthy)
40:00-55:00 Idle period (15 min countdown)
55:00 MicroVM terminates — ALL DATA LOST
idleRuntimeSessionTimeout resets on each invocation. maxLifetime starts at microVM creation and cannot be reset — it’s an absolute ceiling.
Synchronous Flow
A normal request-response cycle:
Client AgentCore Runtime Your Agent
│ │ │
│ 1. POST /invocations │ │
│ {"prompt": "hello"} │ │
├───────────────────────────▶│ │
│ │ 2. Forward to agent │
│ ├───────────────────────────▶│
│ │ 3. Agent responds │
│ │◀───────────────────────────┤
│ 4. Return response │ │
│◀───────────────────────────┤ │
│ "Hello! How can I help?" │ │
Client waits for the full response. Fine for fast queries, but blocks on anything that takes more than a few seconds.
Asynchronous Flow
For long-running tasks, the agent responds immediately and processes in the background:
Client AgentCore Runtime Your Agent
│ │ │
│ 1. POST /invocations │ │
│ {"prompt": "start task"} │ │
├───────────────────────────▶│ │
│ │ 2. Forward payload │
│ ├───────────────────────────▶│
│ │ 3. Agent: │
│ │ ├─▶ Spawn thread │
│ │ └─▶ Task ID: 12345 │
│ │ Status: BUSY │
│ │ │
│ │ 4. Immediate response │
│ │◀───────────────────────────┤
│ 5. Return response │ "Task started!" │
│◀───────────────────────────┤ │
│ "Task 12345 started" │ │
│ │ │
│ │ ┌──────────────────────┐ │
│ │ │ BACKGROUND THREAD │ │
│ │ │ ... doing work ... │ │
│ │ │ complete_async_task()│ │
│ │ └──────────────────────┘ │
The client gets a task ID back instantly. The background thread runs for as long as needed — minutes, hours — and marks itself complete when done.
Health Check Monitoring
AgentCore pings your agent every 15 seconds to check status:
AgentCore Runtime Your Agent
│ │
│ GET /ping (every 15s) │
├───────────────────────────▶│
│ {"status": "HealthyBusy"} │ ← While task running
│◀───────────────────────────┤
│ │
│ GET /ping │
├───────────────────────────▶│
│ {"status": "Healthy"} │ ← After task completes
│◀───────────────────────────┤
HealthyBusy tells AgentCore the microVM is alive and working — don’t kill it. Healthy means the work is done and the idle timeout countdown begins.
The Data Loss Problem
When the microVM terminates after its idle timeout, all in-memory data is lost. You must store results externally and have the client poll for completion:
Client AgentCore Storage (DynamoDB/S3)
│ │ │
│ 1. Start task │ │
├───────────────────────────▶│ │
│ "Task 123 started" │ │
│◀───────────────────────────┤ │
│ │ │
│ │ [Task runs 40 min] │
│ │ Store result ─────────▶│
│ │ │
│ 2. Check result │ │
├───────────────────────────▶│ │
│ │ Query storage ────────▶│
│ │◀─ "still running" ──────┤
│◀───────────────────────────┤ │
│ │ │
│ [Wait 5 minutes] │ │
│ │ │
│ 3. Check result again │ │
├───────────────────────────▶│ │
│ │ Query storage ────────▶│
│ │◀─ "completed: data" ────┤
│◀───────────────────────────┤ │
│ "Result: data" │ │
Storage options:
- DynamoDB — best for structured results, queryable by task ID
- S3 — best for large files and data blobs
- ElastiCache/Redis — best for temporary results with TTL
- In-memory — lost on termination, testing only
Agent Code
The agent uses two tools: one to start background work, one to poll for results.
app = BedrockAgentCoreApp()
@tool
def start_background_task(duration: int):
task_id = app.add_async_task("processing") # Status → BUSY
def background_work():
time.sleep(duration) # Do actual work here
# Store result externally — survives microVM termination
result = f"Task completed after {duration}s"
dynamodb.put_item(Item={'taskId': task_id, 'result': result})
app.complete_async_task(task_id) # Status → Healthy
threading.Thread(target=background_work).start()
return f"Task {task_id} started. Poll with get_task_result({task_id})"
@tool
def get_task_result(task_id: str):
"""Poll for task completion"""
response = dynamodb.get_item(Key={'taskId': task_id})
if 'Item' in response:
return f"Result: {response['Item']['result']}"
return f"Task {task_id} still running or not found"
@app.entrypoint
def main(payload):
return agent(payload["prompt"])
add_async_task() marks the microVM as BUSY (prevents idle termination). complete_async_task() marks it as Healthy (idle countdown begins). Results go to DynamoDB so they survive microVM termination.
Deployment Pipeline
┌──────────────┐
│ Your Code │
│ (Python file)│
└──────┬───────┘
│ bedrock_agentcore_starter_toolkit.launch()
▼
┌──────────────────────────────────────────────────────┐
│ AWS CodeBuild │
│ Builds Docker image with your agent code │
└──────────────────────┬───────────────────────────────┘
▼
┌──────────────────────────────────────────────────────┐
│ Amazon ECR (Container Registry) │
│ Image: bedrock-agentcore-async_agent:20260227 │
└──────────────────────┬───────────────────────────────┘
▼
┌──────────────────────────────────────────────────────┐
│ AWS Bedrock AgentCore Runtime │
│ MicroVMs spin up per runtimeSessionId │
│ ARN: arn:aws:bedrock-agentcore:us-east-1:...:runtime│
└──────────────────────┬───────────────────────────────┘
▲
│ boto3.invoke_agent_runtime()
┌──────────────────────┴───────────────────────────────┐
│ Your Application / Client │
└──────────────────────────────────────────────────────┘
Client Code: With vs Without Session
With a session ID — all calls hit the same microVM:
client = boto3.client('bedrock-agentcore', region_name='us-east-1')
SESSION_ID = "my-session-123"
response = client.invoke_agent_runtime(
agentRuntimeArn='arn:aws:bedrock-agentcore:...:runtime/async_agent',
qualifier='DEFAULT',
runtimeSessionId=SESSION_ID, # Reuses same microVM
payload=json.dumps({"prompt": "start a 5 second task"})
)
Without a session ID — every call creates a new microVM and tasks are completely isolated:
# Each call creates a NEW microVM
response = client.invoke_agent_runtime(
agentRuntimeArn='arn:aws:bedrock-agentcore:...:runtime/async_agent',
qualifier='DEFAULT',
# No runtimeSessionId
payload=json.dumps({"prompt": "start a 5 second task"})
)
Polling for Results
# Start task
response = client.invoke_agent_runtime(
agentRuntimeArn=agent_arn,
runtimeSessionId=SESSION_ID,
payload=json.dumps({"prompt": "start a 600 second task"})
)
# Extract task_id from response
# Poll for completion
while True:
time.sleep(60)
result = client.invoke_agent_runtime(
agentRuntimeArn=agent_arn,
runtimeSessionId=SESSION_ID,
payload=json.dumps({"prompt": f"get_task_result({task_id})"})
)
if "completed" in result:
break
Lifecycle Patterns
| Pattern | Idle Timeout | Max Lifetime | Notes |
|---|---|---|---|
| Interactive chat | 10-15 min | 2-4 hours | Balance responsiveness with cost |
| Long-running tasks (40+ min) | 30 min | 8 hours | DynamoDB/S3 for results, client polling |
| Development/testing | 5 min | 30 min | Quick cleanup for cost optimization |
Monitoring
Tail the runtime logs:
aws logs tail /aws/bedrock-agentcore/runtimes/async_agent-65Z67nDn49-DEFAULT \
--log-stream-name-prefix "2026/02/27/[runtime-logs]" --follow
Key Takeaways
- Immediate response — agent returns a task ID instantly, doesn’t block the client
- Background threads — work runs in a separate thread while the agent stays responsive to new invocations
- Health checks —
HealthyBusyprevents premature termination during long tasks - External storage is mandatory — in-memory data is lost when the microVM terminates
- Session ID is critical — without it, each call gets its own isolated microVM and cannot poll for results from a previous call
- Two timers — idle timeout resets on each invocation; max lifetime is an absolute ceiling that never resets
Reference
- AWS Bedrock AgentCore: Long-running tasks — official documentation on async task patterns and runtime lifecycle
More recent articles
- OpenUSD: Advanced Patterns and Common Gotchas. - 28th March 2026
- OpenUSD Mastery: From Composition to Pipeline — A SO-101 Arm Journey - 25th March 2026
- Learning OpenUSD — From Curious Questions to Real Understanding - 19th March 2026