실시간 음성 에이전트

실시간 에이전트는 양방향 오디오 스트리밍을 사용하여 AI 비서와 음성 기반 상호 작용을 가능하게 합니다. adk-realtime 크레이트는 OpenAI의 Realtime API 및 Google의 Gemini Live API와 함께 작동하는 음성 지원 에이전트를 구축하기 위한 통합 인터페이스를 제공합니다.

개요

실시간 에이전트는 여러 주요 방식에서 텍스트 기반 LlmAgent와 다릅니다.

기능LlmAgentRealtimeAgent
입력텍스트오디오/텍스트
출력텍스트오디오/텍스트
연결HTTP 요청WebSocket
지연 시간요청/응답실시간 스트리밍
VAD해당 없음서버 측 음성 감지

아키텍처

              ┌─────────────────────────────────────────┐
              │              Agent Trait                │
              │  (name, description, run, sub_agents)   │
              └────────────────┬────────────────────────┘
                               │
       ┌───────────────────────┼───────────────────────┐
       │                       │                       │
┌──────▼──────┐      ┌─────────▼─────────┐   ┌─────────▼─────────┐
│  LlmAgent   │      │  RealtimeAgent    │   │  SequentialAgent  │
│ (text-based)│      │  (voice-based)    │   │   (workflow)      │
└─────────────┘      └───────────────────┘   └───────────────────┘

RealtimeAgentLlmAgent와 동일한 Agent trait을 구현하며 다음을 공유합니다.

  • 지침 (정적 및 동적)
  • Tool 등록 및 실행
  • 콜백 (before_agent, after_agent, before_tool, after_tool)
  • Sub-agent 핸드오프

빠른 시작

설치

Cargo.toml에 추가:

[dependencies]
adk-realtime = { version = "0.2.0", features = ["openai"] }

기본 사용법

use adk_realtime::{
    RealtimeAgent, RealtimeModel, RealtimeConfig, ServerEvent,
    openai::OpenAIRealtimeModel,
};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("OPENAI_API_KEY")?;

    // Create the realtime model
    let model: Arc<dyn RealtimeModel> = Arc::new(
        OpenAIRealtimeModel::new(&api_key, "gpt-4o-realtime-preview-2024-12-17")
    );

    // Build the realtime agent
    let agent = RealtimeAgent::builder("voice_assistant")
        .model(model.clone())
        .instruction("You are a helpful voice assistant. Be concise.")
        .voice("alloy")
        .server_vad()  // Enable voice activity detection
        .build()?;

    // Or use the low-level session API directly
    let config = RealtimeConfig::default()
        .with_instruction("You are a helpful assistant.")
        .with_voice("alloy")
        .with_modalities(vec!["text".to_string(), "audio".to_string()]);

    let session = model.connect(config).await?;

    // Send text and get response
    session.send_text("Hello!").await?;
    session.create_response().await?;

    // Process events
    while let Some(event) = session.next_event().await {
        match event? {
            ServerEvent::TextDelta { delta, .. } => print!("{}", delta),
            ServerEvent::AudioDelta { delta, .. } => {
                // Play audio (delta is base64-encoded PCM)
            }
            ServerEvent::ResponseDone { .. } => break,
            _ => {}
        }
    }

    Ok(())
}

지원되는 제공자

제공자모델Feature Flag오디오 형식
OpenAIgpt-4o-realtime-preview-2024-12-17openaiPCM16 24kHz
OpenAIgpt-realtimeopenaiPCM16 24kHz
Googlegemini-2.0-flash-live-preview-04-09geminiPCM16 16kHz/24kHz

참고: gpt-realtime은 향상된 음성 품질, 감정, FunctionTool 호출 기능을 갖춘 OpenAI의 최신 실시간 모델입니다.

RealtimeAgent Builder

RealtimeAgentBuilder는 Agent를 구성하기 위한 fluent API를 제공합니다:

let agent = RealtimeAgent::builder("assistant")
    // Required
    .model(model)

    // Instructions (same as LlmAgent)
    .instruction("You are helpful.")
    .instruction_provider(|ctx| format!("User: {}", ctx.user_name()))

    // Voice settings
    .voice("alloy")  // Options: alloy, coral, sage, shimmer, etc.

    // Voice Activity Detection
    .server_vad()  // Use defaults
    .vad(VadConfig {
        mode: VadMode::ServerVad,
        threshold: Some(0.5),
        prefix_padding_ms: Some(300),
        silence_duration_ms: Some(500),
        interrupt_response: Some(true),
        eagerness: None,
    })

    // Tools (same as LlmAgent)
    .tool(Arc::new(weather_tool))
    .tool(Arc::new(search_tool))

    // Sub-agents for handoffs
    .sub_agent(booking_agent)
    .sub_agent(support_agent)

    // Callbacks (same as LlmAgent)
    .before_agent_callback(|ctx| async { Ok(()) })
    .after_agent_callback(|ctx, event| async { Ok(()) })
    .before_tool_callback(|ctx, tool, args| async { Ok(None) })
    .after_tool_callback(|ctx, tool, result| async { Ok(result) })

    // Realtime-specific callbacks
    .on_audio(|audio_chunk| { /* play audio */ })
    .on_transcript(|text| { /* show transcript */ })

    .build()?;

음성 활동 감지 (VAD)

VAD는 사용자가 말을 시작하고 멈추는 시점을 감지하여 자연스러운 대화 흐름을 가능하게 합니다.

Server VAD (권장)

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .server_vad()  // Uses sensible defaults
    .build()?;

사용자 지정 VAD 구성

