0Pricing
AI Agents · レッスン

エージェント応答における音声合成

エージェントパイプラインでOpenAI TTS、ElevenLabs、Google TTS APIを使用します。

「エージェント応答における音声合成」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。

エージェントにTTSが必要な理由

聞くだけでテキストで応答する音声エージェントは、本当の意味での音声エージェントではありません。音声合成(TTS)は、エージェントのテキスト応答を音声に変換し、音声による一連のやり取りを完成させます。

主なAPIは2つあります。OpenAI TTS(高速で低価格、6種類の音声)とElevenLabs(非常にリアルな音声、ストリーミング、音声クローン)です。

OpenAI TTSの基本

OpenAIのTTS APIは、数秒でテキストを音声に変換します。組み込みの音声はalloy、echo、fable、onyx、nova、shimmerの6種類です。出力形式としてMP3、opus、AAC、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

OpenAIによるストリーミングTTS

体感レイテンシーを短くするには、ファイル全体を待つのではなく、生成された音声をそのままストリーミングします。残りの音声を合成している間に、通話相手は音声の再生を開始できます。

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)

ElevenLabs SDK:高品質なTTS

ElevenLabsは、利用可能な音声の中でも最もリアルな音声を生成します。音声クローン、感情表現の調整、ストリーミングに対応しています。

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

ElevenLabsによるストリーミングTTS

ElevenLabsはストリーミングに対応しており、テキスト全体の合成が完了する前に音声チャンクを受け取れます。レイテンシーが重要な音声エージェントでは、これは欠かせません。

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}')

sounddeviceで音声を再生する

TTS音声を生成したら、ローカルデバイスで再生します。ローカルマシン上で動作するエージェントには、sounddeviceとsoundfileの組み合わせが、最も簡単なクロスプラットフォームの選択肢です。

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?')

pygameで音声を再生する

pygameを使うと、音声の再生中かどうかを確認したり、途中で停止したり、複数の音声を再生したりできます。音声エージェントの割り込み処理に役立ちます。

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)')

TTS応答のキャッシュ

挨拶、エラーメッセージ、メニューの案内などの定型句は、何度も合成されます。音声ファイルをキャッシュすれば、各フレーズを一度だけ合成すれば済みます。

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

LLMの長い応答の場合は、テキストを文に分割し、1文ずつ合成して再生します。残りのテキストが生成されている間も、ユーザーは約500ms以内に最初の文を聞き始められます。

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)

音声とモデルの選択

音声はエージェントのペルソナに合わせて選択してください。フォーマルまたはプロフェッショナルなエージェントには低めの音声(onyx、echo)、親しみやすいアシスタントには温かみのある音声(nova、shimmer)が適しています。

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')

発話速度とピッチの調整

状況によって、適した発話の特徴は異なります。通知アラートは少し速めに、ストーリーテリングはゆっくりと、より表情豊かにするとよいでしょう。TTS音声に後処理を施して、ピッチを変えずに速度を調整できます。

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

知識チェック

音声エージェントで文単位のTTS合成を使用する主な利点は何ですか。

まとめ:エージェントの応答における音声合成

OpenAI TTS(tts-1、tts-1-hd)は、6種類の音声、高速な合成、ストリーミング対応を提供し、ほとんどの音声エージェントに適しています。ElevenLabsは、より高品質でリアルな音声を提供し、eleven_turbo_v2による低レイテンシーのストリーミングに対応しています。

音声の再生には、sounddevice(シンプル)またはpygame(割り込み可能)を使用します。定型句をキャッシュして、APIの重複コストをなくしてください。長い応答には文単位の合成を使用して、体感レイテンシーを最小限に抑えます。MD5ハッシュを使ったTTSキャッシュにより、同じテキストには常にキャッシュから応答できます。

よくある質問

「エージェント応答における音声合成」レッスンは無料ですか?

はい。「エージェント応答における音声合成」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。

「エージェント応答における音声合成」で何を学びますか?

エージェントパイプラインでOpenAI TTS、ElevenLabs、Google TTS APIを使用します。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Agentsを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「エージェント応答における音声合成」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Agentsレッスンでコードを書いて実行できますか?

はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. WhisperとDeepgramによる音声認識
  2. エージェント応答における音声合成
  3. 音声会話ループの構築
  4. 音声エージェントのレイテンシ最適化
← AI Agentsに戻る