0Pricing
AI Agents · บทเรียน

การปรับเวลาแฝงให้เหมาะที่สุดสำหรับตัวแทนเสียง

การสตรีม TTS การแบ่งคำตอบเป็นส่วน และการลดเวลาแฝงก่อนพูดคำแรก

การปรับเวลาแฝงให้เหมาะที่สุดสำหรับตัวแทนเสียง เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

เหตุผลที่ความหน่วงสำคัญต่อเสียง

ในการสนทนาด้วยข้อความ ความล่าช้า 3 วินาทียังยอมรับได้ แต่ในการสนทนาด้วยเสียง ความล่าช้าที่เกิน 1.5 วินาที จะให้ความรู้สึกไม่เป็นธรรมชาติและทำให้จังหวะการสนทนาสะดุด

ความหน่วงของเสียงมีองค์ประกอบหลักสามส่วน ได้แก่ เวลาถอดเสียง (STT) เวลาประมวลผลของ LLM (TTFT + การสร้างผลลัพธ์) และเวลาสังเคราะห์ TTS การปรับปรุงแต่ละส่วนจะช่วยเสริมกันจนทำให้ประสบการณ์ของผู้ใช้ดีขึ้นอย่างมาก

การวัดงบความหน่วง

ก่อนปรับปรุงประสิทธิภาพ ให้ измерเวลาของแต่ละองค์ประกอบ ติดตั้งการวัดเวลาในวงจรเพื่อดูว่าเวลาถูกใช้ไปกับส่วนใดจริง

import time

def voice_loop_timed():
    timing = {}

    # 1. Record
    t0 = time.perf_counter()
    audio, sr = record_until_silence()
    timing['record'] = time.perf_counter() - t0

    # 2. Transcribe
    t0 = time.perf_counter()
    audio_path = audio_to_file(audio, sr)
    user_text = transcribe_file(audio_path)
    timing['transcription'] = time.perf_counter() - t0

    # 3. LLM
    t0 = time.perf_counter()
    agent_text = agent.respond(user_text)
    timing['llm'] = time.perf_counter() - t0

    # 4. TTS
    t0 = time.perf_counter()
    say(agent_text)
    timing['tts'] = time.perf_counter() - t0

    print('Latency breakdown:')
    total = sum(timing.values())
    for step, duration in timing.items():
        print(f'  {step:15} {duration*1000:.0f}ms ({duration/total*100:.0f}%)')

การสตรีม TTS: เล่นเสียงระหว่างการสร้าง

วิธีลดความหน่วงที่เห็นผลมากที่สุดคือการสตรีม TTS แทนที่จะรอให้สังเคราะห์เสียงทั้งหมดเสร็จ ให้เริ่มเล่นชิ้นส่วนเสียงแรกภายในประมาณ 200 มิลลิวินาที ขณะที่ส่วนที่เหลือถูกสร้างไปพร้อมกัน

import threading
import queue
import sounddevice as sd
import numpy as np
import io

def stream_tts_and_play(text, voice='nova'):
    audio_queue = queue.Queue()

    def synthesize():
        from openai import OpenAI
        import os
        client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
        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_queue.put(chunk)
        audio_queue.put(None)  # sentinel

    synth_thread = threading.Thread(target=synthesize, daemon=True)
    synth_thread.start()

    # Collect and play (buffering first 2 chunks for smooth start)
    audio_buffer = b''
    min_buffer = 8192
    import pygame
    pygame.mixer.init()

    while True:
        chunk = audio_queue.get()
        if chunk is None:
            break
        audio_buffer += chunk
        if len(audio_buffer) >= min_buffer:
            # play buffer ...
            pass  # simplified — real impl streams to sounddevice

    synth_thread.join()

การสตรีม TTS ทีละประโยค

แนวทางการสตรีมที่ใช้งานได้จริงที่สุดคือแบ่งคำตอบจาก LLM เป็นประโยค แล้วสังเคราะห์และเล่นทีละประโยค ประโยคแรกจะเริ่มเล่นภายในประมาณ 300 มิลลิวินาที

import re
import concurrent.futures

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

def tts_and_play_streaming(text, voice='nova'):
    sentences = split_sentences(text)
    if not sentences:
        return

    # Prefetch next sentence while current is playing
    executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)

    # Kick off synthesis of first sentence
    futures = []
    for sentence in sentences:
        futures.append(executor.submit(cached_tts, sentence, voice))

    # Play each sentence as soon as it's ready
    for future in futures:
        audio_path = future.result(timeout=10)
        play_audio_file(audio_path)

    executor.shutdown(wait=False)

