Un asistente de voz en una Raspberry Pi
Cuatro piezas y un presupuesto de latencia. Cómo pensar un asistente de voz para que se sienta natural y no una centralita.
Un asistente de voz parece magia cuando responde rápido y una centralita telefónica cuando tarda tres segundos. Casi todo el trabajo de ingeniería está en esa diferencia. Esto es lo que aprendí montando KITT, un asistente para un coche.
Las cuatro piezas
- Palabra de activación: escucha en local, siempre, con muy poco consumo.
- Voz a texto: convierte lo que dices en una frase.
- Modelo de lenguaje: decide qué responder o qué acción ejecutar.
- Texto a voz: lo dice en voz alta.
La Raspberry Pi aguanta bien la primera, y delega las demás según lo que necesites: en local si quieres privacidad y no tienes red, en la nube si quieres calidad.
El presupuesto de latencia
Una conversación se siente natural por debajo de un segundo hasta el primer sonido. Hay que repartir ese tiempo:
| Etapa | Objetivo |
|---|---|
| Detección de fin de frase | 200 ms |
| Voz a texto | 250 ms |
| Primer fragmento del modelo | 300 ms |
| Primer audio sintetizado | 200 ms |
Todo en streaming
El truco que más ayuda es no esperar a que acabe cada etapa. En cuanto el modelo emite la primera frase completa, se manda a síntesis mientras sigue generando:
async def responder(texto: str):
frase = ""
async for trozo in llm.stream(texto):
frase += trozo
if frase.endswith((".", "?", "!")):
await tts.hablar(frase) # suena mientras el modelo sigue
frase = ""
if frase:
await tts.hablar(frase)
Lo que no se ve en las demos
- El ruido del coche rompe la detección de voz: un buen micrófono vale más que un buen modelo.
- Conviene un sonido corto de confirmación para que el usuario sepa que le has oído.
- Hay que poder interrumpir al asistente en mitad de una frase.
Nada de esto es sofisticado, pero es lo que separa un juguete de algo que quieres usar cada día.