Dale voz (y oídos) a tu IA: pipeline de voz completo en local
Habla con tu IA por voz: Whisper para escuchar, un LLM local para responder y Kokoro para sintetizar. Un prototipo controlable, sin API obligatoria.

Un proyecto estupendo para entender un sistema multimodal pequeño: Whisper escucha, un LLM local responde y Kokoro sintetiza. Si todos los componentes y temporales están en tu máquina, puedes mantener el audio dentro de tu perímetro.
La arquitectura (simple de verdad)
🎙️ Micrófono → [Whisper STT] → texto
↓
[Tu LLM local] → respuesta texto
↓
🔊 [TTS] → voz sintética
Son 3 piezas independientes unidas por texto. Cada una la puedes mejorar por separado.
Ingredientes
- Ollama + un modelo local
pip install faster-whisper sounddevice soundfile "kokoro>=0.9.4"- En Linux, el backend de idiomas de Kokoro puede necesitar
espeak-ng. - Micrófono y altavoces :)
Pasos
-
Captura de audio en tiempo real:
import sounddevice as sd import soundfile as sf import numpy as np SAMPLE_RATE = 16000 grabacion = [] def callback(indata, frames, time, status): grabacion.append(indata.copy()) # grabar mientras se mantenga pulsada una tecla o detecte voz (VAD) with sd.InputStream(samplerate=SAMPLE_RATE, channels=1, callback=callback): input("Pulsa ENTER para dejar de grabar...") audio = np.concatenate(grabacion) sf.write("consulta.wav", audio, SAMPLE_RATE) -
Whisper transcribe (con
language="es"):from faster_whisper import WhisperModel stt = WhisperModel("small", device="cpu", compute_type="int8") segments, _ = stt.transcribe("consulta.wav", language="es") pregunta = " ".join(s.text for s in segments) -
Tu LLM responde:
import ollama respuesta = ollama.chat(model="qwen2.5:7b", messages=[ {"role": "system", "content": "Eres un asistente de voz. Responde en 2-3 frases, natural."}, {"role": "user", "content": pregunta}, ])["message"]["content"](El system prompt de “responde corto” suele ser importante en voz: nadie quiere escuchar un ensayo.)
-
Kokoro habla. La API actual devuelve fragmentos; en español usa el código
ey una voz cuyo prefijo también seae:from kokoro import KPipeline import numpy as np tts = KPipeline(lang_code="e") # español for _, _, audio in tts(respuesta, voice="ef_dora"): sd.play(audio.cpu().numpy() if hasattr(audio, "cpu") else np.asarray(audio), samplerate=24000) sd.wait()
Resultado esperado
La latencia total no está garantizada: depende de la captura, el modelo de Whisper, el LLM, la síntesis y si hay streaming. Mide por separado cada tramo antes de prometer una conversación natural.
Trucos de la experiencia
- Latencia es todo: usa modelos SLM (3-7B) para voz. Nadie espera respuestas de genio hablando; espera fluidez.
- VAD (Voice Activity Detection): usa
webrtcvadosilero-vadpara que empiece a escuchar solo cuando hablas. Es la diferencia entre “cutre” y “casi Alexa”. - Streaming: haz que el TTS empiece a hablar en cuanto llegue la primera frase del LLM, no al final. La latencia percibida cae a la mitad.
- Palabra de activación: en modo kiosko,
openwakewordte da un “Oye, En Local” casero.
A dónde llevarlo
La evolución natural es conectar acciones con permisos mínimos. Empieza con respuestas informativas y logs; solo después añade cosas como anotar una lista. Nunca des a un modelo de voz acceso directo a acciones irreversibles.