リアルタイム音声エージェント
リアルタイムエージェントは、双方向オーディオストリーミングを使用してAIアシスタントとの音声ベースの対話を可能にします。adk-realtime crate は、OpenAIの Realtime API および Googleの Gemini Live API と連携する音声対応エージェントを構築するための統一されたインターフェースを提供します。
概要
リアルタイムエージェントは、テキストベースの LlmAgent といくつかの重要な点で異なります。
| 特徴 | LlmAgent | RealtimeAgent |
|---|---|---|
| 入力 | テキスト | 音声/テキスト |
| 出力 | テキスト | 音声/テキスト |
| 接続 | HTTPリクエスト | WebSocket |
| レイテンシ | リクエスト/レスポンス | リアルタイムストリーミング |
| VAD | N/A | サーバー側音声検出 |
アーキテクチャ
┌─────────────────────────────────────────┐
│ 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 = "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")?;
// リアルタイムモデルを作成
let model: Arc<dyn RealtimeModel> = Arc::new(
OpenAIRealtimeModel::new(&api_key, "gpt-4o-realtime-preview-2024-12-17")
);
// リアルタイムエージェントを構築
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()?;
// または低レベルのsession APIを直接使用
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?;
// テキストを送信し、応答を取得
session.send_text("Hello!").await?;
session.create_response().await?;
// イベントを処理
while let Some(event) = session.next_event().await {
match event? {
ServerEvent::TextDelta { delta, .. } => print!("{}", delta),
ServerEvent::AudioDelta { delta, .. } => {
// 音声の再生 (deltaはbase64エンコードされたPCM)
}
ServerEvent::ResponseDone { .. } => break,
_ => {}
}
}
Ok(())
}
サポートされているプロバイダー
| プロバイダー | モデル | フィーチャーフラグ | オーディオ形式 |
|---|---|---|---|
| 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の最新のリアルタイムモデルであり、音声品質、感情、FunctionTool呼び出し機能が向上しています。
RealtimeAgent ビルダー
RealtimeAgentBuilderは、Agentを設定するための流暢なAPIを提供します。
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 など
// 音声活動検出 (VAD)
.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,
})
// Tool (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()?;
セマンティックVAD (Gemini)
Geminiモデルの場合、意味を考慮するセマンティックVADを使用できます。
let vad = VadConfig {
mode: VadMode::SemanticVad,
eagerness: Some("high".to_string()), // low、medium、high
..Default::default()
};
ツール呼び出し
リアルタイム 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?;
}
_ => {}
}
}
マルチ Agent ハンドオフ
専門の Agent 間で会話を転送します。
// 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 | Tool 呼び出しリクエスト |
ResponseDone | 応答完了 |
SpeechStarted | VADが音声の開始を検出 |
SpeechStopped | VADが音声の終了を検出 |
Error | エラー発生 |
クライアントイベント
| イベント | 説明 |
|---|---|
AudioInput | オーディオチャンクを送信 |
AudioCommit | オーディオバッファをコミット |
ItemCreate | テキストまたは Tool 応答を送信 |
CreateResponse | 応答をリクエスト |
CancelResponse | 現在の応答をキャンセル |
SessionUpdate | 設定を更新 |
例
含まれている例を実行します。
# 基本的なテキストのみのセッション
cargo run --example realtime_basic --features realtime-openai
# VAD付き音声アシスタント
cargo run --example realtime_vad --features realtime-openai
# Tool Calling
cargo run --example realtime_tools --features realtime-openai
# マルチ Agent ハンドオフ
cargo run --example realtime_handoff --features realtime-openai
ベストプラクティス
- Server 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_* コールバック |
前へ: ← Graph Agents | 次へ: モデルプロバイダー →