AI Agents · Pelajaran

Optimasi Latensi untuk Agen Suara

TTS streaming, pemecahan respons, dan minimisasi latensi kata pertama.

Pelajaran 4 dari 413 langkah

Optimasi Latensi untuk Agen Suara adalah pelajaran AI Agents gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Agents, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Agents mencakup 4 pelajaran total.

Mengapa Latensi Penting untuk Suara

Dalam percakapan teks, jeda 3 detik masih dapat diterima. Dalam percakapan suara, jeda lebih dari 1,5 detik terasa tidak alami dan mengganggu alur percakapan.

Latensi suara memiliki tiga komponen utama: waktu transkripsi (STT), waktu pemrosesan LLM (TTFT + pembuatan), dan waktu sintesis TTS. Mengoptimalkan setiap komponen akan menghasilkan peningkatan pengalaman pengguna yang jauh lebih baik.

Mengukur Anggaran Latensi

Sebelum melakukan pengoptimalan, ukur setiap komponen. Tambahkan pemanggilan pengukuran waktu ke dalam siklus agar Anda mengetahui bagian yang benar-benar menghabiskan waktu.

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 melalui Penstriman: Putar Sambil Membuat Audio

Peningkatan latensi tunggal terbesar berasal dari TTS melalui penstriman. Alih-alih menunggu seluruh audio selesai disintesis, mulai putar bagian audio pertama dalam waktu sekitar 200ms sementara bagian lainnya dibuat secara bersamaan.

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 melalui Penstriman per Kalimat

Pendekatan penstriman yang paling praktis: bagi respons LLM menjadi beberapa kalimat, lalu sintesis dan putar setiap kalimat satu per satu. Kalimat pertama mulai diputar dalam waktu sekitar 300ms.

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: Pengoptimalan Waktu ke Token Pertama

TTFT (Time to First Token) adalah jeda antara pengiriman permintaan LLM dan penerimaan token pertama respons. Mengoptimalkan TTFT berarti pengguna dapat mendengar awal jawaban lebih cepat.

Gunakan respons LLM melalui penstriman dan alirkan token ke TTS segera setelah batas kalimat terdeteksi.

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)

Membuat Respons Umum Terlebih Dahulu

Beberapa respons agen dapat diprediksi: salam, pesan kesalahan, dan indikator sedang berpikir ("Biar saya memeriksanya untuk Anda."). Buat audio-nya terlebih dahulu saat sistem dimulai agar dapat diputar seketika tanpa latensi.

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

Latensi TTS Lokal dibandingkan TTS Awan

TTS awan menambahkan waktu perjalanan pulang-pergi jaringan (sekitar 100–300ms). Untuk respons singkat atau frasa yang sering digunakan, mesin TTS lokal dapat lebih cepat—dengan mengorbankan kualitas suara.

pyttsx3 adalah TTS luring paling sederhana untuk 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

Memilih Model LLM yang Lebih Cepat

Pilihan model sangat memengaruhi TTFT. GPT-4o-mini 5–10 kali lebih cepat daripada GPT-4o untuk sebagian besar tugas agen suara. Gunakan model terkecil yang memenuhi persyaratan kualitas.

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

Penyimpanan Cache untuk Pertanyaan Berulang

Pengguna sering mengajukan pertanyaan yang sama atau serupa berulang kali. Simpan respons LLM + TTS di cache untuk kueri yang identik agar dapat disajikan seketika saat ditanyakan kembali.

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

Target Latensi Ujung ke Ujung

Dengan semua pengoptimalan diterapkan, agen suara yang dirancang dengan baik seharusnya mencapai waktu kurang dari 1 detik sejak pengguna selesai berbicara hingga respons agen mulai terdengar.

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

Tumpang Tindih Transkripsi Asinkron

Saat agen membuat dan mengucapkan responsnya, segera mulai rekam input pengguna berikutnya. Menjalankan tahapan alur kerja secara tumpang tindih mengurangi waktu jeda antargiliran.

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

Uji Pemahaman

Pengoptimalan tunggal mana yang biasanya memberikan pengurangan latensi terbesar untuk agen suara?

Rangkuman: Pengoptimalan Latensi untuk Agen Suara

Tumpukan pengoptimalan latensi suara: alirkan respons LLM (mulai TTS pada batas kalimat saat token tiba), pemutaran TTS kalimat demi kalimat (putar saat sintesis berlangsung), penyimpanan TTS di cache (pemutaran ulang frasa berulang secara seketika), respons umum yang telah dibuat sebelumnya (salam, frasa pengisi waktu), dan pemilihan model LLM yang cepat (gpt-4o-mini untuk kueri sederhana).

Target: kurang dari 1 detik sejak pengguna selesai berbicara hingga bait audio pertama. Ukur setiap komponen — transkripsi sering kali mencakup 40% dari total latensi. TTS lokal mengorbankan kualitas demi kecepatan; TTS cloud dengan streaming per kalimat memberikan keseimbangan yang lebih baik untuk sebagian besar agen.

Gratis untuk memulai

Belajar AI Agents dengan tutor AI — gratis

Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.

Kursus
60
Pelajaran
239

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Optimasi Latensi untuk Agen Suara” gratis?

Ya — teks lengkap “Optimasi Latensi untuk Agen Suara” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Agents, upgrade ke CoddyKit PRO. Kursus AI Agents mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Optimasi Latensi untuk Agen Suara”?

TTS streaming, pemecahan respons, dan minimisasi latensi kata pertama. Kamu berlatih AI Agents dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai AI Agents?

Tidak diperlukan pengalaman sebelumnya. AI Agents di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.

Berapa lama pelajaran “Optimasi Latensi untuk Agen Suara” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Agents ini?

Ya. Setiap pelajaran AI Agents menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Speech-to-Text dengan Whisper dan Deepgram
  2. Text-to-Speech dalam Respons Agen
  3. Membangun Putaran Percakapan Suara
  4. Optimasi Latensi untuk Agen Suara
← Kembali ke AI Agents