¿Cómo pruebo un agente de voz sin volverme loco?
🟠 Intermedio · Práctico · 8 min de lectura
Monto un agente de voz, hablo con él, funciona. Lo enseño y funciona. Lo
dejo en manos de otra persona y se interrumpen el uno al otro durante
cuarenta segundos. ¿Cómo se prueba esto, exactamente?
El problema
Todo lo que sabes de probar agentes de texto deja de servirte en cuanto hay
audio, y por tres motivos que no tienen nada que ver entre sí.
La respuesta correcta puede llegar tarde y eso ya es un fallo. En texto,
que una respuesta tarde cuatro segundos es una molestia. En una conversación
hablada, cuatro segundos de silencio es la otra persona pensando que se ha
cortado la llamada. La latencia no es una métrica de rendimiento aparte: es
parte de si la respuesta es correcta.
No puedes comparar la salida carácter a carácter. Dos audios que dicen lo
mismo no se parecen en nada como bytes. Y si transcribes para comparar, estás
midiendo también tu transcriptor, así que un fallo te puede venir de tres
sitios y no sabes de cuál.
Las interrupciones son comportamiento, no ruido. Que el agente se calle
cuando le hablas encima, y cuánto tarda en callarse, es tan parte del producto
como lo que dice. Es justo lo que no se puede probar escribiendo.
Y hasta ahora, para quien desarrolla desde España, había un cuarto problema:
mandar audio de usuarios a un servicio fuera de la Unión Europea. Mistral ha
sacado esta semana Voxtral 3, un SDK de agentes de voz con habla en tiempo
real en más de 50 idiomas, transcripción, voz sintética con emoción y uso de
herramientas, alojado en la UE o desplegable en tu propia máquina. Esa
última parte es la que cambia qué puedes probar con datos reales.
Cómo se resuelve hoy
Partiendo el problema en tres capas, porque mezcladas no se depuran.
La capa de texto, que es la que ya sabes probar. Dentro de tu agente de
voz hay una conversación de texto: lo que se transcribió, lo que el modelo
decidió, qué herramienta llamó. Esa capa se prueba con los mismos arneses de
siempre, sin audio por ninguna parte, y es donde debería estar el 80% de tus
pruebas. Si tu agente llama mal a una herramienta, no es un problema de voz.
La capa de transcripción, que se prueba con audio y se mide con números.
Aquí sí hace falta sonido: grabaciones reales, con acentos, con ruido de
fondo, con el nombre de tu producto mal pronunciado. La métrica es la tasa de
error de palabra, y lo que importa no es la media: son los casos donde falla,
porque suelen ser los mismos (nombres propios, cifras, el vocabulario de tu
dominio).
La capa de conversación, que es la que casi nadie prueba. Latencia hasta
el primer sonido, qué pasa cuando le hablas encima, qué hace ante tres
segundos de silencio, si se recupera cuando el usuario cambia de tema a
mitad. Esto no se automatiza bien todavía, y la manera honesta de hacerlo hoy
es un guion de escenarios que alguien ejecuta hablando, con las respuestas
anotadas.
La métrica que más rinde y que menos se mide es el tiempo hasta el primer
sonido, no hasta la respuesta completa. Un agente que empieza a hablar en
400 milisegundos y tarda cuatro segundos en terminar se vive como rápido. Uno
que tarda dos segundos en abrir la boca y termina en tres se vive como lento,
aunque acabe antes.
Ejemplo
Un arnés mínimo para la capa de transcripción, que es la que se puede
automatizar hoy con menos esfuerzo y la que más sorpresas da. Mide la tasa de
error de palabra sobre tus propias grabaciones y, sobre todo, enseña qué
palabras falla.
probar_transcripcion.py:
import json
import sys
import unicodedata
from pathlib import Path
def normalizar(texto: str) -> list[str]:
"""Quita tildes, signos y mayusculas: comparamos palabras, no ortografia."""
sin_tildes = "".join(
c for c in unicodedata.normalize("NFD", texto.lower())
if unicodedata.category(c) != "Mn"
)
limpio = "".join(c if c.isalnum() or c.isspace() else " " for c in sin_tildes)
return limpio.split()
def distancia(esperado: list[str], obtenido: list[str]) -> int:
"""Levenshtein a nivel de palabra: cuantas sustituciones, altas y bajas."""
previa = list(range(len(obtenido) + 1))
for i, palabra in enumerate(esperado, 1):
actual = [i]
for j, otra in enumerate(obtenido, 1):
actual.append(min(
previa[j] + 1,
actual[j - 1] + 1,
previa[j - 1] + (palabra != otra),
))
previa = actual
return previa[-1]
def transcribir(ruta: Path) -> str:
"""Sustituye por tu motor. Recibe un fichero de audio, devuelve texto."""
raise NotImplementedError("engancha aqui tu transcriptor")
def evaluar(casos: list[dict]) -> None:
total_palabras = 0
total_errores = 0
fallos = []
for caso in casos:
esperado = normalizar(caso["texto"])
obtenido = normalizar(transcribir(Path(caso["audio"])))
errores = distancia(esperado, obtenido)
total_palabras += len(esperado)
total_errores += errores
if errores:
faltan = set(esperado) - set(obtenido)
fallos.append((caso["audio"], errores, len(esperado), sorted(faltan)))
tasa = total_errores / total_palabras if total_palabras else 0
print(f"tasa de error de palabra: {tasa:.1%} ({total_errores}/{total_palabras})")
print()
for audio, errores, palabras, faltan in sorted(fallos, key=lambda f: -f[1]):
print(f"{errores:>3}/{palabras:<4} {Path(audio).name}")
if faltan:
print(f" no reconocio: {', '.join(faltan[:8])}")
if __name__ == "__main__":
evaluar(json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")))
casos.json, con la forma que tienen que tener tus grabaciones:
[
{
"audio": "audios/pedido-con-ruido.wav",
"texto": "Quiero cambiar mi pedido numero cuatro siete dos a la direccion de Camargo"
},
{
"audio": "audios/acento-andaluz.wav",
"texto": "Buenas, llamaba para preguntar por la factura de septiembre"
},
{
"audio": "audios/nombre-del-producto.wav",
"texto": "No me funciona el Ludiversia Tweak que compre la semana pasada"
}
]
Se ejecuta así:
python probar_transcripcion.py casos.json
Y lo que de verdad miras no es el porcentaje de arriba, es la lista de abajo:
no reconocio: tweak, ludiversia, camargo. Esa lista es tu trabajo de la
semana, y casi siempre se arregla con un diccionario de términos del dominio
en vez de cambiando de modelo.
Para la capa de conversación, el guion que yo usaría, ejecutado a mano y
cronometrado:
1. Pregunta normal. Mide el tiempo hasta que EMPIEZA a hablar.
2. Interrúmpele a mitad de frase. ¿Se calla? ¿En cuánto?
3. Quédate callado tres segundos despues de que termine. ¿Qué hace?
4. Cambia de tema a mitad de la tarea. ¿Lo sigue o se encalla?
5. Di un numero largo (un IBAN, un numero de pedido). ¿Lo repite bien?
6. Habla con ruido de fondo. ¿Pide que repitas o inventa?
Seis escenarios, cinco minutos, y detectan más problemas reales que cien
casos de texto.
Errores habituales
Probar solo con tu propia voz. Hablas claro, sin ruido, con el micro
bueno y sabiendo lo que el agente espera oír. Tus usuarios no. Tres
grabaciones con acentos distintos valen más que cincuenta tuyas.
Medir la latencia hasta la respuesta completa. Lo que se percibe es el
silencio antes del primer sonido. Si tu panel de métricas no separa esas dos
cosas, está midiendo algo que a nadie le importa.
No probar la interrupción. Es la diferencia más grande entre una
conversación y un sistema de respuesta por voz de los de pulse uno. Y es lo
primero que hace un usuario real que tiene prisa.
Mandar audio real a probar fuera sin mirar dónde cae. El audio de una
persona hablando es dato personal, y a veces trae dentro datos de salud o
bancarios. Que Voxtral 3 se pueda alojar en la UE o desplegar en tu máquina
no es una casilla de cumplimiento: es lo que te permite probar con
grabaciones de verdad en vez de con actores leyendo un guion.
Qué vigilar
🟢 Las dos primeras capas se prueban hoy con lo que ya tienes. El arnés de
arriba son cien líneas y el guion de seis escenarios no necesita nada.
🟡 Los SDK de voz que se pueden desplegar en Europa o en tu propia máquina.
Voxtral 3 es de los primeros en el dossier con esa propiedad. Cuantos más
haya, menos trabajo de cumplimiento costará probar con datos reales.
🟡 La medición separada de la latencia. Claude Code 2.1.300 acaba de separar
en su telemetría las escrituras de caché por duración, que es el mismo
movimiento en otro terreno: una media te dice que algo va lento, el desglose
te dice dónde.
🔴 Probar automáticamente la capa de conversación. Interrupciones, silencios y
cambios de tema siguen evaluándose a mano. Si alguien publica un arnés que lo
haga de forma reproducible, la mitad de este artículo sobra. Hoy no existe.
