Imported from aws-samples/sample-bedrock-agentcore-twilio-agent-connect-reference (
AGENTS.md). Install upstream withnpx skills add aws-samples/sample-bedrock-agentcore-twilio-agent-connect-reference. Copyright stays with the author.
Twilio TAC + Bedrock AgentCore Reference Architecture — Workspace Guide
Project Overview
Reference architecture for building Agentic AI voice applications using Twilio Agent Connect (TAC) and Amazon Bedrock AgentCore. Demonstrates production-grade WebSocket-based streaming, Twilio Conversation Memory, Conversation Orchestrator, Conversation Intelligence, and Conversation Relay integrated with Strands agents deployed via Bedrock AgentCore CLI.
Demo application: SkyOwl Airlines with a Strands agent — support_agent (customer service agent for voice and SMS).
File References
#[[file:.env.example]] #[[file:Makefile]]
Project Structure
twilio-tac-agentcore-reference/
├── agents/ Agent source code
│ └── support_agent/ Customer service agent
│ ├── main.py Agent entrypoint (@app.entrypoint, @app.websocket)
│ ├── prompts.py System prompt
│ ├── local_tools.py DynamoDB tools (lookup_reservation, change_seat, etc.)
│ └── pyproject.toml Agent dependencies (strands-agents, boto3)
│
├── tac_server/ TAC server (dual-runtime: WebSocket + HTTP)
│ ├── main.py Server entrypoint (6 sections, ~250 lines)
│ ├── cr_config.py Conversation Relay configuration
│ └── Dockerfile Multi-stage build (PyPI dependencies)
│
├── dashboard/ Demo dashboard (FastAPI)
│ ├── app.py API endpoints (DynamoDB data, CloudWatch logs, Twilio APIs)
│ ├── templates.py Single-page HTML/CSS/JS with log viewer + data tabs
│ ├── Dockerfile Dashboard container image
│ └── requirements.txt Dashboard Python dependencies
│
├── infra/
│ ├── platform/ CDK stacks
│ │ ├── bin/cdk.ts CDK app entry point (reads .env, creates stacks)
│ │ ├── lib/data-stack.ts DynamoDB tables + IAM policies
│ │ ├── lib/server-cloudfront-stack.ts ECS + HTTP ALB + CloudFront + TAC server + Dashboard
│ │ ├── lib/server-custom-domain-stack.ts ECS + HTTPS ALB + Route 53 + ACM + TAC server + Dashboard
│ │ └── lambda/ AgentCore Gateway Lambda tools (check_refund_eligibility)
│ └── {PROJECT_NAME}/ Bedrock AgentCore CLI project (GITIGNORED, generated)
│ └── agentcore/ agentcore.json, aws-targets, CDK, deployed state
│
├── scripts/
│ ├── setup.sh Prereq check + .env validation + uv sync
│ ├── setup-agentcore.sh AgentCore project creation + BYO agents + deploy
│ ├── setup-twilio-tac.sh Create Twilio resources (Memory, CI, Orchestrator)
│ ├── destroy-twilio.sh Delete Twilio resources
│ ├── sync-agentcore-env.py Extract deployed ARNs → .env
│ ├── build-wheels.sh (legacy, unused) Build TAC SDK .whl for Docker
│ └── seed_data.py Seed DynamoDB with test data
│
├── Makefile Developer entrypoint — all make targets
├── .env.example Environment template (4 sections)
├── .env Actual config (GITIGNORED)
├── pyproject.toml Python project config + dependencies
├── uv.lock Dependency lockfile
└── AGENTS.md This file
Git-tracked vs Generated
| Directory | Tracked | Notes |
|---|---|---|
agents/ |
Yes | Agent source code |
tac_server/ |
Yes | TAC server + Dockerfile |
dashboard/ |
Yes | Dashboard app + Dockerfile |
infra/platform/ |
Yes | CDK stacks |
infra/{PROJECT_NAME}/ |
No | Generated by make setup-aws. Recreate with same command. |
wheels/ |
No | (legacy, unused) Previously generated by build-wheels.sh |
requirements.txt |
No | Generated by uv export for Docker |
Run Flows
Three ways to run. Can run any or all simultaneously (separate infrastructure, no conflicts).
| Flow | TAC Server | Agent | Infrastructure | Run |
|---|---|---|---|---|
| ngrok | Local (port 8000) | Deployed Bedrock AgentCore Runtime | Local + ngrok tunnel | make run-ngrok |
| CloudFront | ECS Fargate | Deployed Bedrock AgentCore Runtime | ECS + ALB + CloudFront | make run-cloudfront |
| Custom Domain | ECS Fargate | Deployed Bedrock AgentCore Runtime | ECS + ALB + Route 53 + ACM | make run-custom-domain |
TWILIO_VOICE_PUBLIC_DOMAIN is auto-populated by each make run-* command with the correct domain (ngrok URL, CloudFront domain, or custom domain). Do not set manually.
All make run-* commands auto-configure Twilio webhooks (voice, SMS, CI) with the deployed domain. No manual Twilio Console steps needed.
Make Targets
Setup (run once)
make setup-environment → validates .env, checks prereqs, uv sync
make setup-twilio → creates Twilio resources, syncs IDs to .env
make setup-aws → deploys Bedrock AgentCore agents + DynamoDB + seeds data
Run (choose one or both)
make run-ngrok → TAC server + dashboard locally + ngrok tunnel + auto-configure webhooks
make run-cloudfront → ECS + ALB + CloudFront + auto-configure webhooks
make run-custom-domain → ECS + ALB + Route 53 + ACM + auto-configure webhooks
Destroy
make destroy-cloudfront → remove ServerCloudFront stack
make destroy-custom-domain → remove ServerCustomDomain stack
make destroy-aws → tear down ALL AWS resources
make destroy-twilio → tear down Twilio resources
Environment Variables
Single .env at project root. Layout follows make sequence:
| Section | Contents | Set By |
|---|---|---|
| 1: Required | PROJECT_NAME, AWS_REGION, LOG_LEVEL, Twilio creds, Bedrock AgentCore memory config | Developer (manual) |
| 2: Twilio Resources [auto] | API Key/Secret, Configuration ID, voice domain, CI config, memory traits | make setup-twilio + make run-* |
| 3: AWS Setup [auto] | Runtime ARNs, Role ARNs, Memory ID | make setup-aws |
| 4: Custom Domain (optional) | HOSTED_ZONE_ID, HOSTED_ZONE_NAME, CUSTOM_DOMAIN_SUBDOMAIN | Developer (only for make run-custom-domain) |
Convention: TWILIO_* prefix for all Twilio variables. TAC SDK reads via TACConfig.from_env().
CDK Stacks
| Stack | CloudFormation Name | Deployed By | Resources |
|---|---|---|---|
| Bedrock AgentCore | AgentCore-{PROJECT}-default |
make setup-aws |
Agent runtimes, memory, IAM |
| Data | {PROJECT}-Data |
make setup-aws |
DynamoDB tables, IAM policy on agent roles |
| ServerCloudFront | {PROJECT}-ServerCloudFront |
make run-cloudfront |
ECS cluster, HTTP ALB, CloudFront, TAC server, Dashboard |
| ServerCustomDomain | {PROJECT}-ServerCustomDomain |
make run-custom-domain |
ECS cluster, HTTPS ALB, Route 53, ACM, TAC server, Dashboard |
ServerCloudFront and ServerCustomDomain are independent, self-contained stacks. Both can be deployed simultaneously. ECR repo is created by Makefile (shared between both stacks).
Bedrock AgentCore CLI Conventions
- Agent naming: Plain names (
support_agent). CDK prepends project name →{PROJECT_NAME}_support_agent. - BYO pattern:
agentcore add agent --type byo --code-location ../../agents/support_agent/ - Project creation:
agentcore createruns frominfra/, creates{PROJECT_NAME}/as child. - aws-targets.json: Auto-filled by
setup-agentcore.sh. - PROJECT_NAME: Letters and numbers only, max 32 characters.
Twilio Resources (created by make setup-twilio)
- API Key (SK...) — TAC SDK runtime auth. Display name:
{PROJECT}-api-key. - Memory Store — Conversation Memory. Async creation (polls statusUrl). Display name:
{PROJECT}-memory. - Intelligence Configuration — Real-time sentiment + post-conversation summaries. Display name:
{PROJECT}-intelligence. - Conversation Configuration — Links Memory + Intelligence. Voice, SMS, RCS capture rules. Async creation. Display name:
{PROJECT}-orchestration.
Idempotent: skips creation if resource ID already in .env. Resumable: writes .env after each step.
Retry-with-suffix: if name is taken (ghost reservation after deletion), retries with -2, -3, etc.
make run-* auto-configures:
- Maestro
statusCallbacks→https://<domain>/webhook - CI webhook →
https://<domain>/ci-webhook - Voice webhook →
https://<domain>/twiml(on the phone number directly)
Profiles are auto-created on first call when Conversation Memory identifies the caller by phone number.
Dashboard
FastAPI app providing a demo UI at /dashboard with:
- Data tab: DynamoDB reservations + flights viewer
- Twilio tab: Conversation Memory profiles, Conversation Orchestrator conversations, Conversation Intelligence results
- AWS Infrastructure tab: CloudWatch log viewer with tail functionality for ECS TAC server and Bedrock AgentCore agent logs
- Architecture tab: System diagrams
Deployed as a separate ECS service behind the same ALB, routed via /dashboard* path rule.
DynamoDB Schema
Reservations (PK: confirmation_code): first_name, last_name, email, phone_number, flight_number, departure, arrival, date, seat, ticket_status, loyalty_status
Flights (PK: flight_number): departure, arrival, departure_time, arrival_time, gate, flight_status, available_seats
Code Rules
- SDK Fidelity: Use TAC SDK and Bedrock AgentCore SDK classes directly. No custom wrappers.
- Lean code: TAC server is ~250 lines. Minimal comments, concise logging, SDK-idiomatic patterns.
- Channel-aware: Agent prompt includes voice and SMS rules. Channel context passed in payload.
- Flat code: Prefer readability over DRY. Duplication OK for self-contained modules.
- No secrets in git:
.envis gitignored..env.examplehas empty credential fields. - Port convention: 8000 (TAC server), 8899 (dashboard).
- Docker: All dependencies installed from PyPI.
Key Decisions
| Decision | Why |
|---|---|
| Two self-sufficient CDK stacks (ServerCloudFront, ServerCustomDomain) | Each stack is independent, can coexist. No mode switching, no env var tricks. |
| CloudFront as default production path | No custom domain required. Auto-generated *.cloudfront.net with TLS. Simplest setup for workshops. |
Agent code at agents/ root, not inside agentcore project |
Cleaner git. Bedrock AgentCore CLI files gitignored. |
Single .env |
One source of truth. make run-* auto-populates domain. |
TWILIO_* prefix only |
TAC SDK reads TWILIO_*. No duplicate variables. |
| ECR repo via Makefile (not CDK) | Avoids chicken-and-egg: images must exist before ECS service starts. |
| Dual-runtime pattern | Voice via WebSocket (~50ms), SMS via HTTP. Connector handles routing internally. |
| Channel-aware agent | Structured payload {prompt, channel, memoryContext}. Agent applies voice/SMS rules from system prompt. |
| Profile-based session continuity | profile_id or conversation_id for AgentCore session ID. Returning callers reconnect to same microVM. |
| Zero manual Twilio Console steps | make run-* auto-configures voice webhook, SMS statusCallbacks, and CI webhook with deployed domain. |
RequestValidator over TAC SDK's validate_twilio_webhook |
TAC SDK reconstructs URL from X-Forwarded-Proto + X-Forwarded-Host; behind CloudFront→ALB both are wrong (ALB overwrites proto to http, CloudFront replaces Host with ALB DNS). No CF/ALB setting can fix this. We use Twilio SDK's RequestValidator directly with URL built from TWILIO_VOICE_PUBLIC_DOMAIN — works across all 3 flows. Note: parse_qs requires keep_blank_values=True for Twilio form payloads (empty params like ToZip="" are included in signature computation). |
Reference Documentation
Amazon Bedrock AgentCore
- Developer Guide: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/
- AgentCore CLI: https://github.com/aws/agentcore-cli
- AgentCore CDK Construct: https://constructs.dev/packages/@aws/agentcore-cdk
- Strands Agents SDK: https://github.com/strands-agents/sdk-python
Twilio Agent Connect (TAC)
- AWS TAC Connector SDK: https://github.com/twilio/aws-twilio-agent-connect-python
- Conversation Memory API: https://www.twilio.com/docs/api/memory/v1
- Conversation Orchestrator API: https://www.twilio.com/docs/api/conversations/v2
- Conversation Intelligence API: https://www.twilio.com/docs/api/intelligence/v3
- Conversation Relay: https://www.twilio.com/docs/voice/twiml/connect/conversationrelay
License
This project is licensed under the MIT-0 License. See LICENSE.