实时语音代理
实时代理通过双向音频流实现与 AI 助手的语音交互。adk-realtime crate 提供了一个统一的接口,用于构建支持 OpenAI's Realtime API 和 Google's Gemini Live API 的语音代理。
概述
实时代理与基于文本的 LlmAgent 在几个关键方面有所不同:
| 功能 | LlmAgent | RealtimeAgent |
|---|---|---|
| 输入 | 文本 | 音频/文本 |
| 输出 | 文本 | 音频/文本 |
| 连接 | HTTP 请求 | WebSocket |
| 延迟 | 请求/响应 | 实时流 |
| VAD | 不适用 | 服务器端语音检测 |
架构
┌─────────────────────────────────────────┐
│ 代理特质 │
│ (name, description, run, sub_agents) │
└────────────────┬────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌──────▼──────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐
│ LlmAgent │ │ RealtimeAgent │ │ SequentialAgent │
│ (基于文本)│ │ (基于语音) │ │ (工作流) │
└─────────────┘ └───────────────────┘ └───────────────────┘
RealtimeAgent 实现了与 LlmAgent 相同的 Agent 特质,共享以下功能:
- 指令(静态和动态)
- 工具注册和执行
- 回调(before_agent, after_agent, before_tool, after_tool)
- 子代理切换
快速入门
安装
添加到你的 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(())
}
支持的提供商
| 提供商 | Model | Feature Flag | 音频格式 |
|---|---|---|---|
| OpenAI | gpt-4o-realtime-preview-2024-12-17 | openai | PCM16 24kHz |
| OpenAI | gpt-realtime | openai | PCM16 24kHz |
gemini-2.0-flash-live-preview-04-09 | gemini | PCM16 16kHz/24kHz |
注意:
gpt-realtime是 OpenAI 最新的实时模型,具有改进的语音质量、情感和函数调用能力。
RealtimeAgent Builder
RealtimeAgentBuilder 提供了一个流畅的 API,用于配置 Agent:
let agent = RealtimeAgent::builder("assistant")
// 必需
.model(model)
// 指令 (与 LlmAgent 相同)
.instruction("You are helpful.")
.instruction_provider(|ctx| format!("User: {}", ctx.user_name()))
// 语音设置
.voice("alloy") // 选项:alloy, coral, sage, shimmer 等
// 语音活动检测
.server_vad() // 使用默认值
.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,
})
// 工具 (与 LlmAgent 相同)
.tool(Arc::new(weather_tool))
.tool(Arc::new(search_tool))
// 用于交接的子Agent
.sub_agent(booking_agent)
.sub_agent(support_agent)
// 回调 (与 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) })
// 实时特定回调
.on_audio(|audio_chunk| { /* 播放音频 */ })
.on_transcript(|text| { /* 显示转录 */ })
.build()?;
语音活动检测 (VAD)
VAD 通过检测用户何时开始和停止说话,实现自然的对话流程。
服务端 VAD (推荐)
let agent = RealtimeAgent::builder("assistant")
.model(model)
.server_vad() // 使用合理默认值
.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), // 允许打断助理
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()), // 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 会自动处理移交。
音频格式
| 格式 | 采样率 | 位深 | 声道 | 用例 |
|---|---|---|---|---|
| PCM16 | 24000 Hz | 16 | 单声道 | OpenAI (默认) |
| PCM16 | 16000 Hz | 16 | 单声道 | Gemini 输入 |
| G711 u-law | 8000 Hz | 8 | 单声道 | 电话系统 |
| G711 A-law | 8000 Hz | 8 | 单声道 | 电话系统 |
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 | 响应已完成 |
SpeechStarted | VAD 检测到语音开始 |
SpeechStopped | VAD 检测到语音结束 |
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
最佳实践
- 使用服务器VAD:让服务器处理语音检测以降低延迟
- 处理中断:启用
interrupt_response以实现自然对话 - 保持指令简洁:语音响应应简短
- 首先用文本测试:在添加音频之前,先用文本调试你的 Agent 逻辑
- 优雅地处理错误:网络问题在使用 WebSocket 连接时很常见
与 OpenAI Agents SDK 的比较
adk-rust 的实时实现遵循 OpenAI Agents SDK 模式:
| 功能 | OpenAI SDK | adk-rust |
|---|---|---|
| Agent 基类 | Agent | Agent trait |
| 实时 Agent | RealtimeAgent | RealtimeAgent |
| 工具 | 函数定义 | Tool trait + ToolDefinition |
| 切换 | transfer_to_agent | sub_agents + 自动生成的工具 |
| 回调 | 钩子 | before_* / after_* callbacks |