Голосовые агенты на Claude в 2026: лучший STT + Claude + TTS стек
Если вы пытались собрать голосового агента в этом году, вы упёрлись в ту же стену, что и все: у Claude нет нативного realtime-аудио API. Нет модели claude-realtime, которая принимает байты микрофона и отдаёт байты речи. У OpenAI такая есть — gpt-4o-realtime. У Google есть Gemini Live. У Anthropic на момент написания — нет.
Звучит как проблема. На деле — нет. Подход с пайплайном — STT → LLM → TTS — в 2026 году стал настолько быстрым, что грамотно собранный стек на Claude укладывается в ~700 мс end-to-end, а это ниже порога, на котором человек ощущает паузу. И вы получаете качество ответа Claude, которое для большинства агентских и reasoning-нагруженных голосовых сценариев (поддержка, квалификация лидов, технические ассистенты) заметно лучше, чем у audio-native GPT-4o-realtime.
Это та архитектура, которую мы шипим в продакшене, бюджет латентности, который её делает рабочей, и код для сборки.
Состояние голоса в 2026
Доминируют три архитектуры:
1. Audio-native realtime (один сокет). OpenAI Realtime API и Gemini Live. Открываете WebSocket, стримите PCM внутрь, получаете PCM наружу. Модель сама делает VAD, обрабатывает прерывания и смену реплик. Латентность отличная (~300–500 мс end-to-end). Минус: вы заперты в audio-native модели. У OpenAI это GPT-4o. Никакого Claude Sonnet, никакого o3, никакого tool calling за пределами OpenAI.
2. Каскадный пайплайн (STT → LLM → TTS). Три провайдера, три стрима, склеенные между собой. Больше движущихся частей. Теоретически больше латентность. На практике современный стриминг делает его конкурентным.
3. Гибрид: realtime STT + LLM-как-текст + realtime TTS. Об этом и пост. Уши — Deepgram Nova-3 или Whisper streaming, мозг — Claude Sonnet 4.5 streaming, голос — ElevenLabs Flash. Полный round-trip в проде: 600–800 мс.
Для пользователей Claude пайплайн выигрывает по простой причине: вариант 1 с Claude недоступен. Вопрос только в том, достаточно ли вариант 2/3 закрывает разрыв по латентности, чтобы не платить за использование Claude. Закрывает.
Почему Claude-пайплайн обыгрывает OpenAI Realtime для большинства агентов
Audio-native модели сжимают LLM в тот же forward pass, что и кодер/декодер речи. Так они и достигают скорости — но «рассуждательная» часть оказывается мельче, чем у текстовых аналогов. GPT-4o-realtime заметно слабее GPT-4o-text на многошаговом reasoning, использовании инструментов и следовании инструкциям, и ещё слабее, чем Claude Sonnet 4.5.
Для голосовых сценариев, которые в основном — болтовня (компаньоны, языковые тьюторы), audio-native годится. Для голосовых сценариев, которые требуют:
- Прочитать и подытожить аккаунт клиента в реальном времени
- Многошаговый агентский флоу с инструментами (найти заказ, проверить остатки, поставить перезвон)
- Следовать сложному скрипту или комплаенс-правилам
- Длинный контекст на документах
— Claude Sonnet 4.5 в пайплайне обыгрывает GPT-4o-realtime по качеству. Штраф 200–400 мс по латентности относительно one-socket realtime того стоит.
Рекомендуемый стек
Микрофон → VAD (Silero) → STT (Deepgram Nova-3 streaming) →
Claude Sonnet 4.5 (streaming) →
TTS (ElevenLabs Flash v2.5) → Динамик
Почему именно эти компоненты:
Deepgram Nova-3 для STT. Стриминг частичных транскриптов с шагом ~150 мс, финальный транскрипт ~200 мс после конца реплики. WER на разговорном английском ~5%. Whisper Large v3 streaming через groq — нормальная альтернатива (дешевле, чуть выше латентность, чуть выше WER). Для не-английского беру Whisper.
Claude Sonnet 4.5 в качестве мозга. Стриминг через Anthropic Messages API, время до первого токена обычно 250–400 мс на коротких промптах. Мы ходим через https://api.claudexia.tech/v1 — это добавляет ~30 мс прокси-оверхеда и даёт кэширование + observability.
ElevenLabs Flash v2.5 для TTS. ~200 мс до первого аудио, если кормить токенами по мере генерации. Новый Turbo v3 быстрее, но голоса звучат менее естественно; Flash — золотая середина в 2026.
Silero VAD локально. Крошечная ONNX-модель, кадры по 10 мс, крутится на CPU. Говорит, когда пользователь начал и закончил говорить — чтобы не отправлять тишину в STT и не держать LLM в ожидании на не-реплике.
Бюджет латентности
Куда уходят ~700 мс от конца речи пользователя до первого аудио на выходе:
| Этап | Бюджет |
|---|---|
| VAD: детект конца речи | 80 мс |
| Deepgram финальный транскрипт | 150 мс |
| Сеть: приложение → Claude | 40 мс |
| Claude время до первого токена | 300 мс |
| Сеть: Claude → TTS | 30 мс |
| ElevenLabs время до первого аудио | 200 мс |
| Прогрев аудио-буфера | 50 мс |
| Итого воспринимаемая латентность | ~700 мс |
Можно ужать дальше:
- Спекулятивный TTS: начинать слать токены в TTS, как только Claude выдаст первый конец предложения, не дожидаясь окончания LLM. Экономит ~300 мс на длинных ответах.
- Предсказательное завершение STT: запускать вызов LLM на высокоуверенном partial-транскрипте, не дожидаясь final. Рискованно, но срезает ~120 мс.
- Тюнинг endpointing: снизить порог тишины VAD с 500 мс до 300 мс. Срезает 200 мс мёртвого эфира, но повышает частоту ложных срабатываний.
Агрессивный тюнинг доводит до ~450 мс. Ниже — вы уже играете с OpenAI Realtime на его поле.
Прерывание и barge-in
Самое сложное в голосовом агенте — не латентность, а прерывание. Пользователь начинает говорить, пока бот ещё говорит. Бот должен:
- Засечь начало речи в пределах ~100 мс (VAD на входном микрофоне, не привязанный к выходу).
- Немедленно остановить воспроизведение TTS (убить аудио-буфер, а не просто прекратить генерацию).
- Отменить in-flight запрос к Claude (
AbortControllerна вызове SDK). - Выбросить частичный ответ или зашить его в контекст как «[прервано]».
Стейт-машина выглядит так:
IDLE → LISTENING → THINKING → SPEAKING → (обратно в LISTENING)
↑ ↓
└── BARGE-IN ──────────┘
Типичный баг: собственное аудио бота протекает в микрофон и триггерит самопрерывание. Лечится эхо-подавлением (WebRTC AEC в браузере или RNNoise на сервере) и порогом уверенности на VAD.
Код: WebSocket-мост на TypeScript
Минимальный end-to-end луп. Браузер шлёт PCM микрофона на сервер, сервер оркестрирует STT/LLM/TTS, стримит аудио обратно.
import Anthropic from "@anthropic-ai/sdk";
import { createClient as deepgram } from "@deepgram/sdk";
import { ElevenLabsClient } from "elevenlabs";
import WebSocket from "ws";
const claude = new Anthropic({
baseURL: "https://api.claudexia.tech/v1",
apiKey: process.env.CLAUDEXIA_API_KEY,
});
const dg = deepgram(process.env.DEEPGRAM_KEY);
const tts = new ElevenLabsClient({ apiKey: process.env.ELEVEN_KEY });
const wss = new WebSocket.Server({ port: 8080 });
wss.on("connection", (client) => {
const history: Array<{ role: "user" | "assistant"; content: string }> = [];
let abortLLM: AbortController | null = null;
const stt = dg.listen.live({
model: "nova-3",
language: "ru",
smart_format: true,
interim_results: true,
endpointing: 300,
});
stt.on("Results", async (data: any) => {
const transcript = data.channel.alternatives[0].transcript;
if (!transcript) return;
// Barge-in: пользователь заговорил, пока мы говорили
if (abortLLM) {
abortLLM.abort();
client.send(JSON.stringify({ type: "stop_audio" }));
}
if (!data.is_final) return;
history.push({ role: "user", content: transcript });
abortLLM = new AbortController();
const stream = await claude.messages.stream(
{
model: "claude-sonnet-4.5",
max_tokens: 1024,
system: "Ты — голосовой ассистент. Отвечай не длиннее 2 предложений.",
messages: history,
},
{ signal: abortLLM.signal },
);
let buffer = "";
let assistantText = "";
for await (const chunk of stream) {
if (chunk.type !== "content_block_delta") continue;
const delta = (chunk.delta as any).text ?? "";
buffer += delta;
assistantText += delta;
// Сбрасываем в TTS на границе предложения
const sentenceEnd = buffer.match(/[.!?]\s/);
if (sentenceEnd) {
const sentence = buffer.slice(0, sentenceEnd.index! + 1);
buffer = buffer.slice(sentenceEnd.index! + 2);
streamTTS(sentence, client);
}
}
if (buffer.trim()) streamTTS(buffer, client);
history.push({ role: "assistant", content: assistantText });
abortLLM = null;
});
client.on("message", (raw) => {
// Браузер шлёт сырой 16-bit PCM @ 16kHz
stt.send(raw);
});
});
async function streamTTS(text: string, client: WebSocket) {
const audio = await tts.textToSpeech.convertAsStream(
"21m00Tcm4TlvDq8ikWAM", // voice id
{
text,
model_id: "eleven_flash_v2_5",
output_format: "pcm_16000",
},
);
for await (const chunk of audio) {
client.send(chunk);
}
}
Это ~80 строк, и они дают рабочий голосовой луп с barge-in. В проде сюда добавляется реконнект, ресемплинг аудио, prompt caching на Claude и метрики.
Когда брать OpenAI Realtime вместо этого
Не используйте Claude-пайплайн, если:
- Качества GPT-4o хватает для вашего сценария (большая часть потребительского чата — да).
- Хочется один сокет и одного вендора ради операционной простоты.
- Нужно < 400 мс end-to-end, а спекулятивный стриминг невозможен.
- Не нужны Anthropic-специфичные фичи (tool use Claude, длинный контекст, MCP).
Берите Claude-пайплайн, если:
- Агент делает реальный reasoning, многошаговые tool calls или комплаенс-флоу.
- Нужен контекст 300K+ для грунтования на документах.
- У вас уже есть текстовый агент на Claude и вы хотите добавить голос, не переписывая мозг.
- Хочется независимо менять STT/TTS-провайдеров (например, Whisper для не-английского, региональный TTS под комплаенс).
Итог
В 2026 выбор не «Claude или realtime» — а «Claude-пайплайн или GPT-4o realtime». Для агентских голосовых задач, где ответ должен быть фактически правильным, Claude-пайплайн выигрывает по качеству, а 200 мс латентного штрафа пользователь не замечает. Для чисто разговорной задачи «латентность любой ценой» OpenAI Realtime пока остаётся опцией.
Когда Anthropic выпустит нативный realtime-аудио режим — а они выпустят, — этот пост устареет целиком. До тех пор стек Deepgram + Claude Sonnet 4.5 + ElevenLabs Flash — это бенчмарк, который надо обыгрывать.
Цены на Claude разобрали в обзоре цен Claude API на 2026.