Agentes de Voz em Tempo Real

Agentes em tempo real permitem interações baseadas em voz com assistentes de IA usando streaming de áudio bidirecional. O crate adk-realtime fornece uma interface unificada para construir agentes habilitados para voz que funcionam com a Realtime API da OpenAI e a Gemini Live API do Google.

Visão Geral

Agentes em tempo real diferem dos LlmAgent baseados em texto em várias características principais:

RecursoLlmAgentRealtimeAgent
EntradaTextoÁudio/Texto
SaídaTextoÁudio/Texto
ConexãoRequisições HTTPWebSocket
LatênciaRequisição/respostaStreaming em tempo real
VADN/ADetecção de voz no lado do servidor

Arquitetura

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

RealtimeAgent implementa o mesmo Agent trait que LlmAgent, compartilhando:

  • Instruções (estáticas e dinâmicas)
  • Registro e execução de Tool
  • Callbacks (before_agent, after_agent, before_tool, after_tool)
  • Transições de sub-agente

Início Rápido

Instalação

Adicione ao seu 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")?;

    // Cria o modelo em tempo real
    let model: Arc<dyn RealtimeModel> = Arc::new(
        OpenAIRealtimeModel::new(&api_key, "gpt-4o-realtime-preview-2024-12-17")
    );

    // Constrói o agente em tempo real
    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()?;

    // Ou use a API de sessão de baixo nível diretamente
    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?;

    // Envia texto e obtém resposta
    session.send_text("Hello!").await?;
    session.create_response().await?;

    // Processa eventos
    while let Some(event) = session.next_event().await {
        match event? {
            ServerEvent::TextDelta { delta, .. } => print!("{}", delta),
            ServerEvent::AudioDelta { delta, .. } => {
                // Reproduz áudio (delta é PCM codificado em base64)
            }
            ServerEvent::ResponseDone { .. } => break,
            _ => {}
        }
    }

    Ok(())
}

Provedores Suportados

ProvedorModelFeature FlagAudio Format
OpenAIgpt-4o-realtime-preview-2024-12-17openaiPCM16 24kHz
OpenAIgpt-realtimeopenaiPCM16 24kHz
Googlegemini-2.0-flash-live-preview-04-09geminiPCM16 16kHz/24kHz

Nota: gpt-realtime é o modelo em tempo real mais recente da OpenAI com qualidade de fala, emoção e capacidade de chamada de função aprimoradas.

RealtimeAgent Builder

O RealtimeAgentBuilder fornece uma API fluente para configurar agents:

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

Detecção de Atividade de Voz (VAD)

VAD permite um fluxo de conversa natural ao detectar quando o usuário começa e para de falar.

Server VAD (Recomendado)

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .server_vad()  // Uses sensible defaults
    .build()?;

Configuração Personalizada de VAD

use adk_realtime::{VadConfig, VadMode};

let vad = VadConfig {
    mode: VadMode::ServerVad,
    threshold: Some(0.5),           // Sensibilidade da detecção de fala (0.0-1.0)
    prefix_padding_ms: Some(300),   // Áudio a ser incluído antes da fala
    silence_duration_ms: Some(500), // Silêncio antes de terminar a vez
    interrupt_response: Some(true), // Permite interromper o assistant
    eagerness: None,                // Para o modo SemanticVad
};

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

Semantic VAD (Gemini)

Para modelos Gemini, você pode usar o semantic VAD que considera o significado:

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

Chamada de Ferramentas

Agents em tempo real suportam a chamada de ferramentas durante conversas de voz:

use adk_realtime::{config::ToolDefinition, ToolResponse};
use serde_json::json;

// Definir ferramentas
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?;

// Lidar com chamadas de ferramentas no loop de eventos
while let Some(event) = session.next_event().await {
    match event? {
        ServerEvent::FunctionCallDone { call_id, name, arguments, .. } => {
            // Executar a ferramenta
            let result = execute_tool(&name, &arguments);

            // Enviar a resposta
            let response = ToolResponse::new(&call_id, result);
            session.send_tool_response(response).await?;
        }
        _ => {}
    }
}

Transferências Multi-Agent

Transfira conversas entre agents especializados:

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

// Criar agent principal com 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()?;

Quando o model chama transfer_to_agent, o RealtimeRunner lida com a transferência automaticamente.

Formatos de Áudio

FormatoTaxa de AmostragemBitsCanaisCaso de Uso
PCM1624000 Hz16MonoOpenAI (padrão)
PCM1616000 Hz16MonoEntrada Gemini
G711 u-law8000 Hz8MonoTelefonia
G711 A-law8000 Hz8MonoTelefonia
use adk_realtime::{AudioFormat, AudioChunk};

// Criar formato de áudio
let format = AudioFormat::pcm16_24khz();

// Trabalhar com chunks de áudio
let chunk = AudioChunk::new(audio_bytes, format);
let base64 = chunk.to_base64();
let decoded = AudioChunk::from_base64(&base64, format)?;

Tipos de Eventos

Eventos do Servidor

EventoDescrição
SessionCreatedConexão estabelecida
AudioDeltaChunk de áudio (PCM base64)
TextDeltaChunk de resposta de texto
TranscriptDeltaTranscrição de áudio de entrada
FunctionCallDoneRequisição de chamada de ferramenta
ResponseDoneResposta concluída
SpeechStartedVAD detectou início da fala
SpeechStoppedVAD detectou fim da fala
ErrorErro ocorrido

Eventos do Cliente

EventoDescrição
AudioInputEnviar chunk de áudio
AudioCommitConfirmar buffer de áudio
ItemCreateEnviar texto ou resposta da ferramenta
CreateResponseSolicitar uma resposta
CancelResponseCancelar resposta atual
SessionUpdateAtualizar configuração

Exemplos

Execute os exemplos incluídos:

# Sessão básica apenas com texto
cargo run --example realtime_basic --features realtime-openai

# Assistente de voz com VAD
cargo run --example realtime_vad --features realtime-openai

# Chamada de ferramentas
cargo run --example realtime_tools --features realtime-openai

# Transferências multi-agent
cargo run --example realtime_handoff --features realtime-openai

Melhores Práticas

  1. Use VAD do Servidor: Deixe o servidor lidar com a detecção de fala para menor latência
  2. Lide com interrupções: Habilite interrupt_response para conversas naturais
  3. Mantenha as instruções concisas: Respostas de voz devem ser breves
  4. Teste com texto primeiro: Depure sua lógica de agente com texto antes de adicionar áudio
  5. Lide com erros de forma elegante: Problemas de rede são comuns com conexões WebSocket

Comparação com o OpenAI Agents SDK

A implementação em tempo real do adk-rust segue o padrão do OpenAI Agents SDK:

FuncionalidadeOpenAI SDKADK-Rust
Classe base de agenteAgentAgent trait
Agente em tempo realRealtimeAgentRealtimeAgent
FerramentasDefinições de funçãoTool trait + ToolDefinition
Transferênciastransfer_to_agentsub_agents + ferramenta auto-gerada
CallbacksHooksbefore_* / after_* callbacks

Anterior: ← Graph Agents | Próximo: Model Providers →