AWS enseña a los agentes de voz con IA a hablar desde la primera frase con Kokoro-82M
AWS ha descrito un esquema en el que el agente de voz con IA no espera a que el modelo termine de escribir toda la respuesta. Cada frase lista se envía de inmediato a Kokoro-82M, desplegado en SageMaker AI, y la locución empieza con la primera oración. Antes, el cliente se quedaba en silencio unos cinco segundos, aunque una respuesta corta se genera en 600 ms. El modelo pesa solo 82 millones de parámetros y funciona en una única GPU ml.g5.xlarge. Entre el cliente y el contenedor se mantiene una sola sesión WebSocket bidireccional: el audio y los comandos JSON viajan por el mismo canal, sin base64 ni reconexiones innecesarias. • Base64 en SSE infla el audio en torno a un 33%: MP3 48k pasa de 308 304 bytes a 411 072 tras la codificación • El error KeyError('text') aparece porque SageMaker envía únicamente frames binarios • El agente en Strands + Bedrock AgentCore, en modo bidi, solapa la generación y la síntesis
Procesado por IA desde AWS Machine Learning Blog; editado por Hamidun News
El usuario hace una pregunta a un agente de voz y espera. La respuesta ya está parcialmente generada, pero no se oye nada: las integraciones de text-to-speech suelen esperar a que termine todo el texto. El modelo produce una frase en 600 ms, pero una respuesta larga completa tarda unos cinco segundos. Durante todo ese tiempo, el cliente escucha silencio. En su blog, AWS ha mostrado cómo eliminar ese vacío: enviar cada frase al sintetizador Kokoro-82M en Amazon SageMaker AI en cuanto el agente la termina.
Un modelo pequeño, una GPU y dos rutas
Kokoro-82M tiene 82 millones de parámetros, y eso basta para ejecutarlo en una sola instancia con GPU de gama media. El modelo se lanza en un contenedor personalizado, sobre un endpoint real-time de SageMaker AI con instancia de tipo ml.g5.xlarge. El contenedor expone dos rutas: POST /invocations, para una respuesta en streaming a una entrada completa, y WS /invocations-bidirectional-stream, donde una sola conexión sigue abierta durante muchos mensajes en ambos sentidos. El contenedor debe declarar la etiqueta Docker com.amazonaws.sagemaker.capabilities.bidirectional-streaming=true.
El despliegue se reduce a dos comandos basados en AWS CDK: `uv run speech-infra deploy kokoro-82m` levanta el endpoint (roles IAM, caché de pesos en S3, imagen del contenedor, modelo y configuración), y `uv run agent-infra deploy --bedrock-model-id <id>` despliega el agente en Strands en el entorno de Amazon Bedrock AgentCore. El orden importa: el agente se conecta a un endpoint que ya existe. Hace falta Python 3.12+, porque el cliente HTTP/2 solo se distribuye en wheels ya compilados para esas versiones. Los autores incluyen además una advertencia: el ejemplo fija una versión antigua de PyTorch con la vulnerabilidad conocida PYSEC-2026-2286 (corregida en torch 2.10.0), y hay que actualizarla antes de cualquier uso fuera de una proof-of-value.
Tres trampas del streaming bidireccional
La primera trampa es el tipo de frame WebSocket. SageMaker entrega cada mensaje del cliente como un frame binario. Un manejador típico en Starlette llama a receive_text(), lee message["text"] y falla con un KeyError. Si el manejador general de excepciones envía str(e) al cliente, este ve el error «'text'», que parece un campo omitido y no un problema de framing. Mientras tanto, el endpoint muestra InService sin mentir: acepta conexiones y responde a cada mensaje, pero con un error. La solución es aceptar ambos tipos de frame y decodificar los bytes como UTF-8 cuando no exista el campo text.
La segunda es el cierre prematuro de la sesión. Tras enviar el audio y el frame de control synthesis_complete, el contenedor debe volver a receive() y esperar el siguiente mensaje. La conexión solo debe cerrarse con un {"type": "close"} explícito. De lo contrario, cada frase exigiría una conexión nueva y la ventaja en latencia desaparecería.
La tercera es el troceado del flujo en frases. La clase IncrementalSentenceChunker recibe el texto por porciones mediante feed(), separa las frases completas con una expresión regular y retiene la cola. Al final, flush() recoge el resto, que no lleva punto final. Los autores lo aclaran con franqueza: no se trata de síntesis paralela. El manejador es estrictamente secuencial: recibir el mensaje, sintetizar, enviar el audio, enviar synthesis_complete. La única ganancia es que la síntesis de la primera frase arranca en cuanto esta aparece. En un endpoint real probaron tres frases en una sola sesión:
- «Hello there.» — 73 200 bytes de audio, 1,525 s
- «This is a post deployment check.» — 108 000 bytes, 2,250 s
- «It has three sentences.» — 98 400 bytes, 2,050 s
SSE con base64 frente a multiplexación binaria
Para entregar el audio al navegador, AWS comparó dos enfoques. El primero es el habitual SSE: el audio se codifica en base64 y se envuelve en un evento JSON. Con 24 clips midieron el coste de esta solución (la calidad según Amazon Transcribe no dependió de la codificación):
- Raw PCM: 2 396 400 bytes, en base64 — 3 195 200, bitrate de 384 kbit/s
- MP3 48k: 308 304 bytes, en base64 — 411 072, 49,5 kbit/s
- AAC 32k fMP4: 258 039 bytes, en base64 — 344 084, 41,6 kbit/s
Base64 añade casi el 33% esperado. Hay además un problema menos evidente: los límites de los eventos SSE (la línea vacía) no coinciden con los límites de los frames MP3 ni con la forma en que el transporte fragmenta los datos. En una medición, cuatro frames MP3 lógicos llegaron en seis chunks, dos de los cuales se cortaban a mitad de frame. Un consumidor que dé por completo cada chunk se rompe con tráfico perfectamente sano. Se resuelve con un búfer: se acumulan los bytes y el frame solo se procesa cuando se encuentra el separador `\n\n`.
El segundo enfoque consiste en prescindir por completo de base64 y de SSE en el tramo hasta el modelo. El audio binario y los comandos JSON viajan por una misma sesión bidireccional y se distinguen con una sola regla: si el chunk se parsea entero como un objeto JSON, es un frame de control; si no, es audio. Comprobar el primer byte no sirve: el PCM puede empezar por 0x7b, es decir, por «{». Pero un chunk PCM completo casi nunca será un JSON válido.
El agente completo
En la configuración completa, el agente en Strands, con un modelo de Amazon Bedrock, se ejecuta en el runtime de Bedrock AgentCore y usa tres canales: SSE hacia el navegador, una llamada en streaming a Bedrock para el texto y una sesión bidireccional con Kokoro para la síntesis. La petición incluye el campo mode: «bidi» o «batch». En run_bidi trabajan en paralelo dos tareas: una lee el flujo de Bedrock y pasa al chunker las frases terminadas; la otra lee las respuestas de Kokoro y emite el audio.
Ambas depositan el resultado en una cola común, de modo que el sonido de las primeras frases puede llegar al llamante mientras Bedrock aún escribe las últimas. run_batch espera todo el texto y hace una sola llamada de síntesis: una base de comparación justa.
En el tramo del agente al llamante, base64 sigue presente: el entrypoint de BedrockAgentCoreApp debe devolver diccionarios serializables a JSON, que el SDK convierte por sí mismo en frames `data: {json}`. No se puede pasar un frame binario por ahí. Este coste es igual en ambos modos, así que la diferencia en las mediciones es puramente el tiempo hasta el primer chunk de audio.
Qué elegir en la práctica
Si el frontend ya usa SSE para el texto, base64 dentro del evento es una opción pragmática: +33% de tamaño y un búfer para el ensamblado, pero con la infraestructura ya lista. Si el canal de voz se construye desde cero o la latencia es crítica, un esquema binario unificado con control por JSON elimina ambos costes y aprovecha la conexión que ya transporta el texto. En cualquier caso, conviene probar precisamente con frames binarios, comprobar el ensamblado en los límites de los frames MP3 y no cerrar la sesión después de cada frase. Y antes de pasar a producción, actualizar el PyTorch fijado.
- 29 oct 2026Cloudflare lanza los modelos open source Clef: 98,5% en BFCL y 38,8 ms
- 29 oct 2026Anthropic muestra en su S-1: 11 900 clientes pagan desde $100 mil al año
- 28 oct 2026Keystone saca el nombre del modelo del código: un gateway LLM open source añade 0,64 ms
- 28 oct 2026Wired probó 6 agentes de IA en un día: Dots lidera y Gemini Spark fracasa con el almuerzo
Preguntas frecuentes sobre la noticia
¿Qué se sabe sobre Un modelo pequeño, una GPU y dos rutas?
Kokoro-82M tiene 82 millones de parámetros, y eso basta para ejecutarlo en una sola instancia con GPU de gama media. El modelo se lanza en un contenedor personalizado, sobre un endpoint real-time de SageMaker AI con instancia de tipo ml.g5.xlarge. El contenedor expone dos rutas: POST /invocations, para una respuesta en streaming a una entrada completa, y WS /invocations-bidirectional-stream, donde una sola conexión sigue abierta durante muchos mensajes en ambos sentidos. El contenedor debe declarar la etiqueta Docker com.amazonaws.sagemaker.capabilities.bidirectional-stream
¿Qué se sabe sobre Tres trampas del streaming bidireccional?
La primera trampa es el tipo de frame WebSocket. SageMaker entrega cada mensaje del cliente como un frame binario. Un manejador típico en Starlette llama a receive_text(), lee message["text"] y falla con un KeyError. Si el manejador general de excepciones envía str(e) al cliente, este ve el error «'text'», que parece un campo omitido y no un problema de framing. Mientras tanto, el endpoint muestra InService sin mentir: acepta conexiones y responde a cada mensaje, pero con un error. La solución es aceptar ambos tipos de frame y decodificar los bytes como UTF-8 cuando no exis
¿Qué se sabe sobre SSE con base64 frente a multiplexación binaria?
Para entregar el audio al navegador, AWS comparó dos enfoques. El primero es el habitual SSE: el audio se codifica en base64 y se envuelve en un evento JSON. Con 24 clips midieron el coste de esta solución (la calidad según Amazon Transcribe no dependió de la codificación): Raw PCM: 2 396 400 bytes, en base64 — 3 195 200, bitrate de 384 kbit/s MP3 48k: 308 304 bytes, en base64 — 411 072, 49,5 kbit/s AAC 32k fMP4: 258 039 bytes, en base64 — 344 084, 41,6 kbit/s Base64 añade casi el 33% esperado. Hay además un problema menos evidente: los límites de los eventos SSE (la línea
Delegate Deep Research, Coding & Multi-Step Workflows to Nomad Agent
Deploy autonomous task execution with Nomad Agent (agent.nomadnet.ai) — automated web research, data synthesis, code generation, and MCP tool orchestration.
Lo esencial de la IA — una vez por semana
Siete historias que de verdad importaron, elegidas a mano. Sin ruido ni notas de prensa.
¡Listo! Revisa tu correo para la confirmación.