use adk_realtime::{VadConfig, VadMode};

let vad = VadConfig {
    mode: VadMode::ServerVad,
    threshold: Some(0.5),           // 음성 감지 민감도 (0.0-1.0)
    prefix_padding_ms: Some(300),   // 음성 전에 포함할 오디오
    silence_duration_ms: Some(500), // 발화 종료 전 침묵 지속 시간
    interrupt_response: Some(true), // Agent 중단 허용
    eagerness: None,                // SemanticVad 모드용
};

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .vad(vad)
    .build()?;

Semantic VAD (Gemini)

Gemini 모델의 경우 의미를 고려하는 semantic VAD를 사용할 수 있습니다:

let vad = VadConfig {
    mode: VadMode::SemanticVad,
    eagerness: Some("high".to_string()),  // 낮음, 중간, 높음
    ..Default::default()
};

도구 호출

Realtime agent는 음성 대화 중에 도구 호출을 지원합니다:

use adk_realtime::{config::ToolDefinition, ToolResponse};
use serde_json::json;

// Define tools
let tools = vec![
    ToolDefinition {
        name: "get_weather".to_string(),
        description: Some("Get weather for a location".to_string()),
        parameters: Some(json!({
            "type": "object",
            "properties": {
                "location": { "type": "string" }
            },
            "required": ["location"]
        })),
    },
];

let config = RealtimeConfig::default()
    .with_tools(tools)
    .with_instruction("Use tools to help the user.");

let session = model.connect(config).await?;

// Handle tool calls in the event loop
while let Some(event) = session.next_event().await {
    match event? {
        ServerEvent::FunctionCallDone { call_id, name, arguments, .. } => {
            // Execute the tool
            let result = execute_tool(&name, &arguments);

            // Send the response
            let response = ToolResponse::new(&call_id, result);
            session.send_tool_response(response).await?;
        }
        _ => {}
    }
}

다중 에이전트 핸드오프

전문 에이전트 간에 대화를 전송합니다:

// Create sub-agents
let booking_agent = Arc::new(RealtimeAgent::builder("booking_agent")
    .model(model.clone())
    .instruction("Help with reservations.")
    .build()?);

let support_agent = Arc::new(RealtimeAgent::builder("support_agent")
    .model(model.clone())
    .instruction("Help with technical issues.")
    .build()?);

// Create main agent with sub-agents
let receptionist = RealtimeAgent::builder("receptionist")
    .model(model)
    .instruction(
        "Route customers: bookings → booking_agent, issues → support_agent. \
         Use transfer_to_agent tool to hand off."
    )
    .sub_agent(booking_agent)
    .sub_agent(support_agent)
    .build()?;

model이 transfer_to_agent를 호출하면, RealtimeRunner가 핸드오프를 자동으로 처리합니다.

오디오 형식

형식샘플 속도비트채널사용 사례
PCM1624000 Hz16MonoOpenAI (기본값)
PCM1616000 Hz16MonoGemini 입력
G711 u-law8000 Hz8Mono전화 통신
G711 A-law8000 Hz8Mono전화 통신
use adk_realtime::{AudioFormat, AudioChunk};

// Create audio format
let format = AudioFormat::pcm16_24khz();

// Work with audio chunks
let chunk = AudioChunk::new(audio_bytes, format);
let base64 = chunk.to_base64();
let decoded = AudioChunk::from_base64(&base64, format)?;

이벤트 유형

서버 이벤트

이벤트설명
SessionCreated연결 설정됨
AudioDelta오디오 청크 (base64 PCM)
TextDelta텍스트 응답 청크
TranscriptDelta입력 오디오 스크립트
FunctionCallDone도구 호출 요청
ResponseDone응답 완료됨
SpeechStartedVAD 음성 시작 감지됨
SpeechStoppedVAD 음성 끝 감지됨
Error오류 발생

클라이언트 이벤트

이벤트설명
AudioInput오디오 청크 전송
AudioCommit오디오 버퍼 커밋
ItemCreate텍스트 또는 도구 응답 전송
CreateResponse응답 요청
CancelResponse현재 응답 취소
SessionUpdate구성 업데이트

예시

포함된 예시를 실행합니다:

# Basic text-only session
cargo run --example realtime_basic --features realtime-openai

# Voice assistant with VAD
cargo run --example realtime_vad --features realtime-openai

# Tool calling
cargo run --example realtime_tools --features realtime-openai

# Multi-agent handoffs
cargo run --example realtime_handoff --features realtime-openai

모범 사례

  1. Server VAD 사용: 서버가 음성 감지를 처리하여 지연 시간을 줄입니다.
  2. 인터럽션 처리: interrupt_response를 활성화하여 자연스러운 대화를 구현합니다.
  3. 명령을 간결하게 유지: 음성 응답은 간결해야 합니다.
  4. 텍스트로 먼저 테스트: 오디오를 추가하기 전에 텍스트로 agent 로직을 디버그합니다.
  5. 오류를 정상적으로 처리: WebSocket 연결에서는 네트워크 문제가 흔합니다.

OpenAI Agents SDK 와(과) 비교

ADK-Rust의 실시간 구현은 OpenAI Agents SDK 패턴을 따릅니다.

기능OpenAI SDKADK-Rust
Agent 기본 클래스AgentAgent trait
실시간 agentRealtimeAgentRealtimeAgent
도구함수 정의Tool trait + ToolDefinition
핸드오프transfer_to_agentsub_agents + 자동 생성된 도구
콜백Hooksbefore_* / after_* callbacks

이전: ← Graph Agents | 다음: Model Providers →