Amazon Bedrock AgentCore

View as Markdown

This guide shows how to integrate the AgentDuet SDK with Amazon Bedrock AgentCore for hosted agent runtimes with lifecycle management, memory, governed tools, and observability around live phone agents.

This guide uses the FinAssist sample from agentduet-samples. Speech uses Amazon Nova Sonic over Bedrock.

What this integration provides

A voice agent that:

  • Starts an AgentDuet listener when the AgentCore runtime comes up
  • Answers inbound calls and bridges 24 kHz PCM to Nova Sonic 2
  • Keeps eligibility / tool-style actions reachable over AgentCore invoke
  • Can deploy the same app to AWS with agentcore deploy

Demo domain: SecureFinance loans and insurance claims over the phone.

How the pieces fit

LayerOwns
AgentCoreProcess host, lifespan, health (ping), entrypoint invoke
AgentDuetConnector, inbound calls, answer / send_audio / barge-in
Nova SonicSpeech-to-speech stream on Bedrock
Your appPrompts, eligibility rules, optional transcript upload

For a Nova-only bridge without AgentCore, see Amazon Nova Sonic.

Prerequisites

  • Python 3.12+
  • Node.js 20+ and the @aws/agentcore CLI
  • AWS credentials with Bedrock access (SSO recommended)
  • AgentDuet API key and connector UUID from agentduet.com
  • uv for the app virtualenv

Deploy also needs an IAM role with CloudFormation + AgentCore permissions (Bedrock-only access is not enough).

Step 1: Clone the sample

$git clone https://github.com/AgentDuet/agentduet-samples.git
$cd agentduet-samples/integrations/bedrock-agentcore/finassist

Layout:

finassist/
├── agentcore/ # agentcore.json, AWS targets, local secrets
└── app/
├── main.py # AgentCore entrypoint + lifespan
├── pyproject.toml
└── finassist_agent/
├── voice_service.py # AgentDuet SessionManager + call bridge
├── nova_sonic.py # Bedrock bidirectional stream
├── prompts.py
└── logic.py # Loan / claim rules

Full source: agentduet-samples / bedrock-agentcore / finassist.

Step 2: Install CLI and app deps

$npm install -g @aws/agentcore
$cd agentcore/cdk && npm install && cd ../..
$cd app && uv sync --python 3.12 && cd ..

Step 3: Configure credentials

$cp agentcore/aws-targets.json.example agentcore/aws-targets.json
$cp agentcore/.env.local.example agentcore/.env.local
  1. Set your 12-digit AWS account ID in agentcore/aws-targets.json.
  2. Fill AgentDuet secrets in agentcore/.env.local:
$AGENTDUET_API_KEY=your-connector-api-key
$AGENTDUET_CONNECTOR_UUID=your-connector-uuid
$AWS_DEFAULT_REGION=ap-southeast-1
$NOVA_SONIC_REGION=ap-northeast-1
$NOVA_SONIC_MODEL_ID=amazon.nova-2-sonic-v1:0
$NOVA_SONIC_VOICE_ID=matthew
  1. Authenticate AWS in the shell (do not commit access keys):
$export AWS_PROFILE=your-sso-profile
$aws sso login --profile your-sso-profile
$aws sts get-caller-identity

.env.local is for local agentcore dev only. Cloud deploy reads env from agentcore.json (see Step 6).

Step 4: Run locally

$agentcore dev

Call your AgentDuet connector number. FinAssist should answer as SecureFinance and walk loan or claim intake.

Optional HTTP-style checks against the same process:

$agentcore dev '{"action": "status"}'
$agentcore dev '{"action": "evaluate_loan", "amount": 15000, "annual_income": 60000, "purpose": "home repair"}'

Step 5: How AgentCore starts AgentDuet

AgentCore lifespan starts the voice listener. AgentDuet does not own process signals.

Lifespan starts the voice listener

From app/main.py:

1@asynccontextmanager
2async def lifespan(app: BedrockAgentCoreApp):
3 """Start AgentDuet voice listener when AgentCore starts."""
4 _listener_task_id = app.add_async_task(
5 "agentduet_nova_listener",
6 {"connector": os.getenv("AGENTDUET_CONNECTOR_UUID", "")},
7 )
8 _listener_task = asyncio.create_task(voice_service.run_forever())
9 yield
10 voice_service.request_shutdown()
11 # cancel listener task, complete_async_task ...
12
13app = BedrockAgentCoreApp(lifespan=lifespan)

SessionManager runs under AgentCore

From app/finassist_agent/voice_service.py:

1# install_signal_handlers=False: AgentCore owns process signals / lifespan.
2async with SessionManager(config) as sm:
3 @sm.on_incoming_call
4 async def on_call(noti: IncomingCallNotification) -> None:
5 await self.handle_incoming_call(sm, noti)
6
7 await sm.run_forever(install_signal_handlers=False)

On shutdown, request_shutdown() disconnects the SessionManager so run_forever exits cleanly.

Invoke and live audio

@app.entrypoint handles status, evaluate_loan, and evaluate_claim. Live calls go through AgentDuet events and the Nova bridge.

Step 6: Deploy to AWS

Set AGENTDUET_API_KEY and AGENTDUET_CONNECTOR_UUID in agentcore/agentcore.json envVars (placeholders ship in the sample). Then:

$agentcore deploy
$agentcore invoke '{"action": "status"}'
$agentcore logs

Notes

  • Keep call control and PCM buffering on the AgentDuet path; use invoke for structured actions only.
  • Treat call id, customer identity, and AgentCore context.session_id as separate namespaces.
  • This sample uses 24 kHz audio (CallAudioConfig(sample_rate=24000)).
  • Runtime region and Nova model region can differ (AWS_DEFAULT_REGION vs NOVA_SONIC_REGION).
  • Optional transcript upload when TRANSCRIPT_S3_BUCKET is set.