# Key insight: while sentence 1 plays (~2 seconds), sentence 2 is being synthesized in parallel

TTFT: การปรับปรุงเวลาจนถึงโทเค็นแรก

TTFT (เวลาจนถึงโทเค็นแรก) คือความล่าช้าระหว่างการส่งคำขอไปยัง LLM กับการได้รับโทเค็นแรกของคำตอบ การปรับปรุง TTFT ช่วยให้ผู้ใช้ได้ยินจุดเริ่มต้นของคำตอบเร็วขึ้น

ใช้ คำตอบแบบสตรีมจาก LLM และส่งโทเค็นเข้า TTS ทันทีที่ตรวจพบขอบเขตของประโยค

import openai
import os

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

def stream_llm_to_tts(user_text, conversation_history, voice='nova'):
    buffer = ''

    stream = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=conversation_history + [{'role': 'user', 'content': user_text}],
        stream=True  # streaming enabled
    )

    for chunk in stream:
        delta = chunk.choices[0].delta.content or ''
        buffer += delta

        # Check if a sentence is complete
        if buffer.endswith(('.', '!', '?')) and len(buffer) > 20:
            sentence = buffer.strip()
            print(f'Queuing TTS: {sentence[:50]}')
            # Synthesize and play immediately (non-blocking)
            audio_path = cached_tts(sentence, voice)
            play_audio_file(audio_path)
            buffer = ''

    # Flush remaining buffer
    if buffer.strip():
        audio_path = cached_tts(buffer.strip(), voice)
        play_audio_file(audio_path)

การสร้างคำตอบทั่วไปไว้ล่วงหน้า

คำตอบบางอย่างของเอเจนต์คาดเดาได้ เช่น คำทักทาย ข้อความแสดงข้อผิดพลาด และข้อความบอกว่ากำลังคิด («ขอเวลาตรวจสอบให้คุณสักครู่») ให้สร้างเสียงของคำตอบเหล่านี้ไว้ล่วงหน้าเมื่อเริ่มต้นระบบ เพื่อให้เล่นได้ทันทีโดยไม่มีความหน่วง

import os

PRE_GENERATED = {
    'greeting':      'Hello! How can I help you today?',
    'thinking':      'Let me look that up for you.',
    'not_found':     'I could not find information on that. Could you rephrase?',
    'error':         'Sorry, something went wrong. Please try again.',
    'goodbye':       'Goodbye! Have a great day.',
    'clarify':       'Could you give me a bit more detail?',
    'working_on_it': 'Working on it, this may take a moment.'
}

pre_gen_cache = {}

def prewarm_responses(voice='nova'):
    for key, text in PRE_GENERATED.items():
        audio_path = cached_tts(text, voice=voice)
        pre_gen_cache[key] = audio_path
    print(f'Pre-generated {len(pre_gen_cache)} common responses')

def instant_response(key):
    path = pre_gen_cache.get(key)
    if path:
        play_audio_file(path)
    else:
        say(PRE_GENERATED.get(key, ''))

# Usage: while LLM is thinking, play a stall message instantly
instant_response('thinking')

ความหน่วงของ TTS ภายในเครื่องเทียบกับ TTS บนคลาวด์

TTS บนคลาวด์เพิ่มเวลาการเดินทางไปกลับของเครือข่ายประมาณ 100–300 มิลลิวินาที สำหรับคำตอบสั้นหรือวลีที่ใช้บ่อย เครื่องมือ TTS ภายในเครื่อง อาจทำงานได้เร็วกว่า โดยแลกกับคุณภาพเสียง

pyttsx3 เป็น TTS แบบออฟไลน์ที่ง่ายที่สุดสำหรับ Python

import pyttsx3
import time

# Local TTS with pyttsx3
def local_tts(text, voice_index=0, rate=175):
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    if voice_index < len(voices):
        engine.setProperty('voice', voices[voice_index].id)
    engine.setProperty('rate', rate)  # words per minute
    t0 = time.perf_counter()
    engine.say(text)
    engine.runAndWait()
    print(f'Local TTS latency: {(time.perf_counter()-t0)*1000:.0f}ms')

