|
| 1 | +# Amazon Bedrock AgentCore Runtime WebRTC Example (KVS Managed TURN) |
| 2 | + |
| 3 | +This example demonstrates how to deploy a Pipecat voice agent to **Amazon Bedrock AgentCore Runtime** using SmallWebRTC as a lightweight transport mechanism, with **Amazon Kinesis Video Streams (KVS)** providing managed TURN infrastructure entirely within AWS. The example pipeline orchestrates Deepgram (speech-to-text), Amazon Nova (LLM), and Cartesia (text-to-speech). |
| 4 | + |
| 5 | +> **Note:** This example focuses on illustrating how to get a Pipecat bot running as an agent in AgentCore Runtime. In the interest of staying focused on that goal, it does not address various production-readiness concerns, including but not limited to: authentication with the server that launches the agent, sanitized logging, rate limiting, CORS tightening, and input validation. Be sure to address these before deploying to production. |
| 6 | +
|
| 7 | +## How KVS Managed TURN Works |
| 8 | + |
| 9 | +Instead of configuring non-AWS TURN providers, this example uses [Amazon Kinesis Video Streams (KVS)](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/what-is-amazon-kinesis-video-streams.html) for managed TURN infrastructure. KVS provides temporary, auto-rotating TURN credentials through the [GetIceServerConfig](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_signaling_GetIceServerConfig.html) API, eliminating non-AWS dependencies for NAT traversal. |
| 10 | + |
| 11 | +The flow works as follows: |
| 12 | + |
| 13 | +1. **One-time setup:** A KVS signaling channel is created (automatically on first connection, or via CLI). The channel is used only for TURN credential provisioning -- your agent continues to use Pipecat's WebRTC transport for all signaling and media. |
| 14 | +2. **At connection time:** Your agent calls `GetSignalingChannelEndpoint` to get the HTTPS endpoint, then calls `GetIceServerConfig` to retrieve temporary TURN credentials (URIs, username, password). |
| 15 | +3. **Configure the peer connection:** The returned credentials are passed to the WebRTC peer connection as ICE servers. TURN traffic flows through KVS-managed infrastructure. |
| 16 | + |
| 17 | +### Choosing Between KVS and Non-AWS TURN |
| 18 | + |
| 19 | +| Factor | KVS Managed TURN (this example) | Non-AWS TURN | |
| 20 | +|---|---|---| |
| 21 | +| AWS-native | Yes -- no external dependency | No -- requires external account | |
| 22 | +| Credential management | Automatic rotation | Manual or provider-managed | |
| 23 | +| Setup | Create signaling channel + API calls | Configure environment variables | |
| 24 | +| Best for | AWS-centric deployments | Simplicity or existing provider relationships | |
| 25 | + |
| 26 | +> For the non-AWS TURN variant, see the [`aws-agentcore-webrtc`](../aws-agentcore-webrtc) example. |
| 27 | +
|
| 28 | +## Prerequisites |
| 29 | + |
| 30 | +- Accounts with: |
| 31 | + - AWS |
| 32 | + - Deepgram |
| 33 | + - Cartesia |
| 34 | +- Python 3.10 or higher |
| 35 | +- `uv` package manager |
| 36 | + |
| 37 | +## Set Up the Environment |
| 38 | + |
| 39 | +### IAM Configuration |
| 40 | + |
| 41 | +Configure your IAM user with the necessary policies for AgentCore deployment and management: |
| 42 | + |
| 43 | +- `BedrockAgentCoreFullAccess` |
| 44 | +- A new policy (maybe named `BedrockAgentCoreCLI`) configured [like this](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html#runtime-permissions-starter-toolkit), with the following additional statements: |
| 45 | + |
| 46 | + **EC2 access** (for VPC setup and teardown): |
| 47 | + ```json |
| 48 | + { |
| 49 | + "Sid": "EC2Access", |
| 50 | + "Effect": "Allow", |
| 51 | + "Action": [ |
| 52 | + "ec2:CreateVpc", |
| 53 | + "ec2:CreateTags", |
| 54 | + "ec2:ModifyVpcAttribute", |
| 55 | + "ec2:CreateInternetGateway", |
| 56 | + "ec2:AttachInternetGateway", |
| 57 | + "ec2:DescribeAvailabilityZones", |
| 58 | + "ec2:CreateSubnet", |
| 59 | + "ec2:AllocateAddress", |
| 60 | + "ec2:CreateNatGateway", |
| 61 | + "ec2:DescribeNatGateways", |
| 62 | + "ec2:CreateRouteTable", |
| 63 | + "ec2:CreateRoute", |
| 64 | + "ec2:AssociateRouteTable", |
| 65 | + "ec2:CreateSecurityGroup", |
| 66 | + "ec2:AuthorizeSecurityGroupEgress", |
| 67 | + "ec2:DeleteNatGateway", |
| 68 | + "ec2:ReleaseAddress", |
| 69 | + "ec2:DetachInternetGateway", |
| 70 | + "ec2:DeleteInternetGateway", |
| 71 | + "ec2:DeleteSubnet", |
| 72 | + "ec2:DeleteRouteTable", |
| 73 | + "ec2:DeleteSecurityGroup", |
| 74 | + "ec2:DeleteVpc" |
| 75 | + ], |
| 76 | + "Resource": "*" |
| 77 | + } |
| 78 | + ``` |
| 79 | + |
| 80 | + **KVS access** (for signaling channel creation): |
| 81 | + ```json |
| 82 | + { |
| 83 | + "Sid": "KVSAccess", |
| 84 | + "Effect": "Allow", |
| 85 | + "Action": [ |
| 86 | + "kinesisvideo:CreateSignalingChannel", |
| 87 | + "kinesisvideo:DescribeSignalingChannel", |
| 88 | + "kinesisvideo:DeleteSignalingChannel", |
| 89 | + "kinesisvideo:GetSignalingChannelEndpoint", |
| 90 | + "kinesisvideo:GetIceServerConfig" |
| 91 | + ], |
| 92 | + "Resource": "*" |
| 93 | + } |
| 94 | + ``` |
| 95 | + |
| 96 | +You can also choose to specify more granular permissions; see [Amazon Bedrock AgentCore docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html) for more information. |
| 97 | + |
| 98 | +To authenticate with AWS, you have two options: |
| 99 | + |
| 100 | +1. Export environment variables: |
| 101 | + |
| 102 | + ```bash |
| 103 | + export AWS_SECRET_ACCESS_KEY=your_secret_key |
| 104 | + export AWS_ACCESS_KEY_ID=your_access_key |
| 105 | + export AWS_REGION=your_region |
| 106 | + export AWS_DEFAULT_REGION=your_default_region |
| 107 | + export AWS_SESSION_TOKEN=your_session_token # Optional: only for temporary credentials (e.g. AWS SSO, STS AssumeRole) |
| 108 | + ``` |
| 109 | + |
| 110 | +2. Or use AWS CLI configuration: |
| 111 | + ```bash |
| 112 | + aws configure |
| 113 | + ``` |
| 114 | + This will create/update your AWS credentials file (~/.aws/credentials). |
| 115 | + |
| 116 | +### Virtual Environment Setup |
| 117 | + |
| 118 | +Create and activate a virtual environment: |
| 119 | + |
| 120 | +```bash |
| 121 | +uv sync |
| 122 | +``` |
| 123 | + |
| 124 | +### Environment Variables Configuration |
| 125 | + |
| 126 | +1. For the agent: |
| 127 | + |
| 128 | + ```bash |
| 129 | + cd agent |
| 130 | + cp env.example .env |
| 131 | + ``` |
| 132 | + |
| 133 | + Add your API keys: |
| 134 | + - `DEEPGRAM_API_KEY`: Your Deepgram API key |
| 135 | + - `CARTESIA_API_KEY`: Your Cartesia API key |
| 136 | + - `KVS_CHANNEL_NAME`: Name of the KVS signaling channel for TURN credentials (default: `voice-agent-turn`) |
| 137 | + |
| 138 | + > No TURN server URLs or credentials are needed -- KVS provides these automatically. |
| 139 | +
|
| 140 | +2. For the server: |
| 141 | + |
| 142 | + ```bash |
| 143 | + cd server |
| 144 | + cp env.example .env |
| 145 | + ``` |
| 146 | + |
| 147 | + Add your AWS credentials and configuration: |
| 148 | + |
| 149 | + - `AWS_ACCESS_KEY_ID` |
| 150 | + - `AWS_SECRET_ACCESS_KEY` |
| 151 | + - `AWS_REGION` |
| 152 | + - `AWS_SESSION_TOKEN` (optional -- only needed for temporary credentials, e.g. AWS SSO or STS AssumeRole) |
| 153 | + |
| 154 | + Also configure: |
| 155 | + |
| 156 | + - `AGENT_RUNTIME_ARN`: Automatically set during agent deployment |
| 157 | + - `KVS_CHANNEL_NAME`: Must match the agent configuration |
| 158 | + |
| 159 | + > **KVS permissions:** The AWS account running the server also needs KVS permissions (`kinesisvideo:DescribeSignalingChannel`, `GetSignalingChannelEndpoint`, `GetIceServerConfig`) to fetch TURN credentials for the browser client. The same KVS IAM policy listed above applies here. |
| 160 | +
|
| 161 | +### KVS Signaling Channel Setup (Optional) |
| 162 | + |
| 163 | +The signaling channel is auto-created on first connection. To create it ahead of time: |
| 164 | + |
| 165 | +```bash |
| 166 | +aws kinesisvideo create-signaling-channel \ |
| 167 | + --channel-name voice-agent-turn \ |
| 168 | + --channel-type SINGLE_MASTER \ |
| 169 | + --region us-west-2 |
| 170 | +``` |
| 171 | + |
| 172 | +## Agent Configuration |
| 173 | + |
| 174 | +Configure your bot as an AgentCore agent: |
| 175 | + |
| 176 | +```bash |
| 177 | +./scripts/configure.sh |
| 178 | +``` |
| 179 | + |
| 180 | +This script automatically: |
| 181 | + |
| 182 | +1. Creates IAM execution role (if needed) with Bedrock and KVS permissions |
| 183 | +2. Configures container deployment with docker runtime |
| 184 | +3. Patches Dockerfile to add SmallWebRTC dependencies (`libgl1` and `libglib2.0-0`) |
| 185 | + |
| 186 | +> Technical Note: |
| 187 | +> Direct Code Deploy isn't used because some dependencies (like `numba`) lack `aarch64_manylinux2014` wheels. |
| 188 | +
|
| 189 | +## Before Proceeding |
| 190 | + |
| 191 | +Just in case you've previously deployed other agents to AgentCore, ensure that you have the desired agent selected as "default" in the `agentcore` tool: |
| 192 | + |
| 193 | +``` |
| 194 | +# Check |
| 195 | +uv run agentcore configure list |
| 196 | +# Set |
| 197 | +uv run agentcore configure set-default <agent-name> |
| 198 | +``` |
| 199 | + |
| 200 | +The following steps act on `agentcore`'s default agent. |
| 201 | + |
| 202 | +## Deployment to AgentCore Runtime |
| 203 | + |
| 204 | +**VPC Mode (recommended) - TCP and UDP TURN support:** |
| 205 | + |
| 206 | +```bash |
| 207 | +# First time: Create VPC infrastructure (NAT Gateway costs ~$32/month) |
| 208 | +# Note that this creates various Elastic IP addresses; ensure you have sufficient quota (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html#using-instance-addressing-limit) |
| 209 | +./scripts/setup-vpc.sh |
| 210 | + |
| 211 | +# Deploy agent |
| 212 | +# (This is the only command you need to run after an incremental change to your agent's code) |
| 213 | +./scripts/launch.sh |
| 214 | +``` |
| 215 | + |
| 216 | +This deploys AgentCore Runtime in private subnets with NAT Gateway for outbound internet access, enabling UDP TURN relay (blocked in PUBLIC mode) for better WebRTC connection reliability, lower latency, and enhanced security with private subnet isolation. |
| 217 | + |
| 218 | +**Infrastructure overview:** |
| 219 | + |
| 220 | +- VPC with public and private subnets across 2 availability zones |
| 221 | +- Internet Gateway for public subnet connectivity |
| 222 | +- NAT Gateway in public subnet for private subnet outbound traffic |
| 223 | +- Route tables directing private subnet traffic through NAT Gateway |
| 224 | +- Security groups allowing outbound HTTPS and UDP connections |
| 225 | + |
| 226 | +**PUBLIC Mode - TCP TURN only:** |
| 227 | + |
| 228 | +For development/testing without UDP TURN: |
| 229 | + |
| 230 | +```bash |
| 231 | +./scripts/launch.sh |
| 232 | +``` |
| 233 | + |
| 234 | +The launch script: |
| 235 | + |
| 236 | +1. Reads environment variables from `agent/.env` |
| 237 | +2. Deploys to AgentCore |
| 238 | +3. Updates the server's configuration with the agent ARN |
| 239 | +4. Displays log-tailing commands for monitoring |
| 240 | + |
| 241 | +> **Note on KVS and VPC:** KVS TURN endpoints do not support PrivateLink, so the VPC still requires internet egress (via NAT Gateway) to reach KVS TURN endpoints. |
| 242 | +
|
| 243 | +## Running on AgentCore Runtime |
| 244 | + |
| 245 | +1. Start the server: |
| 246 | + |
| 247 | + ```bash |
| 248 | + cd server |
| 249 | + uv run server.py |
| 250 | + ``` |
| 251 | + |
| 252 | +2. Access the UI: |
| 253 | + - Open http://localhost:7860 in your browser |
| 254 | + - Or use your configured custom port |
| 255 | + |
| 256 | +3. Test WebRTC connectivity: |
| 257 | + - Click "Connect" in the UI |
| 258 | + - Allow microphone permissions when prompted |
| 259 | + - Speak to the agent - you should hear a voice response |
| 260 | + - Verify connection type: |
| 261 | + - Open browser DevTools (F12 -> Console tab) |
| 262 | + - Type `chrome://webrtc-internals` in address bar (Chrome) or `about:webrtc` (Firefox) for detailed stats |
| 263 | + - Look for "Selected candidate pair" showing protocol (`udp` for VPC, `tcp` for PUBLIC) and type (`relay` for TURN) |
| 264 | + - For log monitoring, see the next section below |
| 265 | + |
| 266 | +## KVS Considerations |
| 267 | + |
| 268 | +- **Cost:** Each active signaling channel costs $0.03/month. At low to moderate volume, this is negligible. |
| 269 | +- **Rate limit:** `GetIceServerConfig` is limited to 5 transactions per second (TPS) per channel. For high-volume deployments exceeding 100,000 sessions per month, implement a channel pooling strategy where you distribute requests across multiple channels: `channels_needed = ceil(peak_new_sessions_per_second / 5)`. |
| 270 | +- **No PrivateLink:** The VPC still requires internet egress (via NAT Gateway) to reach KVS TURN endpoints. |
| 271 | +- **Credential lifetime:** KVS TURN credentials are temporary and auto-rotated, so you do not need to manage credential rotation. |
| 272 | + |
| 273 | +## Monitoring and Troubleshooting |
| 274 | + |
| 275 | +### View Intermediary Server Logs |
| 276 | + |
| 277 | +The intermediary server (`server.py`) proxies WebRTC signaling between the browser client and AgentCore Runtime. Check the terminal where the server is already running (from step 1 above). |
| 278 | + |
| 279 | +Look for: |
| 280 | + |
| 281 | +- WebRTC SDP offers and answers |
| 282 | +- ICE candidate exchanges showing protocol (`udp`/`tcp`) and type (`relay`/`host`) |
| 283 | +- KVS TURN credential retrieval logs |
| 284 | +- Connection events and errors |
| 285 | + |
| 286 | +### View Agent Logs |
| 287 | + |
| 288 | +Use the log-tailing command provided during deployment: |
| 289 | + |
| 290 | +```bash |
| 291 | +# Replace with your actual command |
| 292 | +aws logs tail /aws/bedrock-agentcore/runtimes/bot1-0uJkkT7QHC-DEFAULT --log-stream-name-prefix "2025/11/19/[runtime-logs]" --follow |
| 293 | +``` |
| 294 | + |
| 295 | +If you don't have that command handy, no worries. Just run: |
| 296 | + |
| 297 | +```bash |
| 298 | +uv run agentcore status |
| 299 | +``` |
| 300 | + |
| 301 | +## Test Agent Manually |
| 302 | + |
| 303 | +Test the agent using the AWS CLI: |
| 304 | + |
| 305 | +```bash |
| 306 | +uv run agentcore invoke \ |
| 307 | + --session-id user-123456-conversation-12345679 \ |
| 308 | + '{ |
| 309 | + "sdp": "YOUR_OFFER", |
| 310 | + "type": "offer" |
| 311 | +}' |
| 312 | +``` |
| 313 | + |
| 314 | +> This will only allow you to see that the Pipecat agent has started, but you won't be able to hear or send audio. So it is only useful for troubleshooting. |
| 315 | +
|
| 316 | +## Cleanup |
| 317 | + |
| 318 | +Remove your agent: |
| 319 | + |
| 320 | +```bash |
| 321 | +./scripts/destroy.sh |
| 322 | +``` |
| 323 | + |
| 324 | +If using VPC mode, remove VPC resources: |
| 325 | + |
| 326 | +```bash |
| 327 | +./scripts/cleanup-vpc.sh |
| 328 | +``` |
| 329 | + |
| 330 | +Optionally, delete the KVS signaling channel: |
| 331 | + |
| 332 | +```bash |
| 333 | +aws kinesisvideo delete-signaling-channel \ |
| 334 | + --channel-arn $(aws kinesisvideo describe-signaling-channel \ |
| 335 | + --channel-name voice-agent-turn \ |
| 336 | + --query 'ChannelInfo.ChannelARN' \ |
| 337 | + --output text) \ |
| 338 | + --region us-west-2 |
| 339 | +``` |
| 340 | + |
| 341 | +## Local Development |
| 342 | + |
| 343 | +For testing, it may be helpful to run your bot locally without having to deploy to AgentCore. |
| 344 | + |
| 345 | +First, ensure that your agent's `.env` file specifies the necessary variables for local development (placeholders should already be there, from env.example). |
| 346 | + |
| 347 | +Then, run your bot in local dev mode: |
| 348 | + |
| 349 | +```bash |
| 350 | +PIPECAT_LOCAL_DEV=1 uv run pipecat-agent.py |
| 351 | +``` |
| 352 | + |
| 353 | +## Additional Resources |
| 354 | + |
| 355 | +- [Amazon Bedrock AgentCore Developer Guide](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/what-is-bedrock-agentcore.html) |
| 356 | +- [Amazon Kinesis Video Streams Developer Guide](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/what-is-amazon-kinesis-video-streams.html) |
| 357 | +- [GetIceServerConfig API Reference](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_signaling_GetIceServerConfig.html) |
0 commit comments