0Pricing
AI Agents · Lección

Conversión de texto a voz en las respuestas de agentes

API de OpenAI TTS, ElevenLabs y Google TTS en pipelines de agentes.

Conversión de texto a voz en las respuestas de agentes es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.

Por qué los agentes necesitan TTS

Un agente de voz que solo escucha, pero responde mediante texto, no es un verdadero agente de voz. Text-to-Speech (TTS) convierte las respuestas de texto del agente en audio hablado, completando el ciclo de voz.

Hay dos API destacadas: OpenAI TTS (rápida, económica y con 6 voces) y ElevenLabs (voces ultrarrealistas, streaming y clonación de voz).

Conceptos básicos de OpenAI TTS

La API de TTS de OpenAI convierte texto en voz en cuestión de segundos. Incluye seis voces: alloy, echo, fable, onyx, nova y shimmer. Admite los formatos de salida MP3, opus, AAC y FLAC.

import openai
import os

client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

def text_to_speech(text, voice='alloy', output_path='response.mp3'):
    response = client.audio.speech.create(
        model='tts-1',          # tts-1 (fast) or tts-1-hd (higher quality)
        voice=voice,            # alloy, echo, fable, onyx, nova, shimmer
        input=text,
        response_format='mp3'   # mp3, opus, aac, flac
    )
    with open(output_path, 'wb') as f:
        f.write(response.content)
    print(f'Audio saved to: {output_path}')
    return output_path

# Available voices:
# alloy    - neutral, balanced
# echo     - warm, conversational
# fable    - expressive
# onyx     - deep, authoritative
# nova     - friendly, upbeat
# shimmer  - soft, clear

Streaming de TTS con OpenAI

Para reducir la latencia percibida, transmita el audio a medida que se genera en lugar de esperar al archivo completo. La persona que llama puede comenzar a reproducir el audio mientras el resto todavía se sintetiza.

import openai
import os

client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

def stream_tts_to_file(text, output_path, voice='nova'):
    with client.audio.speech.with_streaming_response.create(
        model='tts-1',
        voice=voice,
        input=text
    ) as response:
        response.stream_to_file(output_path)
    return output_path

def stream_tts_to_bytes(text, voice='nova'):
    audio_chunks = []
    with client.audio.speech.with_streaming_response.create(
        model='tts-1',
        voice=voice,
        input=text
    ) as response:
        for chunk in response.iter_bytes(chunk_size=4096):
            audio_chunks.append(chunk)
    return b''.join(audio_chunks)

SDK de ElevenLabs: TTS de alta calidad

ElevenLabs produce algunas de las voces más realistas disponibles. Admite clonación de voz, control del tono emocional y streaming.

Instálelo con pip install elevenlabs.

from elevenlabs import ElevenLabs, VoiceSettings
import os

client = ElevenLabs(api_key=os.getenv('ELEVENLABS_API_KEY'))

def elevenlabs_tts(text, voice_id='pNInz6obpgDQGcFmaJgB', output_path='response.mp3'):
    # Common voice IDs:
    # Rachel:  21m00Tcm4TlvDq8ikWAM
    # Adam:    pNInz6obpgDQGcFmaJgB
    # Bella:   EXAVITQu4vr4xnSDxMaL

    audio = client.generate(
        text=text,
        voice=voice_id,
        model='eleven_multilingual_v2',  # supports 29 languages
        voice_settings=VoiceSettings(
            stability=0.5,        # 0.0-1.0: lower = more expressive
            similarity_boost=0.8, # 0.0-1.0: higher = closer to original voice
            style=0.3             # 0.0-1.0: style exaggeration
        )
    )

    with open(output_path, 'wb') as f:
        for chunk in audio:
            f.write(chunk)

    return output_path

Streaming de TTS con ElevenLabs

ElevenLabs admite streaming: recibe fragmentos de audio antes de que se sintetice el texto completo. Esto es fundamental para los agentes de voz en los que la latencia es importante.

from elevenlabs import ElevenLabs
import os

client = ElevenLabs(api_key=os.getenv('ELEVENLABS_API_KEY'))

def streaming_tts(text, voice_id='pNInz6obpgDQGcFmaJgB'):
    audio_stream = client.generate(
        text=text,
        voice=voice_id,
        model='eleven_turbo_v2',  # fastest model, optimized for streaming
        stream=True
    )
    return audio_stream  # yields audio chunks as they are generated

