Agentes de Voz en Tiempo Real
Los agentes en tiempo real permiten interacciones basadas en voz con asistentes de IA utilizando transmisión de audio bidireccional. La crate adk-realtime proporciona una interfaz unificada para construir agentes habilitados para voz que funcionan con la API en tiempo real de OpenAI y la API Gemini Live de Google.
Resumen
Los agentes en tiempo real difieren de los LlmAgent basados en texto de varias maneras clave:
| Característica | LlmAgent | RealtimeAgent |
|---|---|---|
| Entrada | Texto | Audio/Texto |
| Salida | Texto | Audio/Texto |
| Conexión | Solicitudes HTTP | WebSocket |
| Latencia | Solicitud/respuesta | Streaming en tiempo real |
| VAD | N/A | Detección de voz del lado del servidor |
Arquitectura
┌─────────────────────────────────────────┐
│ Agent Trait │
│ (name, description, run, sub_agents) │
└────────────────┬────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌──────▼──────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐
│ LlmAgent │ │ RealtimeAgent │ │ SequentialAgent │
│ (text-based)│ │ (voice-based) │ │ (workflow) │
└─────────────┘ └───────────────────┘ └───────────────────┘
RealtimeAgent implementa el mismo trait Agent que LlmAgent, compartiendo:
- Instrucciones (estáticas y dinámicas)
- Registro y ejecución de Tool
- Callbacks (before_agent, after_agent, before_tool, after_tool)
- Transferencias de Sub-agent
Inicio Rápido
Instalación
Agrega a tu Cargo.toml:
[dependencies]
adk-realtime = { version = "0.2.0", features = ["openai"] }
Uso Básico
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(())
}
Proveedores Compatibles
| Proveedor | Modelo | Feature Flag | Formato de Audio |
|---|---|---|---|
| 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 |
Nota:
gpt-realtimees el último modelo en tiempo real de OpenAI con calidad de voz, emoción y capacidades de llamada a funciones mejoradas.
Constructor de RealtimeAgent
El RealtimeAgentBuilder proporciona una API fluida para configurar agentes:
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()?;
Detección de Actividad de Voz (VAD)
VAD permite un flujo de conversación natural al detectar cuándo el usuario comienza y deja de hablar.
VAD del Servidor (Recomendado)
let agent = RealtimeAgent::builder("assistant")
.model(model)
.server_vad() // Uses sensible defaults
.build()?;
Configuración de VAD Personalizada
use adk_realtime::{VadConfig, VadMode};
let vad = VadConfig {
mode: VadMode::ServerVad,
threshold: Some(0.5), // Sensibilidad de detección de voz (0.0-1.0)
prefix_padding_ms: Some(300), // Audio a incluir antes del habla
silence_duration_ms: Some(500), // Silencio antes de finalizar el turno
interrupt_response: Some(true), // Permitir interrumpir al asistente
eagerness: None, // Para el modo SemanticVad
};
let agent = RealtimeAgent::builder("assistant")
.model(model)
.vad(vad)
.build()?;
VAD Semántico (Gemini)
Para los modelos Gemini, puedes usar VAD semántico que considera el significado:
let vad = VadConfig {
mode: VadMode::SemanticVad,
eagerness: Some("high".to_string()), // low, medium, high
..Default::default()
};
Llamada a herramientas
Los agentes en tiempo real soportan la llamada a herramientas durante las conversaciones de voz:
use adk_realtime::{config::ToolDefinition, ToolResponse};
use serde_json::json;
// Definir herramientas
let tools = vec![
ToolDefinition {
name: "get_weather".to_string(),
description: Some("Obtener el clima para una ubicación".to_string()),
parameters: Some(json!({
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"]
})),
},
];
let config = RealtimeConfig::default()
.with_tools(tools)
.with_instruction("Usar herramientas para ayudar al usuario.");
let session = model.connect(config).await?;
// Manejar llamadas a herramientas en el bucle de eventos
while let Some(event) = session.next_event().await {
match event? {
ServerEvent::FunctionCallDone { call_id, name, arguments, .. } => {
// Ejecutar la herramienta
let result = execute_tool(&name, &arguments);
// Enviar la respuesta
let response = ToolResponse::new(&call_id, result);
session.send_tool_response(response).await?;
}
_ => {}
}
}
Traspasos Multi-Agente
Transferir conversaciones entre agentes especializados:
// Crear sub-agentes
let booking_agent = Arc::new(RealtimeAgent::builder("booking_agent")
.model(model.clone())
.instruction("Ayudar con las reservas.")
.build()?);
let support_agent = Arc::new(RealtimeAgent::builder("support_agent")
.model(model.clone())
.instruction("Ayudar con problemas técnicos.")
.build()?);
// Crear agente principal con sub-agentes
let receptionist = RealtimeAgent::builder("receptionist")
.model(model)
.instruction(
"Dirigir clientes: reservas → booking_agent, problemas → support_agent. \
Usar la herramienta transfer_to_agent para el traspaso."
)
.sub_agent(booking_agent)
.sub_agent(support_agent)
.build()?;
Cuando el modelo llama a transfer_to_agent, el RealtimeRunner gestiona el traspaso automáticamente.
Formatos de Audio
| Formato | Frecuencia de Muestreo | Bits | Canales | Caso de Uso |
|---|---|---|---|---|
| PCM16 | 24000 Hz | 16 | Mono | OpenAI (por defecto) |
| PCM16 | 16000 Hz | 16 | Mono | Entrada Gemini |
| G711 u-law | 8000 Hz | 8 | Mono | Telefonía |
| G711 A-law | 8000 Hz | 8 | Mono | Telefonía |
use adk_realtime::{AudioFormat, AudioChunk};
// Crear formato de audio
let format = AudioFormat::pcm16_24khz();
// Trabajar con fragmentos de audio
let chunk = AudioChunk::new(audio_bytes, format);
let base64 = chunk.to_base64();
let decoded = AudioChunk::from_base64(&base64, format)?;
Tipos de Eventos
Eventos del Servidor
| Evento | Descripción |
|---|---|
SessionCreated | Conexión establecida |
AudioDelta | Fragmento de audio (PCM base64) |
TextDelta | Fragmento de respuesta de texto |
TranscriptDelta | Transcripción de audio de entrada |
FunctionCallDone | Solicitud de llamada a herramienta |
ResponseDone | Respuesta completada |
SpeechStarted | Inicio de habla detectado por VAD |
SpeechStopped | Fin de habla detectado por VAD |
Error | Ocurrió un error |
Eventos del Cliente
| Evento | Descripción |
|---|---|
AudioInput | Enviar fragmento de audio |
AudioCommit | Confirmar búfer de audio |
ItemCreate | Enviar texto o respuesta de herramienta |
CreateResponse | Solicitar una respuesta |
CancelResponse | Cancelar respuesta actual |
SessionUpdate | Actualizar configuración |
Ejemplos
Ejecutar los ejemplos incluidos:
# Sesión básica solo de texto
cargo run --example realtime_basic --features realtime-openai
# Asistente de voz con VAD
cargo run --example realtime_vad --features realtime-openai
# Llamada a herramientas
cargo run --example realtime_tools --features realtime-openai
# Traspasos multi-agente
cargo run --example realtime_handoff --features realtime-openai
Mejores Prácticas
- Usar VAD del Servidor: Permita que el servidor gestione la detección de voz para una menor latencia
- Gestionar interrupciones: Habilite
interrupt_responsepara conversaciones naturales - Mantenga las instrucciones concisas: Las respuestas de voz deben ser breves
- Pruebe primero con texto: Depure la lógica de su Agent con texto antes de añadir audio
- Maneje los errores con elegancia: Los problemas de red son comunes con las conexiones WebSocket
Comparación con el SDK de OpenAI Agents
La implementación en tiempo real de adk-rust sigue el patrón del SDK de OpenAI Agents:
| Característica | OpenAI SDK | adk-rust |
|---|---|---|
| Clase base de Agent | Agent | trait Agent |
| Agent en tiempo real | RealtimeAgent | RealtimeAgent |
| Herramientas | Definiciones de funciones | trait Tool + ToolDefinition |
| Traspasos | transfer_to_agent | sub_agents + herramienta autogenerada |
| Callbacks | Ganchos | callbacks before_* / after_* |
Anterior: ← Agents de Grafo | Siguiente: Proveedores de Modelos →