Если вы пробовали собрать голосового агента в этом году, то упёрлись в ту же стену, что и все: у 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.6 в стриминге. Голос: ElevenLabs Flash. Полный круг в продакшене занимает 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.6 уступает ещё сильнее.
Если голосовой сценарий в основном болтовня (компаньоны, языковые тьюторы), audio-native подходит. А если он требует:
- читать и пересказывать аккаунт клиента в реальном времени;
- вести агентский флоу с несколькими инструментами (найти заказ, проверить остатки, поставить перезвон);
- следовать сложному скрипту или комплаенс-правилам;
- опираться на длинный контекст из документов,
то Claude Sonnet 4.6 в пайплайне обыгрывает GPT-4o-realtime по качеству. Штраф по задержке в 200-400 мс относительно realtime на одном сокете того стоит.
Рекомендуемый стек
Микрофон → VAD (Silero) → STT (Deepgram Nova-3 streaming) →
Claude Sonnet 4.6 (streaming) →
TTS (ElevenLabs Flash v2.5) → Динамик
Почему выбраны именно эти компоненты.
Deepgram Nova-3 для STT. Частичные транскрипты приходят стримом с шагом ~150 мс, финальный появляется примерно через 200 мс после конца реплики. WER на разговорном английском около 5%. Whisper Large v3 streaming через groq тоже годится (дешевле, задержка и WER чуть выше). Для не-английского беру Whisper.
Claude Sonnet 4.6 в роли мозга. Стриминг идёт через Anthropic Messages API, время до первого токена на коротких промптах обычно 250-400 мс. Мы ходим через шлюз codexkey, https://api.codexkey.ru/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. Очистить нужно аудио-буфер, одной остановки генерации мало.
- Отменить запрос к Claude, который ещё выполняется (
AbortControllerна вызове SDK). - Выбросить частичный ответ или добавить его в контекст с пометкой «[прервано]».
Конечный автомат выглядит так:
IDLE → LISTENING → THINKING → SPEAKING → (обратно в LISTENING)
↑ ↓
└── BARGE-IN ──────────┘
Типичная ошибка: собственный звук бота попадает в микрофон и вызывает самопрерывание. Лечится эхоподавлением (WebRTC AEC в браузере, RNNoise на сервере) и порогом уверенности у VAD.
Код: WebSocket-мост на TypeScript
Минимальный сквозной цикл. Браузер отправляет 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.codexkey.ru/v1",
apiKey: process.env.CODEXKEY_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-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, многошаговыми вызовами инструментов или процессами с комплаенсом.
- Для опоры на документы нужен контекст 300K+.
- Текстовый агент на Claude у вас уже есть, и голос хочется добавить без переписывания «мозга».
- Хочется менять провайдеров STT и TTS независимо (например, Whisper для не-английского, региональный TTS ради комплаенса).
Что в итоге
В 2026 году выбирать приходится между Claude-пайплайном и GPT-4o realtime. Для агентских голосовых задач, где ответ обязан быть фактически верным, Claude-пайплайн выигрывает по качеству, а 200 мс штрафа по задержке пользователь не замечает. Для чисто разговорной задачи, где задержка важнее всего, OpenAI Realtime пока остаётся вариантом.
Когда Anthropic выпустит нативный realtime-аудио режим (а они выпустят), эта статья устареет целиком. До тех пор связка Deepgram + Claude Sonnet 4.6 + ElevenLabs Flash остаётся стеком, который надо обыграть.
Цены на Claude разобраны в обзоре цен Claude API на 2026 год.