# Comparison (rough benchmarks):
# pyttsx3 (local):     ~50ms start, robotic quality
# OpenAI TTS-1:        ~200-400ms, good quality
# ElevenLabs turbo:    ~150-250ms, excellent quality
# OpenAI TTS-1-hd:     ~400-800ms, best quality

# Recommendation:
# Real-time voice agent -> OpenAI TTS-1 or ElevenLabs turbo
# Quality recording     -> OpenAI TTS-1-hd or ElevenLabs standard

การเลือกโมเดล LLM ที่เร็วกว่า

การเลือกโมเดลส่งผลอย่างมากต่อ TTFT สำหรับงานเอเจนต์เสียงส่วนใหญ่ GPT-4o-mini เร็วกว่า GPT-4o ประมาณ 5–10 เท่า ให้ใช้โมเดลขนาดเล็กที่สุดที่ยังตอบสนองข้อกำหนดด้านคุณภาพได้

# Model latency comparison (rough benchmarks for voice agent use case):
MODEL_BENCHMARKS = {
    'gpt-4o-mini':   {'ttft_ms': 300,  'quality': 'good',      'cost': 'very low'},
    'gpt-4o':        {'ttft_ms': 800,  'quality': 'excellent',  'cost': 'medium'},
    'claude-haiku':  {'ttft_ms': 250,  'quality': 'good',       'cost': 'very low'},
    'claude-sonnet': {'ttft_ms': 600,  'quality': 'excellent',  'cost': 'medium'},
    'llama3-8b':     {'ttft_ms': 100,  'quality': 'decent',     'cost': 'free (local)'},
}

# Strategy: use fast model for simple factual queries,
# fall back to powerful model for complex reasoning
def select_model(question):
    # Short, simple questions -> fast model
    if len(question.split()) < 15:
        return 'gpt-4o-mini'
    # Complex, multi-step -> better model
    if any(kw in question.lower() for kw in ['analyze', 'compare', 'explain why', 'write a']):
        return 'gpt-4o'
    return 'gpt-4o-mini'

if __name__ == '__main__':
    for q in ['What time is it in Tokyo?', 'Analyze why sales dropped last quarter and compare to competitors']:
        print(f'{select_model(q)!r} chosen for: "{q}"')

การแคชคำตอบสำหรับคำถามซ้ำ

ผู้ใช้มักถามคำถามเดิมหรือคำถามที่คล้ายกันซ้ำหลายครั้ง ให้แคชคำตอบจาก LLM + TTS สำหรับคำถามที่เหมือนกัน เพื่อให้ตอบได้ทันทีเมื่อมีการถามซ้ำ

import hashlib
import json

RESPONSE_CACHE = {}  # in production: use Redis with TTL

def get_cache_key(user_text):
    # Normalize: lowercase, strip punctuation
    import re
    normalized = re.sub(r'[^a-z0-9 ]', '', user_text.lower()).strip()
    return hashlib.md5(normalized.encode()).hexdigest()

def cached_agent_respond(user_text, voice='nova'):
    key = get_cache_key(user_text)

    if key in RESPONSE_CACHE:
        print('Response cache hit!')
        entry = RESPONSE_CACHE[key]
        play_audio_file(entry['audio_path'])
        return entry['text']

    # Cache miss
    text = agent.respond(user_text)
    audio_path = cached_tts(text, voice=voice)

    RESPONSE_CACHE[key] = {'text': text, 'audio_path': audio_path}
    play_audio_file(audio_path)
    return text

เป้าหมายเวลาแฝงตั้งแต่ต้นจนจบ

เมื่อใช้การเพิ่มประสิทธิภาพทั้งหมดแล้ว เอเจนต์เสียงที่สร้างมาอย่างดีควรใช้เวลาน้อยกว่า 1 วินาที ตั้งแต่ผู้ใช้พูดจบจนเริ่มตอบสนอง

# Target latency budget breakdown (1000ms total):

LATENCY_BUDGET = {
    'silence_detection_end':   0,    # user stops speaking
    'audio_processing':        50,   # RMS check, noise gate
    'transcription_whisper':   400,  # STT API call
    'llm_ttft':                300,  # time to first token (gpt-4o-mini)
    'tts_first_sentence':      200,  # first sentence synthesized
    'playback_start':          1000  # TOTAL: user hears response within 1 second
}

