2025/01/17 | AI
with openai
A lo largo del proceso para configurar y solucionar problemas con el asistente de voz, hemos realizado ajustes en el sistema operativo, configuraciones de audio y código del asistente. A continuación, detallo los principales cambios:
Se crea un entorno virtual para proyecto de asistente virtual con OpenAI. Creamos un archivo assistant.py (nano assistant.py) con el siguiente código (sustituimos "nuestra api", por la que se genere en OpenAI para el proyecto).
MATERIAL UTILIZADO
Aplicaciones instaladas en el entorno virtual:
Para su instalación lo más fácil puede ser crear un archivo por ejemplo llamado requirements.txt, pegar la lista de aplicaciones, guardar, y desde fuera ejecutar con el entorno activado:
pip install -r requirements.txt
Contenido de requirements.txt
annotated-types==0.7.0
anyio==4.8.0
certifi==2024.12.14
charset-normalizer==3.4.1
click==8.1.8
distro==1.9.0
gTTS==2.5.4
h11==0.14.0
httpcore==1.0.7
httpx==0.28.1
idna==3.10
jiter==0.8.2
openai==1.59.9
PyAudio==0.2.14
pydantic==2.10.5
pydantic_core==2.27.2
pyttsx3==2.98
requests==2.32.3
sniffio==1.3.1
SpeechRecognition==3.14.0
tqdm==4.67.1
typing_extensions==4.12.2
urllib3==2.3.0
Código del archivo principal creado en el entorno virtual: assistant.py (para después ejecutar mediante "python assistant.py"
from openai import OpenAI
import pyttsx3
import speech_recognition as sr
import os
import subprocess
from gtts import gTTS
# Configura tu clave de API desde una variable de entorno
api_key = 'AQUI VA LA API KEY CREADA EN OPENAI'
if not api_key:
raise ValueError("Por favor, configura la variable de entorno 'OPENAI_API_KEY' con tu clave de OpenAI.")
client = OpenAI(api_key=api_key)
# Configura el motor de texto a voz
engine = pyttsx3.init()
engine.setProperty('rate', 150)
FLAG_FILE = "assistant_initialized.flag" # Archivo que indica si ya se ejecutó el comando inicial
os.system("fuser -k /dev/snd/*")
def stop_audio_processes():
"""
Finaliza los procesos que ocupan los dispositivos de audio.
"""
os.system("killall arecord")
os.system("killall aplay")
def speak(text):
"""
Convierte texto a voz usando Google Text-to-Speech (gTTS) y lo reproduce.
"""
tts = gTTS(text=text, lang='es', slow=False)
tts.save("response.mp3")
os.system("mpg123 response.mp3") # Reproduce el archivo de audio
def record_audio(file_name="temp_audio.wav", duration=5):
"""
Graba audio desde el micrófono sin convertirlo.
"""
stop_audio_processes()
print("Grabando...")
try:
# Graba directamente en mono sin conversión adicional
subprocess.run(
["arecord", "-D", "plughw:2,0", "-f", "S16_LE", "-c", "1", "-r", "44100", "-t", "wav", "-d", str(duration), file_name],
check=True
)
print("Grabación completa.")
return file_name
except subprocess.CalledProcessError as e:
print(f"Error al grabar audio: {e}")
return None
def transcribe_audio(file_name="temp_audio.wav"):
"""
Convierte el archivo de audio grabado a texto usando speech_recognition.
"""
recognizer = sr.Recognizer()
try:
with sr.AudioFile(file_name) as source:
print("Procesando audio para transcripción...")
audio = recognizer.record(source)
text = recognizer.recognize_google(audio, language="es-ES")
return text
except sr.UnknownValueError:
return ""
except sr.RequestError as e:
return f"Error en el servicio de reconocimiento de voz: {e}"
def get_response(prompt):
"""
Obtiene la respuesta de OpenAI ChatGPT.
"""
try:
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "Eres un asistente útil."},
{"role": "user", "content": prompt}
]
)
if response and response.choices:
return response.choices[0].message.content
else:
return "Lo siento, no pude generar una respuesta. Inténtalo de nuevo."
except Exception as e:
return f"Error al conectarse con OpenAI: {e}"
if __name__ == "__main__":
# Solo ejecuta `fuser -k` la primera vez
if not os.path.exists(FLAG_FILE):
print("Finalizando procesos de audio por primera vez...")
stop_audio_processes()
with open(FLAG_FILE, "w") as flag:
flag.write("initialized")
print("Asistente iniciado. Di 'hola lucy' para activarlo y 'eso es todo lucy' para desactivarlo.")
assistant_active = False # Comienza en modo inactivo
while True:
try:
# Grabar audio
audio_file = record_audio()
if not audio_file:
print("No se pudo grabar el audio. Intentando nuevamente...")
continue
# Transcribir el audio grabado
user_input = transcribe_audio(audio_file).lower()
print(f"Tú: {user_input}")
# Activar el asistente
if any(trigger in user_input for trigger in ["hola lucy", "hola luci", "hola", "lucy", "luci"]):
assistant_active = True
speak("Hola, dime.")
continue
# Desactivar el asistente
if "eso es todo" in user_input:
assistant_active = False
speak("De acuerdo.")
continue
# Responder solo si el asistente está activo
if assistant_active and user_input:
response = get_response(user_input)
print(f"Asistente: {response}")
speak(response)
except Exception as e:
print(f"Error durante la ejecución: {e}")
finally:
# Limpieza de archivos temporales
if os.path.exists("temp_audio.wav"):
os.remove("temp_audio.wav")
Deshabilitar PulseAudio:
systemctl --user stop pulseaudio.service
systemctl --user disable pulseaudio.service
pulseaudio --kill
killall pulseaudio
fuser -v /dev/snd/*
Salida:
kill -9 <PID>
kill -9 1092
fuser -k /dev/snd/*
~/.asoundrc
# Micrófono USB (Entrada)
pcm.input {
type plug
slave {
pcm "hw:2,0"
format S16_LE
channels 1
rate 44100
}
}
# Salida Analógica de 3.5 mm (Salida)
pcm.output {
type plug
slave {
pcm "hw:1,0"
format S16_LE
channels 2
rate 44100
}
}
pcm.!default pcm.output
sudo alsactl init
Listar Dispositivos de Audio:
aplay -l
arecord -l
fuser -v /dev/snd/*
Grabar Audio desde el Micrófono USB:
arecord -D plughw:2,0 -f S16_LE -c 1 -r 44100 test_audio.wav
sox test_audio.wav test_audio_stereo.wav channels 2
aplay -D plughw:1,0 test_audio_stereo.wav
killall arecord
killall aplay
Grabación de Audio: Configuramos la función para grabar desde el micrófono USB (plughw:2,0
) y convertir el audio a estéreo si es necesario.
Reproducción de Audio: Configuramos la función para reproducir audio en la salida analógica de 3.5 mm (plughw:1,0
).
Gestión de Errores: Agregamos manejo de excepciones para evitar que el asistente falle por errores inesperados.
Limpieza de Archivos Temporales: Eliminamos archivos de audio temporales después de cada ejecución.
~/.asoundrc
. arecord
, aplay
, sox
, fuser
.El asistente virtual funciona, pero requiere algunas mejoras como: