实时语音代理

实时代理通过双向音频流实现基于语音的 AI 助手交互。adk-realtime crate 提供了统一接口,用于构建支持语音的代理,这些代理可与 OpenAI 的 Realtime API 和 Google 的 Gemini Live API 协同工作。

概述

实时代理在几个关键方面不同于基于文本的 LlmAgents:

功能LlmAgentRealtimeAgent
输入文本音频/文本
输出文本音频/文本
连接HTTP 请求WebSocket
延迟请求/响应实时流式传输
VAD不适用服务端语音检测

架构

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

RealtimeAgent 实现了与 LlmAgent 相同的 Agent trait,共享以下内容:

  • 指令(静态和动态)
  • 工具注册与执行
  • 回调(before_agent、after_agent、before_tool、after_tool)
  • 子代理交接

快速开始

安装

添加到你的 Cargo.toml:

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

# For Vertex AI Live (Google Cloud with ADC auth)
# adk-realtime = { version = "2.1.0", features = ["vertex-live"] }

# For LiveKit WebRTC bridge
# adk-realtime = { version = "2.1.0", features = ["livekit"] }

# For all transports (except WebRTC which needs cmake)
# adk-realtime = { version = "2.1.0", features = ["full"] }

基本用法

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-realtime-2.1")
    );

    // 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(())
}

支持的提供商

提供商模型传输方式功能标志音频格式
OpenAIgpt-realtime-2.1WebSocketopenaiPCM16 24kHz
OpenAIgpt-realtime-2.1WebRTCopenai-webrtcOpus
Googlegemini-3.1-flash-live-previewWebSocketgeminiPCM16 16kHz/24kHz
Google通过 Vertex AI 使用 GeminiWebSocket + OAuth2vertex-livePCM16 16kHz/24kHz
LiveKit任意(连接到 Gemini/OpenAI)WebRTClivekitPCM16

注意:gpt-realtime-2.1 是 ADK-Rust 的当前 OpenAI 实时默认值。

传输选项

ADK-Realtime 支持多个传输层:

  • WebSocket(默认):直接连接到 OpenAI 或 Gemini。简单、低延迟、适用范围广。
  • Vertex AI Live:通过 Google Cloud 使用 OAuth2 身份验证(应用默认凭据)连接到 Gemini。需要企业身份验证和 GCP 集成时使用。
  • LiveKit WebRTC:生产级 WebRTC 桥接。通过 LiveKit 服务器路由音频,适用于可扩展的多参与者场景。
  • OpenAI WebRTC:与 OpenAI 的直接 WebRTC 连接,支持 Opus 编解码器和数据通道。构建 Opus C 库需要 cmake。

RealtimeAgent 构建器

RealtimeAgentBuilder 提供了用于配置代理的流式 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 通过检测用户开始和停止说话的时间,实现自然的对话流程。

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),           // Speech detection sensitivity (0.0-1.0)
    prefix_padding_ms: Some(300),   // Audio to include before speech
    silence_duration_ms: Some(500), // Silence before ending turn
    interrupt_response: Some(true), // Allow interrupting assistant
    eagerness: None,                // For SemanticVad mode
};

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

语义 VAD(Gemini)

对于 Gemini 模型,可以使用会考虑语义的语义 VAD:

let vad = VadConfig {
    mode: VadMode::SemanticVad,
    eagerness: Some("high".to_string()),  // low, medium, high
    ..Default::default()
};

工具调用

实时代理支持在语音对话期间调用工具:

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()?;

当模型调用 transfer_to_agent 时,RealtimeRunner 会自动处理交接。

音频格式

格式采样率位数声道使用场景
PCM1624000 Hz16单声道OpenAI(默认)
PCM1616000 Hz16单声道Gemini 输入
G711 u-law8000 Hz8单声道电话
G711 A-law8000 Hz8单声道电话
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更新配置

Vertex AI Live(Google Cloud)

通过 Vertex AI 使用企业身份验证(ADC、服务帐号、WIF)连接到 Gemini Live:

use adk_realtime::gemini::{GeminiLiveBackend, GeminiRealtimeModel};
use adk_realtime::{RealtimeConfig, RealtimeModel};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let project_id = std::env::var("GOOGLE_CLOUD_PROJECT")?;
    let region = std::env::var("GOOGLE_CLOUD_REGION")
        .unwrap_or_else(|_| "us-central1".to_string());

    // Use Application Default Credentials
    let credentials = google_cloud_auth::credentials::Builder::default()
        .build()
        .await?;

    let backend = GeminiLiveBackend::Vertex { credentials, region, project_id };
    let model = GeminiRealtimeModel::new(backend, "models/gemini-3.1-flash-live-preview");

    let config = RealtimeConfig::default()
        .with_instruction("You are a helpful voice assistant.");

    let session = model.connect(config).await?;
    session.send_text("Hello from Vertex AI!").await?;
    session.create_response().await?;

    // Process events...
    Ok(())
}

此外,还提供了一个用于 ADC 的便捷构造函数:

let model = GeminiRealtimeModel::vertex_adc(
    "us-central1",
    "my-project-id",
    "models/gemini-3.1-flash-live-preview",
).await?;

使用工具调用的 Vertex AI Live

