Crie um agente de voz em tempo real com Google ADK e Gemini Live API
Autoria: Google Cloud Tech · Assistir no YouTube
Vídeo de autoria do canal indicado, não da IANIA; todos os direitos pertencem ao criador. Transcrição, resumo e tradução gerados automaticamente; o conteúdo no idioma original é preservado na versão em idioma original.
Idioma original: inglês
Vídeo original: Build a real-time voice AI agent with Google ADK and Gemini Live API · Canal: Google Cloud Tech · 09:24 · Assistir no YouTube
Tópicos: Agentes de IA, Modelos de IA, Modelos multimodais
O vídeo apresenta a construção de um agente de voz ao vivo que conversa, aceita interrupções e pode executar ações, usando Google ADK e Gemini Live. A arquitetura usa uma conexão persistente entre navegador e backend, com áudio transmitido nos dois sentidos, em vez de uma sequência de etapas de fala para texto, modelo e texto para fala. No backend, o ADK organiza agente, runner, sessão e uma fila que separa os fluxos de áudio de entrada e os eventos de saída. O vídeo também explica quando usar send_realtime e send_content e por que manter a sessão em memória ajuda a reduzir a latência.
Pontos-chave
Seção intitulada “Pontos-chave”- A voz em tempo real pede uma conexão bidirecional persistente, diferente de um pipeline sequencial de fala para texto, modelo e texto para fala.
- O navegador envia e recebe áudio por WebSocket, enquanto o backend conecta a aplicação ao Gemini Live.
- No ADK, o agente é definido por um modelo, instruções e ferramentas; novas habilidades podem ser adicionadas como funções Python.
- O runner mantém a chamada ao vivo e entrega eventos que a aplicação pode usar para reagir.
- A sessão guarda o estado da conversa e, para voz ao vivo, deve permanecer em memória para evitar atrasos de rede.
- Uma fila de requisições ao vivo desacopla o envio de áudio da recepção de eventos.
- send_realtime serve para fluxos contínuos, como o áudio do microfone; é importante enviar também os trechos de silêncio.
- send_content serve para entradas completas e pontuais, como texto ou uma foto enviada pelo usuário.
- A aplicação reage aos eventos para reproduzir áudio, mostrar legendas, iniciar ações ou interromper a reprodução.
Capítulos
Seção intitulada “Capítulos”| Início | Capítulo | Resumo |
|---|---|---|
| 00:00 | Demonstração e arquitetura de voz ao vivo | A apresentadora demonstra um DJ de rádio conversacional e explica por que a voz em tempo real precisa de uma conexão bidirecional, não de um pipeline sequencial. |
| 01:32 | Arquitetura e componentes do ADK | O vídeo descreve o fluxo entre navegador, backend e Gemini Live e apresenta agente, runner e sessão. |
| 03:42 | Fila e envio de áudio | A fila desacopla os fluxos simultâneos, e send_realtime e send_content são explicados para entradas contínuas e completas. |
| 06:17 | Eventos, interrupções e latência | O runner processa eventos de áudio, legendas, ações e interrupções; manter a sessão em memória reduz a latência. |
Citações
Seção intitulada “Citações”Pense nisso não como um walkie-talkie, mas como uma ligação telefônica. — 01:32
Voz ao vivo é uma linha aberta com fluxos, não cinco etapas em sequência. — 02:37
Sempre envie, até mesmo o silêncio. — 05:46
Uma fila, duas direções; nenhuma espera. — 06:48
Conceitos e entidades
Seção intitulada “Conceitos e entidades”- Google ADK (ferramenta): Kit usado no vídeo para definir e executar agentes, organizar sessões e processar eventos de voz.
- Gemini Live API (ferramenta): API apresentada para manter uma conexão de voz bidirecional e transmitir áudio em tempo real.
- WebSocket (conceito): Conexão persistente usada para transportar áudio entre o navegador e o backend.
- send_realtime (ferramenta): Método usado para enviar fluxos contínuos, como trechos de áudio do microfone, à fila de requisições ao vivo.
- send_content (ferramenta): Método usado para enviar entradas completas e pontuais, como uma mensagem de texto ou uma foto.
- runner.run_live (ferramenta): Chamada que inicia o fluxo ao vivo do runner e recebe os eventos da sessão.
- Annie Wang (pessoa): Apresentadora que explica a arquitetura e a implementação do agente de voz.
- Google Cloud Tech (empresa): Canal do YouTube que publicou o vídeo.
Tradução completa (pt-BR)
Seção intitulada “Tradução completa (pt-BR)”00:00 ANNIE WANG: Oi. Eu sou a Annie. Hoje, vamos criar um agent de voz ao vivo com o Google ADK, um com quem você pode realmente conversar, que pode interromper no meio de uma frase e a quem você pode pedir para fazer coisas. Aqui está o agent que vamos criar. É um DJ de rádio que consegue manter uma conversa de verdade e atender a pedidos. Ei, Mira. MIRA: Oi, seja bem-vinda. O que você tem em mente para hoje à noite? ANNIE WANG: Pode tocar algo de dream pop? MIRA: Dream pop? Claro. Só um instante.
00:30 Vou encontrar algo para você. ANNIE WANG: Ao final deste vídeo, você vai entender como criar isso com o ADK. Vamos explicar em três partes. Parte 1: a arquitetura — por que a voz em tempo real precisa de um design diferente. Parte 2: os blocos de construção do ADK. Parte 3: o loop ao vivo — como o áudio flui nas duas direções ao mesmo tempo, sem bloqueios. É nessa parte que um agent de voz ao vivo parece natural
01:02 ou desmorona. Então, antes de falarmos sobre o ADK, vamos recapitular rapidamente a arquitetura. No episódio anterior, falamos sobre por que a voz ao vivo é diferente. A maneira comum de criar um sistema de voz é usar um pipeline: fala para texto, modelo, texto para fala. Cada etapa espera a anterior terminar, e isso funciona para chat. Mas, na voz, até dois segundos de silêncio parecem uma falha. A voz ao vivo precisa de um formato diferente.
01:32 Não pense nisso como um walkie-talkie, mas como uma ligação telefônica. Os dois lados precisam estar conectados. O áudio flui nas duas direções, e você pode interromper naturalmente. Esse é o formato que o Gemini Live nos oferece: uma conexão persistente e bidirecional, com áudio entrando e saindo por streaming o tempo todo. Então, aqui está o diagrama completo. À esquerda, o navegador. Ele nos dá o microfone, o alto-falante e o player de música. No meio, um pequeno backend.
02:04 O navegador se comunica com ele por um WebSocket, que permanece aberto. O áudio se move nas duas direções. Dentro do backend, temos os componentes do ADK: uma fila, um runner, um agent e uma sessão. À direita, o Gemini Live. Este diagrama mostra o fluxo completo: do navegador ao backend, do backend ao Gemini Live, e os eventos voltando. Então, a conclusão da parte 1: a voz ao vivo é uma única linha aberta com streams,
02:37 não cinco etapas em sequência. Agora vamos ver os componentes do ADK dentro do backend. Primeiro, o agent. O modelo mental é simples. Um agent tem três coisas: um modelo, uma personalidade e habilidades. No ADK, você define um agent em um só lugar. Você escolhe um modelo, dá instruções e fornece ferramentas. Essa é a definição completa do agent. Se quiser uma nova habilidade, basta adicionar uma função Python
03:10 à lista de ferramentas. Esse é o padrão. O agent é definido por configuração. As habilidades são apenas funções, e vamos nos aprofundar nessas ferramentas no próximo episódio. Em seguida, o runner. A definição de um agent não significa que ele esteja em execução. O runner o executa. Ele controla uma chamada ao vivo do início ao fim, e tudo o que acontece retorna pelo runner como eventos. Guarde essa palavra. Os eventos são a chave para o loop ao vivo.
03:42 Depois, temos a sessão. A sessão mantém o estado da conversa. Para voz ao vivo, mantemos esse estado na memória, para que o caminho do áudio não precise esperar por uma chamada de rede. Então, a configuração é: descrever o agent, entregá-lo ao runner, manter a sessão na memória e dizer à chamada o que queremos. Para responder com áudio, usar voz e exibir legendas. Pronto. Então, a conclusão da parte 2: descreva o agent.
04:13 Entregue-o a um runner. Mantenha a sessão na memória. Agora vem a parte difícil. Como o áudio se move nas duas direções ao mesmo tempo sem colidir? Eis o problema. O navegador envia áudio do microfone a cada poucos milissegundos. O Gemini envia a voz de volta com a mesma rapidez. Esses dois fluxos acontecem ao mesmo tempo e, se um dos lados parar, não pode bloquear o outro. Esse é o grande desafio. Então, imagine uma esteira de sushi.
04:44 A esteira continua colocando pratos em uma ponta. Você continua retirando pratos da outra. Nenhum dos lados espera, e esse é o objetivo. O navegador envia blocos de áudio. O modelo os consome. No ADK, essa esteira é uma fila de solicitações ao vivo. Ela desacopla as duas direções. Seu servidor se torna dois processos paralelos: upstream, do navegador para a fila; downstream, dos eventos de volta para o navegador,
05:15 completamente desacoplados. Essa é a esteira. Agora, o que vai nela? Há duas maneiras de enviar entrada. Primeiro, send_realtime. Você pode usar isso para fluxos contínuos, como o áudio do microfone. Pense nisso como manter pressionado o botão de falar. Não há uma fronteira clara entre mensagens. O áudio simplesmente flui, e a detecção de voz do Gemini determina quando você parou de falar.
05:46 Aqui está o ponto em que todo mundo costuma errar. Envie sempre, até mesmo o silêncio. O modelo precisa do silêncio para perceber onde sua frase termina. E, em segundo lugar, send_content. Você pode usar isso para itens discretos e completos: uma mensagem de texto, uma foto, algo em que o usuário claramente tocou em Enviar. Pense nisso como tocar em Enviar no app de chat. Pronto. Responda agora.
06:17 Então, a regra geral é: se isso estiver acontecendo continuamente, use send_realtime. Se estiver completo, use send_content. No nosso loop do microfone, cada bloco que chega pelo WebSocket vai direto para a fila com send_realtime. Esse é o envio. Agora, a outra direção. Uma chamada inicia um pipeline: runner.run_live. Você aponta para a fila e, então, a sessão full-duplex
06:48 entra ao vivo. O que retorna é um fluxo de eventos. Um bloco de voz? Reproduza-o. Uma legenda? Exiba-a. Uma solicitação de ferramenta? Inicie uma música. Um evento de interrupção? O usuário falou por cima dela; pare o áudio imediatamente. É por isso que os eventos importam. O runner transforma a chamada ao vivo em um fluxo de coisas às quais seu app pode reagir. Então, a conclusão da parte 3: uma fila, duas direções, nenhum dos lados espera.
07:22 É isso que faz o agent parecer estar ao vivo. Agora vamos acompanhar uma frase pela máquina. Você diz: “Toque algo de dream pop.” Seu microfone transforma isso em blocos de áudio. Esses blocos são enviados pelo WebSocket ao backend. O processo upstream os coloca na fila de solicitações ao vivo com send_realtime. O runner os envia ao Gemini Live. A detecção de voz do Gemini percebe quando você termina,
07:52 e então os eventos voltam. A voz dela sai pelo seu alto-falante. A legenda aparece na tela. O comando para tocar a música vai para o player de música. Depois, você fala por cima dela. Chega um evento de interrupção. O áudio dela para. Uma frase: essa é a máquina inteira. Um detalhe de implementação realmente importante: mantenha a sessão na memória para voz ao vivo. O serviço de sessão hospedado grava um evento para cada bloco de áudio.
08:22 Isso significa uma viagem de ida e volta pela rede a cada poucos milissegundos, e a voz dela chegou com quatro segundos de atraso. Na memória, a chamada parece instantânea. Então, eis o que você aprendeu. Primeiro, a arquitetura. A voz ao vivo é uma única linha aberta, com streams nas duas direções, não um pipeline de cinco etapas. Segundo, os blocos de construção do ADK: descrever um agent e entregá-lo a um runner. Manter a sessão na memória.
08:54 Terceiro, o loop ao vivo. Use uma fila. Envie áudio contínuo com send_realtime. Envie entradas completas com send_content e reaja aos eventos conforme eles retornam. Esse é um agent de voz ao vivo com o ADK. É isso. Não se esqueça de deixar sua opinião nos comentários. No próximo episódio, vamos dar a esse agent habilidades para fazer chamadas, não apenas conversar, mas também controlar o app.
09:24 Você fala. O agent multimodal decide que ação executar. Seu código executa a ação, e a conversa continua. Certo, vejo vocês no próximo episódio. Tchau. [MÚSICA TOCANDO]
Transcrição original
[00:00] ANNIE WANG: Hi. I’m Annie. Today, we’re building a live voice agent with Google ADK, one you can actually talk to, one you can interrupt mid-sentence, and one you can ask to do things. Here’s the agent we’re building. This is a radio DJ that can hold a real conversation and take requests. Hey, Mira. MIRA: Hey, welcome. What’s on your mind tonight? ANNIE WANG: Can you play something dream pop? MIRA: Dream pop? Of course. Just a moment.
[00:30] Let me find something for you. ANNIE WANG: By the end of this video, you will understand how to build it with ADK. We will explain it in three parts. Part 1, the architecture, why real-time voice needs a different design. Part 2, the ADK building blocks. Part 3, the live loop, how audio flows both directions at once without blocking. That part is where live voice agent either feel natural
[01:02] or fall apart. So before we get into ADK, let’s quickly reset the architecture. In the last episode, we talked about why live voice is different. The common way to build voice is a pipeline, speech to text, model, text to speech. Each step waits for the last one, and that works for chat. But in voice, even two seconds of silence feels broken. Live voice needs a different shape.
[01:32] Don’t think of it as a walkie-talkie, but a phone call. Both sides needs connected. Audio flows both ways, and you can interrupt naturally. That is a shape Gemini Live gives us, one persistent, two-way connection, audio in, audio out, streaming the whole time. So here’s the full diagram. On the left, the browser. That gives us the mic, the speaker, and the music player. In the middle, a small backend.
[02:04] The browser talks to it over a WebSocket that WebSocket stays open. Audio moves both ways. Inside the backend, we have the ADK pieces, a queue, a runner, an agent, and a session. On the right, Gemini Live. This diagram shows the full flow, browser to backend, backend to Gemini Live, and events flowing back. So part 1 takeaway, live voice is one open line with strings,
[02:37] not five steps in a row. Now let’s look at the ADK pieces inside the backend. First, the agent. The mental model is simple. An agent is three things, a model, a personality, and abilities. In ADK, you define an agent in one place. You give it a model, you give it instructions, and you give it tools. That is the whole agent definition. If you want a new ability, just add a Python function
[03:10] to the tool list. That is the pattern. The agent is defined by configuration. The abilities are just functions, and we will go deep on those tools in the next episode. Next, the runner. An agent definition is not an agent running. The runner runs her. It owns a live call from open to close, and everything that happens comes back through the runner as events. Hold that word. Events are the key to the live loop.
[03:42] Then we have the session. The session holds a conversation state. For live voice, we keep it in memory, and that way, the audio path does not wait on a network call. So the setup is describe the agent, hand it to the runner, keep the session in memory, and tell the call what we want. Answering audio uses voice, gives us captions, done. So part 2 takeaway, describe the agent.
[04:13] Hand it to a runner. Keep the session in memory. So now the hard part. How does the audio move both ways at once without colliding? Here is a problem. The browser sends microphone audio every few milliseconds. Gemini sends voice back just as fast. Those two flows are happening at the same time, and if one side stalls, it cannot block another. That is a whole challenge. So picture a sushi conveyor belt.
[04:44] The shelf keeps putting plates on one end. You keep taking plates off the other end. Neither waits, and that is the point. The browser is sending audio chunks. The model is consuming them. In ADK, that belt is a live request queue. It decouples the two directions. Your server becomes two parallel jobs, upstream, browser to queue, downstream, events back to the browser,
[05:15] completely decoupled. That is the belt. Now, what goes on the belt? There are two ways to send input. First, send_realtime. You can use this for continuous streams, like microphone audio. Think of it like holding down the Talk button. There is no clean message boundary. The audio just flows, and Gemini’s voice detection decides when you have stopped talking.
[05:46] Here’s the part everyone gets wrong easily. Always send, even silence. The model needs the quiet to hear where your sentence ends. And second, send_content. You can use this for discrete, complete things, a text message, a photo, something where the user has clearly hit Send. Think of it like hitting Send in the Chat app. This is done. Respond now.
[06:17] So rule of thumb, if this is continuously happening, use send_realtime. If this is complete, use send_content. For our mic loop, every chunk from the WebSocket goes straight onto the queue with send_realtime. That is sending. Now, the other direction. One call starts a pipeline, runner.run_live. You point it at the queue, and now the full duplex session
[06:48] is live. What comes back is a stream of events. A voice chunk? Play it. A caption? Show it. A tool request? Start a music. An interrupted event? The user talked over her, stop the audio instantly. That is why events matter. The runner turns the live call into a stream of things your app can react to. So part 3 takeaway, one queue, two directions, neither waits.
[07:22] That is what makes the agent feel live. Now let’s trace one sentence through the machine. You say, play something dream pop. Your mic becomes audio chunks. Those chunks write the WebSocket to the backend. The upstream job puts them on the live request queue with send_realtime. The runner feeds them to Gemini Live. Gemini’s voice detection hears when you finish,
[07:52] then events come back down. Her voice goes through your speaker. The caption goes to the screen. The play command goes to the music player. Then you talk over her. An interrupted event arrives. Her audio stops. One sentence, this is the entire machine, one implementation detail that really matters. Keep the session in memory for live voice. The hosted session service writes an event for each audio chunk.
[08:22] That means a network round trip every few milliseconds, and her voice came four seconds behind. In memory, the call feels instant. So here is what you learn. First, the architecture. Live voice is one open line with streams in both directions, not a five-step pipeline. Second, the ADK building blocks, describing an agent, hand it to a runner. Keep the session in memory.
[08:54] Third, the live loop. Use one queue. Send continuous audio with send_realtime. Send complete input with send_content, and react to events as they flow back. That is a live voice agent with ADK. That’s it. Don’t forget to leave your thoughts in the comments. In the next episode, we give this agent abilities to calling, not just talking, actually controlling the app.
[09:24] You speak. The multimodal agent decides what action to take. Your code runs the action, and the conversation keeps going. All right, I will see you in the next episode. Bye. [MUSIC PLAYING]