def stream_to_file(text, output_path, voice_id='pNInz6obpgDQGcFmaJgB'):
    stream = streaming_tts(text, voice_id)
    with open(output_path, 'wb') as f:
        for chunk in stream:
            if chunk:
                f.write(chunk)
    print(f'Streaming TTS complete: {output_path}')

Reproducción de audio con sounddevice

Después de generar el audio TTS, reprodúzcalo en el dispositivo local. sounddevice junto con soundfile es la opción multiplataforma más sencilla para un agente que se ejecuta en una máquina local.

Instálelo con pip install sounddevice soundfile.

import sounddevice as sd
import soundfile as sf
import numpy as np
import tempfile
import os

def play_audio_file(audio_path):
    data, sample_rate = sf.read(audio_path)
    sd.play(data, sample_rate)
    sd.wait()  # block until playback finishes

def play_mp3_bytes(audio_bytes):
    # Write to temp file then play (soundfile needs a file)
    with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f:
        f.write(audio_bytes)
        tmp_path = f.name
    try:
        play_audio_file(tmp_path)
    finally:
        os.unlink(tmp_path)

def say(text, voice='nova'):
    audio_bytes = stream_tts_to_bytes(text, voice=voice)
    play_mp3_bytes(audio_bytes)

# Usage
say('Hello! How can I help you today?')

Reproducción de audio con pygame

pygame ofrece un mayor control: permite comprobar si el audio todavía se está reproduciendo, detenerlo antes de tiempo y reproducir varios sonidos. Resulta útil para gestionar interrupciones en agentes de voz.

Instálelo con pip install pygame.

import pygame
import io
import tempfile
import os

pygame.mixer.init(frequency=44100, size=-16, channels=1, buffer=512)

def play_audio_pygame(audio_bytes):
    with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f:
        f.write(audio_bytes)
        tmp_path = f.name

    try:
        pygame.mixer.music.load(tmp_path)
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            pygame.time.wait(50)  # check every 50ms
    finally:
        pygame.mixer.music.stop()
        os.unlink(tmp_path)

def stop_audio():
    if pygame.mixer.music.get_busy():
        pygame.mixer.music.stop()
        print('Audio stopped (interrupt)')

Almacenamiento en caché de respuestas TTS

Las frases habituales, como saludos, mensajes de error e indicaciones de menú, se sintetizan repetidamente. Almacene sus archivos de audio en caché para pagar la síntesis una sola vez por frase.

import hashlib
import os

TTS_CACHE_DIR = '/tmp/tts_cache'
os.makedirs(TTS_CACHE_DIR, exist_ok=True)

def cached_tts(text, voice='nova'):
    cache_key = hashlib.md5(f'{voice}:{text}'.encode()).hexdigest()
    cache_path = os.path.join(TTS_CACHE_DIR, f'{cache_key}.mp3')

    if os.path.exists(cache_path):
        print('TTS cache hit')
        return cache_path

    # Generate and save
    audio_bytes = stream_tts_to_bytes(text, voice=voice)
    with open(cache_path, 'wb') as f:
        f.write(audio_bytes)

    print(f'TTS cached: {cache_path}')
    return cache_path

# Pre-warm cache for common phrases on startup
COMMON_PHRASES = [
    'Hello! How can I help you today?',
    'I did not catch that. Could you repeat?',
    'Let me look that up for you.',
    'Thank you, goodbye!'
]

def prewarm_tts_cache():
    for phrase in COMMON_PHRASES:
        cached_tts(phrase)
    print(f'Pre-warmed {len(COMMON_PHRASES)} phrases')

TTS oración por oración para streaming

Para respuestas largas del LLM, divida el texto en oraciones y sintetícelas y reprodúzcalas una por una. El usuario escucha la primera oración en unos 500 ms mientras el resto todavía se genera.

import re

def split_into_sentences(text):
    sentences = re.split(r'(?<=[.!?])\s+', text.strip())
    return [s.strip() for s in sentences if s.strip()]

def stream_response_as_voice(llm_response_text, voice='nova'):
    sentences = split_into_sentences(llm_response_text)
    print(f'Speaking {len(sentences)} sentences')

    for i, sentence in enumerate(sentences):
        if not sentence:
            continue
        print(f'Speaking ({i+1}/{len(sentences)}): {sentence[:60]}')
        cache_path = cached_tts(sentence, voice=voice)
        play_audio_file(cache_path)