vertex_live_tools 示例演示了通过 Vertex AI Live 会话进行函数调用:

use adk_realtime::config::ToolDefinition;
use adk_realtime::events::ToolResponse;
use serde_json::json;

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

let config = RealtimeConfig::default()
    .with_tools(tools)
    .with_instruction("Use tools to answer questions about weather.");

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

// Handle FunctionCallDone events and send ToolResponse back
while let Some(event) = session.next_event().await {
    match event? {
        ServerEvent::FunctionCallDone { call_id, name, arguments, .. } => {
            let result = match name.as_str() {
                "get_weather" => json!({"temperature": "22°C", "condition": "sunny"}),
                _ => json!({"error": "unknown tool"}),
            };
            session.send_tool_response(ToolResponse::new(&call_id, result)).await?;
        }
        ServerEvent::TextDelta { delta, .. } => print!("{delta}"),
        ServerEvent::ResponseDone { .. } => break,
        _ => {}
    }
}

功能标志

功能依赖项用例
vertex-livegemini + google-cloud-auth使用 ADC/服务账号身份验证的 Vertex AI Live
livekitlivekit + livekit-apiLiveKit WebRTC 桥接
openai-webrtcopenai + str0m + audiopus使用 Opus 的 OpenAI WebRTC(需要 cmake)
fullopenai + gemini + vertex-live + livekit除 WebRTC 外的所有传输方式
full-webrtcfull + openai-webrtc全部内容(需要 cmake)

LiveKit WebRTC 桥接

对于生产环境中的语音应用,LiveKit 桥接会通过 LiveKit 服务器路由音频,以支持可扩展的多参与者场景。

LiveKitConfig

安全地配置 LiveKit 凭据。API 密钥和机密使用 secrecy::SecretString 存储,并在 Debug 输出中进行脱敏:

use adk_realtime::livekit::{LiveKitConfig, LiveKitRoomBuilder};

let config = LiveKitConfig::new(
    "wss://your-server.livekit.cloud",
    std::env::var("LIVEKIT_API_KEY")?,
    std::env::var("LIVEKIT_API_SECRET")?,
)?;

LiveKitConfig::new() 会验证 URL 格式,并在构造时拒绝空凭据。

LiveKitRoomBuilder

用于连接到 LiveKit 房间的类型状态构建器。identity 字段在编译时为必需项——只有设置该字段后,connect() 才可用:

let bundle = LiveKitRoomBuilder::new(config)
    .identity("my-agent")           // required — enables connect()
    .name("Voice Agent")            // optional display name
    .room_name("session-room-123")  // optional — auto-generated if omitted
    .auto_subscribe(true)           // subscribe to remote tracks
    .with_audio(24_000, 1)          // publish a local audio track (sample rate, channels)
    .connect()
    .await?;

// The bundle contains everything you need
let room = bundle.room;
let mut events = bundle.events;
let audio_source = bundle.audio_source;  // for publishing audio
let audio_track = bundle.audio_track;

音频桥接

使用桥接工具将 LiveKit 音频连接到 RealtimeRunner:

use adk_realtime::livekit::{LiveKitEventHandler, bridge_input};

// Wrap your event handler to publish model audio to LiveKit
let lk_handler = LiveKitEventHandler::new(inner_handler, audio_source, 24000, 1);

// Bridge participant audio from LiveKit into the RealtimeRunner
tokio::spawn(bridge_input(remote_track, runner));

示例

运行随附的示例:

# OpenAI Realtime (WebSocket)
cargo run -p adk-realtime --example openai_session_update --features openai

# Vertex AI Live (requires gcloud auth application-default login)
cargo run -p adk-realtime --example vertex_live_voice --features vertex-live
cargo run -p adk-realtime --example vertex_live_tools --features vertex-live

# LiveKit Bridge (requires LiveKit server)
cargo run -p adk-realtime --example livekit_bridge --features livekit,openai
cargo run -p adk-realtime --example livekit_gemini_bridge --features livekit,gemini

# Debug utilities
cargo run -p adk-realtime --example debug_gemini --features gemini
cargo run -p adk-realtime --example debug_livekit_auth --features livekit

# OpenAI WebRTC (requires cmake)
cargo run -p adk-realtime --example openai_webrtc --features openai-webrtc

最佳实践

  1. 使用服务器 VAD:让服务器处理语音检测,以降低延迟
  2. 处理打断:启用 interrupt_response,实现自然的对话
  3. 保持指令简洁:语音响应应简短
  4. 先使用文本进行测试:在添加音频之前,先使用文本调试代理逻辑
  5. 妥善处理错误:WebSocket 连接中经常会出现网络问题

与 OpenAI 代理 SDK 的比较

ADK-Rust 的实时实现遵循 OpenAI 代理 SDK 模式:

功能OpenAI SDKADK-Rust
智能体基类AgentAgent trait
实时智能体RealtimeAgentRealtimeAgent
工具函数定义Tool trait + ToolDefinition
交接transfer_to_agentsub_agents + 自动生成的工具
回调钩子before_* / after_* 回调

上一页:← 图代理 | 下一页:模型提供商 →

实时语音代理 - ADK-Rust 文档 | ADK-Rust