BidiAgent — 실시간 음성
양방향 스트리밍 실시간 음성 대화
BidiAgent — 실시간 음성
양방향 스트리밍 실시간 음성 대화
에이전트와 대화한다 — 목소리로
Speech-to-Speech 양방향 음성 에이전트 — 텍스트 변환 없는 직접 처리
BidiAgent 구현
BidiAudioIO · 양방향 스트림 · 도구 통합
BidiAgent
BidiNovaSonicModel · BidiAudioIO · 도구 — 양방향 음성 에이전트의 3층 구성
BidiAgent
양방향 세션 전용 에이전트 — 도구 호출 중에도 음성 스트림을 유지합니다
- BidiNovaSonicModel — 모델 래퍼
- BidiAudioIO — 마이크·스피커 I/O
- 에이전틱 루프 — 기존 Strands와 동일
- LPCM 입력 16kHz / 출력 24kHz
모델이 toolUse를 요청하면 루프가 도구를 실행하고 결과를 음성 스트림에 주입합니다 — 대화가 끊기지 않습니다.
BidiAgent 전체 구현
BidiNovaSonicModel + BidiAudioIO + 도구를 결합한 음성 에이전트 완성 코드
핵심 포인트
- @tool
- 음성 대화 중 모델이 호출하는 도구 — 텍스트 에이전트와 동일한 데코레이터
- BidiNovaSonicModel
- Nova 2 Sonic 모델 ID + client_config(region)로 양방향 스트림 연결
- BidiAgent
- 모델 + 도구 + system_prompt 조립 — 에이전트 구성은 기존과 동일
- await agent.run()
- inputs·outputs에 마이크·스피커 연결 — asyncio 기반 양방향 세션
코드
import asyncio
from strands import tool
from strands.experimental.bidi import BidiAgent, BidiAudioIO
from strands.experimental.bidi.models import BidiNovaSonicModel
@tool
def get_weather(city: str) -> dict:
"""도시의 현재 날씨를 조회합니다."""
return {"city": city, "temp": 22, "condition": "sunny"}
async def main():
model = BidiNovaSonicModel(model_id="amazon.nova-2-sonic-v1:0",
client_config={"region": "us-east-1"})
agent = BidiAgent(model=model, tools=[get_weather],
system_prompt="You are a helpful voice assistant.")
audio_io = BidiAudioIO() # 마이크 입력 · 스피커 출력
await agent.run(inputs=[audio_io.input()], outputs=[audio_io.output()])
asyncio.run(main())
양방향 오디오 처리 흐름
마이크 입력부터 스피커 출력까지 — 실시간 양방향 오디오 파이프라인
-
1
Mic Capture
시스템 마이크에서 16kHz PCM 오디오를 512프레임 버퍼(기본값) 단위로 캡처. BidiAudioIO는 PyAudio 기반.
-
2
Audio Event 변환
16kHz LPCM 프레임을 오디오 입력 이벤트로 감싸 전송 큐에 적재. 압축 없이 원시 PCM 그대로 전달.
-
3
Stream to Model
HTTP/2 양방향 스트림으로 인코딩된 오디오 청크를 Nova Sonic에 실시간 전송. 발화 중에도 모델이 처리 시작.
-
4
Model Response
모델이 오디오 토큰으로 응답을 생성. toolUse 이벤트 발생 시 에이전틱 루프가 도구를 실행하고 결과 반환.
-
5
PCM Buffering
모델이 반환한 24kHz LPCM 오디오 청크를 버퍼링. 청크 간 끊김 없이 연속적인 재생 보장.
-
6
Speaker Output
디코딩된 PCM 오디오를 스피커로 실시간 재생. 첫 청크 도착 즉시 재생 시작하여 체감 레이턴시 최소화.
캡처부터 재생까지 전 구간이 스트리밍입니다 — 첫 청크 도착 즉시 재생을 시작해 체감 레이턴시를 최소화합니다.
BidiAgent 핵심 컴포넌트
양방향 음성 에이전트를 구성하는 3개 레이어와 역할 분담
BidiNovaSonicModel
Bedrock Nova 2 Sonic 모델 래퍼 — HTTP/2 양방향 스트림 관리
BidiAudioIO
마이크 입력 · 스피커 출력 · LPCM 스트림을 통합하는 오디오 I/O 레이어
BidiAgent(에이전틱 루프)
도구 호출 판단 · 실행 · 결과 반환 — 기존 Strands 루프와 동일
모델 래퍼 · 오디오 I/O · 에이전틱 루프 — 세 레이어의 역할 분담이 양방향 음성 에이전트의 전부입니다.
멀티 프로바이더
Gemini Live · OpenAI Realtime · 프로바이더 전환
멀티 프로바이더
Nova Sonic · Gemini Live · OpenAI Realtime — 동일 인터페이스로 전환
멀티 프로바이더
모델 클래스만 교체하면 나머지 코드는 그대로 — 프로바이더 전환의 아키텍처 변화 최소화
- BidiNovaSonicModel — HTTP/2 bidi
- BidiGeminiLiveModel — WebSocket
- BidiOpenAIRealtimeModel — WebSocket
- 도구 · BidiAudioIO — 그대로 재사용
프로바이더마다 레이턴시·언어 지원·감정 표현 수준이 다릅니다 — 한국어가 필요하면 Gemini Live·OpenAI Realtime이 현재 선택지입니다.
멀티 프로바이더 비교
SDK 클래스·프로토콜·코덱·세션 제한은 상이 — Strands 통합 인터페이스는 셋 모두 동일(실험적)
| 항목 | Nova 2 Sonic | Gemini Live | OpenAI Realtime |
|---|---|---|---|
| SDK 클래스 | BidiNovaSonicModel | BidiGeminiLiveModel | BidiOpenAIRealtimeModel |
| 전송 프로토콜 | HTTP/2 bidi stream | WebSocket | WebSocket |
| 오디오 코덱 | LPCM | 16-bit PCM (raw PCM 전용) | PCM16 / G.711 |
| 샘플레이트 | 입력 16kHz / 출력 24kHz | 16kHz / 24kHz | 24kHz |
| 도구 호출 | toolUse 이벤트 | function_call | function_call |
| 세션 제한 | 연결 8분 | 오디오 15분 (연결 ~10분, 재개 토큰 2시간) | 최대 60분 |
| Strands 통합 | ✓ (실험적) | ✓ (실험적) | ✓ (실험적) |
SDK 클래스와 프로토콜·코덱은 달라도 Strands 통합 인터페이스는 동일합니다 — 셋 모두 아직 실험적(experimental) 단계입니다.
Gemini Live 연동
BidiGeminiLiveModel로 교체 — 나머지 코드 변경 없이 프로바이더 전환
핵심 포인트
- BidiGeminiLiveModel
- 모델 클래스만 교체 — Live는 native-audio 계열 모델 사용
- client_config(api_key)
- 프로바이더 인증 — Nova Sonic의 region 자리에 API 키
- BidiAudioIO
- 오디오 I/O는 그대로 재사용 — 변경 없음
- BidiAgent
- 구성 코드는 Nova Sonic과 완전 동일 — 도구·프롬프트 유지
코드
from strands.experimental.bidi import BidiAgent, BidiAudioIO
from strands.experimental.bidi.models import BidiGeminiLiveModel
# Gemini Live 모델로 교체 — 인터페이스 동일 (Live는 native-audio 계열)
model = BidiGeminiLiveModel(
model_id="gemini-2.5-flash-native-audio-preview-09-2025",
client_config={"api_key": "GEMINI_API_KEY"},
)
audio_io = BidiAudioIO() # 오디오 I/O는 그대로 재사용
# BidiAgent 코드는 Nova Sonic과 완전 동일
agent = BidiAgent(
model=model,
tools=[get_weather, search_db],
system_prompt="You are a helpful voice assistant.",
)
텍스트 에이전트 → 음성 에이전트 전환
기존 텍스트 기반 에이전트에서 음성 에이전트로 전환 시 변경 사항
텍스트 입출력 전용 → BidiNovaSonicModel
HTTP/2 양방향 오디오 스트림
또는 REST API → BidiAudioIO
마이크+스피커+LPCM 스트림
동기 호출, 결과 텍스트 반환 → await agent.run(inputs=..., outputs=...)
비동기 세션, 실시간 스트림
@tool 데코레이터 그대로 → 변경 없음
동일한 도구를 그대로 사용
Nova Sonic · BidiAgent · WebRTC — 음성으로 대화하는 에이전트