Muchos LLM, particularmente aquellos que son de código abierto, generalmente se han limitado a procesar texto o, ocasionalmente, texto con imágenes (Grandes Modelos Multimodales o LMM). Pero, ¿qué pasa si quieres comunicarte con tu LLM usando tu voz? Gracias al avance de potentes tecnologías de código abierto de conversión de voz a texto en los últimos años, esto es posible.
Analizaremos la integración de Llama 3 con un modelo de voz a texto, todo dentro de una interfaz fácil de usar. Esta fusión permite la comunicación (casi) en tiempo real con un LLM a través del habla. Nuestra exploración implica seleccionar Llama 3 8B como LLM, usar el modelo de voz a texto Whisper y las capacidades de NiceGUI, un marco que usa FastAPI en el backend y Vue3 en el frontend, interconectado con socket.io.
Después de leer esta publicación, podrás complementar un LLM con una nueva modalidad de audio. Esto le permitirá crear un flujo de trabajo completo de extremo a extremo y una interfaz de usuario que le permita usar su voz para ordenar y solicitar un LLM en lugar de escribir. Esta característica puede resultar especialmente beneficiosa para aplicaciones móviles, donde escribir en un teclado puede no ser tan fácil de usar como en las computadoras de escritorio. Además, la integración de esta funcionalidad puede mejorar la accesibilidad de su aplicación LLM, haciéndola más inclusiva para personas con discapacidades.
Estas son las herramientas y tecnologías con las que este proyecto le ayudará a familiarizarse:
- Llama 3 LLM
- Susurro STT
- Bonita GUI
- (Algunos) Javascript básico y Vue3
- La API replicada
En este proyecto, integramos varios componentes para permitir la interacción de voz con LLM (Large Language Models). En primer lugar, los LLM sirven como el núcleo de nuestro sistema, procesando entradas y generando resultados basados en un amplio conocimiento del idioma. A continuación, Whisper, nuestro modelo de voz a texto elegido, convierte la entrada hablada en texto, lo que permite una comunicación fluida con los LLM. Nuestra interfaz, basada en Vue3, incorpora componentes personalizados dentro del marco NiceGUI, proporcionando una interfaz de usuario intuitiva para la interacción. En el backend, el código personalizado combinado con FastAPI forma la base de la funcionalidad de la aplicación. Finalmente, Replicate.com proporciona la infraestructura de alojamiento para los modelos de ML, lo que garantiza un acceso confiable y escalabilidad. Juntos, estos componentes convergen para crear una aplicación básica para la interacción de voz (casi) en tiempo real con LLM.
NiceGUI aún no tiene un componente de grabación de audio, así que contribuí con uno a su conjunto de ejemplos: https://github.com/zauberzeug/nicegui/tree/main/examples/audio_recorder que estaré reutilizando aquí.
Para crear dicho componente, solo necesitamos definir un archivo .vue que defina lo que queremos:
<template>
<div>
<button class="record-button" @mousedown="startRecording" @mouseup="stopRecording">Hold to speak</button>
</div>
</template>
Aquí, básicamente, creamos un elemento de botón donde, al hacer clic, se llamará a un método. startRecording
y tan pronto como el mouse esté arriba llamará stopRecording
.
Para ello, definimos estos métodos principales:
methods: {
async requestMicrophonePermission() {
try {
this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (error) {
console.error('Error accessing microphone:', error);
}
},
async startRecording() {
try {
if (!this.stream) {
await this.requestMicrophonePermission();
}
this.audioChunks = [];
this.mediaRecorder = new MediaRecorder(this.stream);
this.mediaRecorder.addEventListener('dataavailable', event => {
if (event.data.size > 0) {
this.audioChunks.push(event.data);
}
});
this.mediaRecorder.start();
this.isRecording = true;
} catch (error) {
console.error('Error accessing microphone:', error);
}
},
stopRecording() {
if (this.isRecording) {
this.mediaRecorder.addEventListener('stop', () => {
this.isRecording = false;
this.saveBlob();
// this.playRecordedAudio();
});
this.mediaRecorder.stop();
}
}
Este código define tres métodos: requestMicrophonePermission
, startRecording
y stopRecording
. El requestMicrophonePermission
El método intenta de forma asincrónica acceder al micrófono del usuario usando navigator.mediaDevices.getUserMedia
, manejando cualquier error que pueda ocurrir. El startRecording
El método, también asíncrono, inicializa la grabación configurando una grabadora multimedia con el flujo de micrófono obtenido, mientras que el stopRecording
El método detiene el proceso de grabación y guarda el audio grabado.
Una vez finalizada la grabación, este código también emitirá un evento llamado 'audio_ready'
junto con datos de audio codificados en base64. Dentro del método, un nuevo FileReader
se crea el objeto. Al cargar el archivo, el onload
Se activa el evento, extrayendo los datos base64 del resultado del archivo cargado. Finalmente, estos datos base64 se emiten como parte del 'audio_ready'
evento usando $emit()
funcionar con la tecla 'audioBlobBase64'
que contiene los datos base64.
emitBlob() {
const reader = new FileReader();
reader.onload = () => {
const base64Data = reader.result.split(',')[1]; // Extracting base64 data from the result
this.$emit('audio_ready', { audioBlobBase64: base64Data });
};
}
Este evento será recibido por el backend junto con los datos base64.
El backend será básicamente el pegamento que une la entrada del usuario con los modelos ML alojados en Replicate.
Emplearemos dos modelos principales para nuestro proyecto:
openai/whisper
: Este modelo de secuencia a secuencia de Transformer está dedicado a tareas de conversión de voz a texto y es competente en la conversión de audio en texto. Capacitado en diversas tareas de procesamiento del habla, como reconocimiento de voz multilingüe, traducción de voz, identificación del idioma hablado y detección de actividad de voz.meta/meta-llama-3-8b-instruct
: La familia Llama 3, incluida esta variante de 8 mil millones de parámetros, es una familia LLM creada por Meta. Estos modelos de texto generativo previamente entrenados y ajustados a las instrucciones están optimizados específicamente para casos de uso de diálogo.
Para el primero, definimos una función simple que toma como entrada el audio base64 y llama a la API replicada:
def transcribe_audio(base64_audio):
audio_bytes = base64.b64decode(base64_audio)
prediction = replicate.run(
f"{MODEL_STT}:{VERSION}", input={"audio": io.BytesIO(audio_bytes), **ARGS}
)
text = "\n".join(segment["text"] for segment in prediction.get("segments", []))
return text
Que se puede utilizar fácilmente como:
with open("audio.ogx", "rb") as f:
content = f.read()_base64_audio = base64.b64encode(content).decode("utf-8")
_prediction = transcribe_audio(_base64_audio)
pprint.pprint(_prediction)
Luego, para el segundo componente, definimos una función similar:
def call_llm(prompt):
prediction = replicate.stream(MODEL_LLM, input={"prompt": prompt, **ARGS})
output_text = ""
for event in prediction:
output_text += str(event)
return output_text
Esto consultará el LLM y transmitirá las respuestas token por token al output_text
A continuación, definimos el flujo de trabajo completo en el siguiente método asíncrono:
async def run_workflow(self, audio_data):
self.prompt = "Transcribing audio..."
self.response_html = ""
self.audio_byte64 = audio_data.args["audioBlobBase64"]
self.prompt = await run.io_bound(
callback=transcribe_audio, base64_audio=self.audio_byte64
)
self.response_html = "Calling LLM..."
self.response = await run.io_bound(callback=call_llm, prompt=self.prompt)
self.response_html = self.response.replace("\n", "</br>")
ui.notify("Result Ready!")
Una vez que los datos de audio están listos, primero transcribimos el audio y luego, una vez hecho esto, llamamos al LLM y mostramos su respuesta. las variables self.prompt
y self.response_html
están vinculados a otros componentes de NiceGUI que se actualizan automáticamente. Si quieres saber más sobre cómo funciona, puedes consultar un tutorial anterior que escribí:
El resultado completo del flujo de trabajo se ve así:
¡Con buena pinta!
Lo que lleva más tiempo aquí es la transcripción del audio. El punto final siempre está activo en la replicación cuando lo verifico, pero esta versión es la v3 grande, que no es la más rápida. Los archivos de audio también son mucho más pesados para mover que el texto sin formato, lo que contribuye a la pequeña latencia.
Notas:
- Deberá configurar REPLICATE_API_TOKEN antes de ejecutar este código. Puede obtenerlo registrándose en replicate.com. Pude hacer estos experimentos usando su nivel gratuito.
- A veces, la transcripción se retrasa un poco y se devuelve después de un breve período de «cola».
- El código está en: https://github.com/CVxTz/LLM-Voice El punto de entrada es main.py.
En resumen, la integración de modelos de código abierto como Whisper y Llama 3 ha simplificado significativamente la interacción de voz con los LLM, haciéndola muy accesible y fácil de usar. Esta combinación es particularmente conveniente para los usuarios que prefieren no escribir, ya que ofrece una experiencia fluida. Sin embargo, esta es sólo la primera parte del proyecto; Habrá más mejoras por venir. Los próximos pasos incluyen habilitar la comunicación de voz bidireccional, brindar la opción de utilizar modelos locales para mejorar la privacidad, mejorar el diseño general para una interfaz más pulida, implementar conversaciones de múltiples turnos para interacciones más naturales, desarrollar una aplicación de escritorio para una mayor accesibilidad, y optimizar la latencia para el procesamiento de voz a texto en tiempo real. Con estas mejoras, el objetivo es mejorar la experiencia de interacción de voz con los LLM, haciéndola más fácil de usar para aquellos, como a mí, a los que no les gusta mucho escribir.
Déjame saber en qué mejoras crees que debería trabajar primero.