AI · 2026年3月3日

从零搭建400ms延迟的语音Agent:自己撸管线比Vapi快2倍

语音Agent是2026年最火的AI应用方向之一,但市面上的一站式平台(Vapi、ElevenLabs Conversational AI)到底藏了多少延迟?最近HN上一个开源项目shuo给出了答案:自己搭管线,端到端延迟能压到400ms,比Vapi快整整2倍。我花了两天时间把这套架构从头跑了一遍,记录下整个过程和踩的坑。

语音Agent为什么这么难

文本聊天的交互很简单——用户打字、按发送、等回复。轮次边界由用户的”发送”按钮定义,所有延迟都可以接受。语音完全不是这回事。系统需要实时判断:用户在说话还是在听?两个状态之间的切换,就是所有难度所在:

  • 用户开始说话 → 立即打断Agent,取消生成、取消TTS、清空音频缓冲
  • 用户说完了 → 立即开始生成回复,延迟越低越好

判断”用户说完了”远比想象中困难。人说话会停顿、犹豫、发语气词,背景噪音也会干扰。纯粹的音量检测(VAD)根本不够用。

架构设计:把问题拆成一个状态机

整个语音Agent可以简化为一个两状态的状态机:

状态 Agent行为 触发条件
用户说话中 Agent静默、持续监听 检测到语音开始
用户听取中 Agent生成回复并播放 检测到语音结束

两个状态转换就是核心:barge-in(打断)和turn-end(轮次结束)。所有优化都围绕这两个转换的速度展开。

第一步:VAD + 预录音频验证架构

先不管STT/LLM/TTS,用最简单的方式验证核心循环能不能跑通。技术栈:

  • FastAPI处理WebSocket连接
  • Twilio做电话接入,流式传输μ-law音频(8kHz、20ms帧)
  • Silero VAD做语音活动检测(模型仅2MB)
import asyncio
from silero_vad import load_silero_vad, get_speech_timestamps

model = load_silero_vad()

class TurnState:
    def __init__(self):
        self.user_speaking = False

    async def on_audio_frame(self, frame, ws):
        # Silero VAD判断是否有语音
        speech_prob = model(frame, 8000).item()

        if speech_prob > 0.5 and not self.user_speaking:
            # 用户开始说话 → 立即打断Agent
            self.user_speaking = True
            await ws.send_json({"event": "clear"})  # 清空Twilio音频缓冲

        elif speech_prob < 0.3 and self.user_speaking:
            # 用户停止说话 → 播放预录回复
            self.user_speaking = False
            await self.play_prerecorded(ws)

这一步的意义:隔离测试轮次检测逻辑,拿到延迟基线。预录音频的响应几乎是瞬时的,所以后面加上STT/LLM/TTS后,增加的延迟就是管线本身的开销。踩坑:Silero VAD对短停顿非常敏感。用户说话中间稍微停一下,VAD就判定结束了。纯VAD做turn detection在实际对话中根本不可用,必须结合语义信号。

第二步:Deepgram Flux替代VAD

Deepgram的Flux API把语音识别和轮次检测合并到了一个模型里。输入音频流,输出两种关键事件:start_of_turnend_of_turn(附带转写文本)。这是一个重大升级——Flux不仅看音频信号,还看语义内容来判断用户是否说完了。”我想要那个…”后面停顿,Flux不会急着切轮次,因为语义上句子没说完。

async def handle_flux_events(flux_ws, agent_pipeline):
    async for event in flux_ws:
        if event["type"] == "end_of_turn":
            transcript = event["transcript"]
            # 用户说完了 → 启动Agent回复管线
            await agent_pipeline.start_turn(transcript)

        elif event["type"] == "start_of_turn":
            # 用户开始说话 → 打断一切
            await agent_pipeline.cancel()  # 停LLM生成
            await agent_pipeline.flush()   # 停TTS
            await twilio_ws.send_json({"event": "clear"})

第三步:STT → LLM → TTS全流式管线

这是整套架构的核心。当Flux发出end_of_turn信号后,三级管线同时启动:

  1. LLM流式生成:转写文本+对话历史发给LLM,拿到第一个token就立即往下传
  2. TTS流式合成:LLM输出的token实时喂给ElevenLabs的WebSocket TTS
  3. 音频流式播放:TTS产出的每个音频包直接转发到Twilio

关键:三级管线是流式衔接的,不是等上一步完成再开始下一步。LLM吐出第一个token的瞬间,TTS就开始合成了。