# Example
response = 'The weather today is sunny and 22 degrees. Perfect for outdoor activities. Bring sunscreen if you plan to be outside for more than an hour.'
stream_response_as_voice(response)

Elección de la voz y el modelo

Adapte la elección de la voz a la personalidad del agente. Utilice voces más graves (onyx, echo) para agentes formales o profesionales, y voces más cálidas (nova, shimmer) para asistentes amables.

VOICE_PROFILES = {
    'assistant':    {'service': 'openai',     'voice': 'nova',   'model': 'tts-1'},
    'professional': {'service': 'openai',     'voice': 'onyx',   'model': 'tts-1-hd'},
    'narrator':     {'service': 'openai',     'voice': 'fable',  'model': 'tts-1-hd'},
    'premium':      {'service': 'elevenlabs', 'voice_id': '21m00Tcm4TlvDq8ikWAM', 'model': 'eleven_multilingual_v2'}
}

def speak(text, persona='assistant'):
    profile = VOICE_PROFILES.get(persona, VOICE_PROFILES['assistant'])

    if profile['service'] == 'openai':
        audio_bytes = stream_tts_to_bytes(text, voice=profile['voice'])
        play_mp3_bytes(audio_bytes)
    elif profile['service'] == 'elevenlabs':
        output_path = elevenlabs_tts(text, voice_id=profile['voice_id'])
        play_audio_file(output_path)

speak('Your meeting has been rescheduled.', persona='professional')

Ajuste de la velocidad y el tono del habla

Los distintos contextos requieren características de habla diferentes. Las alertas de notificación deben ser ligeramente más rápidas; las narraciones, más lentas y expresivas. Procese posteriormente el audio TTS para ajustar la velocidad sin cambiar el tono.

from pydub import AudioSegment
from pydub.effects import speedup
import os

def adjust_speech_speed(audio_path, speed_factor=1.0, output_path=None):
    if speed_factor == 1.0:
        return audio_path

    audio = AudioSegment.from_file(audio_path)

    if speed_factor > 1.0:
        # Speed up without changing pitch
        audio = speedup(audio, playback_speed=speed_factor)
    else:
        # Slow down: overlay with silence (simple method)
        # For production: use ffmpeg with atempo filter
        slow_factor = 1.0 / speed_factor
        audio = audio._spawn(
            audio.raw_data,
            overrides={'frame_rate': int(audio.frame_rate / slow_factor)}
        ).set_frame_rate(audio.frame_rate)

    if output_path is None:
        base = os.path.splitext(audio_path)[0]
        output_path = f'{base}_speed{speed_factor}.mp3'

    audio.export(output_path, format='mp3')
    return output_path

# Usage
path = text_to_speech('Your meeting starts in 5 minutes.')
fast_path = adjust_speech_speed(path, speed_factor=1.2)  # 20% faster for alerts

Comprobación de conocimientos

¿Cuál es el principal beneficio de utilizar la síntesis TTS oración por oración en un agente de voz?

Resumen: conversión de texto a voz en las respuestas de los agentes

OpenAI TTS (tts-1, tts-1-hd) ofrece 6 voces, síntesis rápida y compatibilidad con streaming, por lo que es ideal para la mayoría de los agentes de voz. ElevenLabs ofrece voces realistas de mayor calidad con eleven_turbo_v2 para streaming de baja latencia.

Reproduzca el audio con sounddevice (sencillo) o pygame (interrumpible). Almacene en caché las frases habituales para eliminar los costes repetidos de la API. Utilice la síntesis oración por oración para las respuestas largas y así minimizar la latencia percibida. El almacenamiento en caché de TTS mediante hashes MD5 garantiza que el texto idéntico siempre se sirva desde la caché.

Preguntas frecuentes

¿La lección «Conversión de texto a voz en las respuestas de agentes» es gratis?

Sí — el texto completo de «Conversión de texto a voz en las respuestas de agentes» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.

¿Qué aprenderé en «Conversión de texto a voz en las respuestas de agentes»?

API de OpenAI TTS, ElevenLabs y Google TTS en pipelines de agentes. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Agents?

No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Conversión de texto a voz en las respuestas de agentes»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Agents?

Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Conversión de voz a texto con Whisper y Deepgram
  2. Conversión de texto a voz en las respuestas de agentes
  3. Creación de un ciclo de conversación por voz
  4. Optimización de la latencia para agentes de voz
← Volver a AI Agents