リアルタイム音声エージェント
リアルタイムエージェントは、双方向音声ストリーミングを使用して、AI アシスタントとの音声ベースのインタラクションを可能にします。adk-realtime crate は、OpenAI の Realtime API および Google の Gemini Live API と連携する、音声対応エージェントを構築するための統一インターフェースを提供します。
概要
リアルタイムエージェントは、テキストベースの LlmAgents とは、いくつかの重要な点で異なります。
| 機能 | LlmAgent | RealtimeAgent |
|---|---|---|
| 入力 | テキスト | 音声/テキスト |
| 出力 | テキスト | 音声/テキスト |
| 接続 | HTTP リクエスト | WebSocket |
| 遅延 | リクエスト/レスポンス | リアルタイムストリーミング |
| VAD | 該当なし | サーバー側音声検出 |
アーキテクチャ
┌─────────────────────────────────────────┐
│ Agent Trait │
│ (name, description, run, sub_agents) │
└────────────────┬────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌──────▼──────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐
│ LlmAgent │ │ RealtimeAgent │ │ SequentialAgent │
│ (text-based)│ │ (voice-based) │ │ (workflow) │
└─────────────┘ └───────────────────┘ └───────────────────┘
RealtimeAgent は LlmAgent と同じ Agent trait を実装しており、以下を共有します。
- Instructions(静的および動的)
- Tool の登録と実行
- Callbacks(before_agent、after_agent、before_tool、after_tool)
- Sub-agent へのハンドオフ
クイックスタート
インストール
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(())
}
サポート対象プロバイダー
| プロバイダー | モデル | トランスポート | 機能フラグ | オーディオ形式 |
|---|---|---|---|---|
| OpenAI | gpt-realtime-2.1 | WebSocket | openai | PCM16 24kHz |
| OpenAI | gpt-realtime-2.1 | WebRTC | openai-webrtc | Opus |
gemini-3.1-flash-live-preview | WebSocket | gemini | PCM16 16kHz/24kHz | |
| Vertex AI 経由の Gemini | WebSocket + OAuth2 | vertex-live | PCM16 16kHz/24kHz | |
| LiveKit | 任意(Gemini/OpenAIへのブリッジ) | WebRTC | livekit | PCM16 |
注:
gpt-realtime-2.1は ADK-Rust の現在の OpenAI リアルタイムのデフォルトです。
トランスポート オプション
ADK-Realtime は複数のトランスポート層をサポートします。
- WebSocket(デフォルト):OpenAI または Gemini への直接接続。シンプルで低レイテンシー、どこでも動作します。
- Vertex AI Live:OAuth2 認証(Application Default Credentials)を使用し、Google Cloud 経由で Gemini に接続します。エンタープライズ認証と GCP 統合が必要な場合に使用します。
- LiveKit WebRTC:本番環境向けの WebRTC ブリッジ。スケーラブルなマルチパーティシナリオ向けに、音声を LiveKit サーバー経由でルーティングします。
- OpenAI WebRTC:Opus コーデックとデータチャネルを使用した、OpenAI への直接 WebRTC 接続。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 は、ユーザーが話し始めたときと話し終えたときを検出することで、自然な会話の流れを実現します。
サーバー 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 がハンドオフを自動的に処理します。
オーディオ形式
| 形式 | サンプルレート | ビット数 | チャンネル | 用途 |
|---|---|---|---|---|
| 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 | 設定を更新 |
Vertex AI Live(Google Cloud)
エンタープライズ認証(ADC、サービスアカウント、WIF)を使用して、Vertex AI 経由で 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-live | gemini + google-cloud-auth | Vertex AI Live と ADC/サービスアカウント認証 |
livekit | livekit + livekit-api | LiveKit WebRTC ブリッジ |
openai-webrtc | openai + str0m + audiopus | Opus を使用(cmake が必要)する OpenAI WebRTC |
full | openai + gemini + vertex-live + livekit | WebRTC 以外のすべてのトランスポート |
full-webrtc | full + 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ルームに接続するためのtypestateビルダーです。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
ベストプラクティス
- Server VADを使用する: 低レイテンシーを実現するため、音声検出をサーバーに任せます
- 割り込みを処理する: 自然な会話のために
interrupt_responseを有効にします - 指示を簡潔に保つ: 音声応答は短くします
- まずテキストでテストする: 音声を追加する前に、テキストを使ってエージェントのロジックをデバッグします
- エラーを適切に処理する: WebSocket接続ではネットワークの問題がよく発生します
OpenAIエージェントSDKとの比較
ADK-Rustのリアルタイム実装は、OpenAIエージェントSDKのパターンに従っています。
| 機能 | OpenAI SDK | ADK-Rust |
|---|---|---|
| エージェントの基底クラス | Agent | Agent トレイト |
| リアルタイムエージェント | RealtimeAgent | RealtimeAgent |
| ツール | 関数定義 | Tool trait + ToolDefinition |
| 引き継ぎ | transfer_to_agent | sub_agents + 自動生成ツール |
| コールバック | フック | before_* / after_* コールバック |
前へ: ← グラフエージェント | 次へ: モデルプロバイダー →