# Optimizations applied:
# - Streaming LLM responses (saves 200-500ms vs waiting for full response)
# - Sentence-by-sentence TTS (play while rest generates)
# - gpt-4o-mini instead of gpt-4o (saves 500ms TTFT)
# - TTS cache for common phrases (saves 200ms on cached phrases)
# - Pre-generated stall messages ('Let me check...' plays instantly)

print('Target: < 1000ms from speech end to first audio byte played')
print('Typical with optimizations: 600-900ms')

การถอดเสียงแบบอะซิงโครนัสซ้อนทับ

ขณะที่เอเจนต์กำลังสร้างคำตอบและพูดคำตอบนั้นอยู่ ให้เริ่มบันทึกข้อมูลนำเข้าถัดไปจากผู้ใช้ทันที การทำให้ขั้นตอนต่าง ๆ ในกระบวนการทำงานซ้อนทับกันช่วยลดเวลาว่างระหว่างแต่ละรอบการสนทนา

import threading
import time

class PipelineOverlapAgent:
    def __init__(self):
        self.next_audio = None
        self.recording_thread = None

    def start_background_recording(self):
        def record():
            self.next_audio = record_until_silence()
        self.recording_thread = threading.Thread(target=record, daemon=True)
        self.recording_thread.start()

    def get_recorded_audio(self, timeout=30):
        if self.recording_thread:
            self.recording_thread.join(timeout=timeout)
        audio = self.next_audio
        self.next_audio = None
        return audio

    def conversation_turn(self, audio, sr):
        # Transcribe and run agent
        text = transcribe_file(audio_to_file(audio, sr))
        if not text.strip():
            return

        # Start recording NEXT turn BEFORE speaking
        # (user can start speaking as soon as agent starts)
        response = agent.respond(text)

        # Start next recording while speaking
        self.start_background_recording()

        # Speak current response
        speak_with_interrupt(response)

        # Next audio is already being captured in background
        return self.get_recorded_audio()

ตรวจสอบความรู้

การเพิ่มประสิทธิภาพเพียงรายการใดมักช่วยลดเวลาแฝงของเอเจนต์เสียงได้มากที่สุด

สรุป: การเพิ่มประสิทธิภาพเวลาแฝงสำหรับเอเจนต์เสียง

ชุดการเพิ่มประสิทธิภาพเวลาแฝงของเสียงประกอบด้วย การสตรีมคำตอบจาก LLM (เริ่ม TTS เมื่อจบประโยคแต่ละประโยคทันทีที่โทเค็นมาถึง) การเล่นเสียงจาก TTS ทีละประโยค (เล่นไปพร้อมกับการสังเคราะห์เสียง) การแคช TTS (เล่นวลีที่ถามซ้ำได้ทันที) การสร้างคำตอบทั่วไปไว้ล่วงหน้า (คำทักทายและวลีสำหรับถ่วงเวลา) และ การเลือกโมเดล LLM ที่รวดเร็ว (gpt-4o-mini สำหรับคำถามง่าย ๆ)

เป้าหมายคือใช้เวลาน้อยกว่า 1 วินาที ตั้งแต่ผู้ใช้พูดจบจนถึงไบต์แรกของเสียง ควรวัดแต่ละองค์ประกอบ เพราะการถอดเสียงมักคิดเป็น 40% ของเวลาแฝงทั้งหมด TTS ในเครื่องแลกคุณภาพกับความเร็ว ส่วน TTS บนคลาวด์ที่สตรีมทีละประโยคให้สมดุลที่ดีกว่าสำหรับเอเจนต์ส่วนใหญ่

คำถามที่พบบ่อย

บทเรียน “การปรับเวลาแฝงให้เหมาะที่สุดสำหรับตัวแทนเสียง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การปรับเวลาแฝงให้เหมาะที่สุดสำหรับตัวแทนเสียง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การปรับเวลาแฝงให้เหมาะที่สุดสำหรับตัวแทนเสียง”

การสตรีม TTS การแบ่งคำตอบเป็นส่วน และการลดเวลาแฝงก่อนพูดคำแรก คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การปรับเวลาแฝงให้เหมาะที่สุดสำหรับตัวแทนเสียง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม

ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การแปลงเสียงพูดเป็นข้อความด้วย Whisper และ Deepgram
  2. การแปลงข้อความเป็นเสียงพูดในคำตอบของตัวแทน
  3. การสร้างลูปสนทนาด้วยเสียง
  4. การปรับเวลาแฝงให้เหมาะที่สุดสำหรับตัวแทนเสียง
← กลับไปที่ AI Agents