class AgentPipeline:
    def __init__(self):
        # 预热TTS WebSocket连接池(关键优化!)
        self.tts_pool = WebSocketPool(
            url="wss://api.elevenlabs.io/v1/text-to-speech/...",
            size=3
        )

    async def start_turn(self, transcript: str):
        self.cancelled = False

        # 从连接池拿一个预热好的TTS连接
        tts_ws = await self.tts_pool.acquire()

        # LLM流式生成
        async for chunk in self.llm.stream_chat(transcript):
            if self.cancelled:
                break
            # 实时把文本喂给TTS
            await tts_ws.send({"text": chunk.content})

        # 告诉TTS文本发完了
        await tts_ws.send({"text": "", "flush": True})

    async def cancel(self):
        self.cancelled = True

一个省了300ms的优化:TTS的WebSocket连接必须预热。每次新建WebSocket到ElevenLabs要几百毫秒的握手时间,维护一个连接池(3个预连接的socket),turn开始时直接从池里取,省掉了约300ms。

延迟对比:地理位置比模型选择更重要

先看本地运行的结果(作者在土耳其的偏远木屋里测试):

部署方式 TTFT (首token) 端到端延迟 体感
本地(土耳其) ~1300ms ~1700ms 明显卡顿,对话不自然
Railway EU ~300-500ms ~790ms 流畅,接近自然对话
Railway EU + Groq ~80ms ~400ms Agent回复比人还快
Vapi(同配置) ~840ms 正常

从1700ms到400ms,4倍提升。最大的两个因素:1. 服务器和API提供商同区域部署音频包在三个外部服务之间来回跳转(Twilio → Deepgram → LLM → ElevenLabs),每一跳都有网络延迟。把编排服务器部署到跟API提供商同一区域(EU),直接省掉了900ms。2. LLM的TTFT是瓶颈中的瓶颈在语音管线里,LLM的首token时间(TTFT)决定了整条管线能多快开始产出音频。作者测了360次请求,结果很清楚:

模型 TTFT (中位数) 备注
Groq llama-3.3-70b ~80ms 比人眨眼还快
gpt-4o-mini ~250ms 还行
gpt-4o ~400ms 语音场景太慢
Claude Sonnet ~350ms 文本好用,语音嫌慢

Groq的80ms TTFT碾压所有云端LLM API。换上Groq之后,对话体验质变——Agent回复快到人类跟不上。

自建 vs 平台:什么时候该自己搭

跑完整个流程,我的判断:用Vapi/ElevenLabs的场景:

  • 快速原型验证,不关心延迟细节
  • 团队没有实时音频处理经验
  • 标准的客服/助手场景,延迟800ms够用

自建管线的场景:

  • 延迟是核心体验指标(400ms和800ms体感差距巨大)
  • 需要深度定制轮次检测逻辑
  • 想控制成本(API调用按量计费,平台还要加一层抽成)

自建的核心代码量其实不大——shuo项目总共就几百行Python。难度不在代码,在于理解每一层的延迟来源和优化手段。

如果你也想试

最小化复现步骤:

  1. 注册Twilio账号,买一个电话号码($1/月)
  2. 注册Deepgram(有免费额度),拿到Flux API key
  3. 注册Groq(免费),拿到API key
  4. 注册ElevenLabs(有免费额度),拿到TTS API key
  5. clone shuo仓库,配置环境变量,部署到Railway/Fly.io
git clone https://github.com/NickTikhonov/shuo.git
cd shuo
cp .env.example .env
# 填入 TWILIO_*, DEEPGRAM_API_KEY, GROQ_API_KEY, ELEVENLABS_API_KEY
pip install -r requirements.txt
python main.py

部署时注意:服务器一定要选跟API提供商同区域。选美西还是欧洲取决于你用的服务商的数据中心位置,选错了延迟直接翻倍。

写在后面

语音Agent的技术栈在2026年已经相当成熟了。Silero VAD、Deepgram Flux、Groq推理、ElevenLabs TTS——每一层都有优秀的专业供应商。真正的技术门槛不在单个组件,而在流式编排:怎么让数据在组件之间不停顿地流动。400ms的端到端延迟意味着什么?意味着AI语音对话已经比大多数人类客服的反应速度还快。接下来的问题不是”能不能做到实时”,而是”实时对话能做什么新东西”。项目地址:github.com/NickTikhonov/shuo原文:ntik.me/posts/voice-agent