Skip to main content

Amazon Bedrock AgentCore Runtime

5-minute deploy · BYO Python code · AgentCore managed microVM

This is the BYO-code path on Amazon Bedrock AgentCore Runtime: you write your agent in Python, ship it via the agentcore CLI, and AWS hosts it in a managed microVM. Because the microVM runs your code, waxell-observe installs and runs inside the runtime just like any other Python dependency — the AgentCore boundary is transparent to the SDK.

What you get

  • Every LLM call your agent makes (Bedrock Converse / InvokeModel, OpenAI, Anthropic, anything) → captured as a child LLM span on the parent run with real model id, token counts, and cost.
  • Every tool call (MCP, function tools, action group invocations from inside the agent loop) → captured as tool spans.
  • Every ctx.check_policy(...) gate → enforced inside the runtime before the LLM/tool call fires.
  • Sub-agent lineage, memory ops, custom spans → same shape as any other host you'd deploy the agent to.

The microVM is just another Python host. Your agent code is unchanged; AWS handles isolation, scaling, and lifecycle.

Prerequisites

pipx install bedrock-agentcore-starter-toolkit   # provides the `agentcore` CLI
pipx install uv # required by direct_code_deploy

You also need:

  1. AWS IAM credentials with bedrock-agentcore-control:* perms (the toolkit auto-creates the runtime execution role on first deploy).
  2. Bedrock model access in your region for whichever foundation model your agent calls.

Project layout

Scaffold a new project:

agentcore create -p mywaxellagent -t basic
cd mywaxellagent

Edit pyproject.toml to add waxell-observe:

[project]
name = "mywaxellagent"
version = "0.1.0"
requires-python = ">=3.12"

dependencies = [
"waxell-observe[bedrock]>=0.1.1",
"bedrock-agentcore>=1.0.3",
"boto3>=1.40,<2.0",
]

Edit .bedrock_agentcore.yaml to use Python 3.12 or 3.13 (the versions waxell-observe ships wheels for):

runtime_type: PYTHON_3_13

Edit src/main.py with the canonical two-line instrumentation pattern:

import os
import waxell_observe as waxell

waxell.init()

import boto3
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()
REGION = os.getenv("AWS_REGION", "us-east-1")


@waxell.observe(agent_name="my-byo-agent")
def chat_turn(user_message: str) -> str:
client = boto3.client("bedrock-runtime", region_name=REGION)
response = client.converse(
modelId="amazon.nova-lite-v1:0",
messages=[{"role": "user", "content": [{"text": user_message}]}],
)
return response["output"]["message"]["content"][0]["text"]


@app.entrypoint
async def invoke(payload, context):
yield chat_turn(payload.get("prompt", ""))


if __name__ == "__main__":
app.run()

Deploy

WAXELL_API_KEY + WAXELL_API_URL are passed via the --env flag on agentcore deploy — they end up as environmentVariables on the AgentCore Runtime config and propagate into the microVM at provision time.

export WAXELL_API_KEY=<your-key>   # from `wax setup` or your .env
agentcore deploy \
--env "WAXELL_API_KEY=$WAXELL_API_KEY" \
--env "WAXELL_API_URL=https://api.waxell.dev"
Shell quoting gotcha

If you try WAXELL_API_KEY=... agentcore deploy --env "WAXELL_API_KEY=$WAXELL_API_KEY" ... in a single line, Bash expands $WAXELL_API_KEY before the prefix assignment runs and the runtime ends up with an empty key. Always export the value first or read it from a file with a separate command. Verify after deploy:

aws bedrock-agentcore-control get-agent-runtime \
--agent-runtime-id <id> --region <region> \
--query 'environmentVariables.WAXELL_API_KEY'

If it returns "", re-deploy with the export trick above.

Invoke

agentcore invoke '{"prompt": "what is the deepest known cave?"}'

Then check Waxell:

wax runs list --limit 5

You'll see your agent run under agent_name=my-byo-agent with the same shape as 01-hello-waxell — parent agent span, chat amazon.nova-lite-v1:0 LLM child span, real token counts and cost.

Network egress

AgentCore Runtime's default network_mode: PUBLIC gives the microVM NAT egress over the public internet — api.waxell.dev (HTTPS) is reachable out of the box. If you switch to network_mode: VPC you'll need a NAT gateway / VPC endpoint in your VPC so the microVM can still reach Waxell.

Policy enforcement inside the runtime

The full @waxell.observe + @waxell.tool + ctx.check_policy(...) stack runs unchanged inside the microVM — there is no AgentCore- specific shim or escape hatch. A policy denial raised during execution aborts the agent before the offending LLM or tool call lands, surfaces as PolicyViolationError to your entrypoint, and records the violation in your Waxell governance dashboard.