Эта инструкция собирает в n8n расшифровку записей звонков через Yandex SpeechKit. На выходе получается диалог с репликами менеджера и клиента, который можно отправить в нейросеть на разбор.
Что понадобится
- n8n на своём сервере.
- Каталог в Yandex Cloud и сервисный аккаунт.
- Записи звонков из телефонии в MP3, WAV или OGG. Лучше всего стерео, где менеджер и клиент записаны в разные каналы.
Как устроено асинхронное распознавание
Записи звонков распознают асинхронно. Вы отправляете файл, получаете идентификатор операции, ждёте и забираете результат. Документация описывает две версии API. Мы используем API v3. Он принимает файл прямо в теле запроса и сам разбирает форматы MP3, WAV и OGG.
Ограничения из документации:
- файл в теле запроса до 60 МБ, через Object Storage до 1 ГБ;
- длительность аудио до 4 часов, для многоканальных файлов считается суммарное время всех каналов;
- результат хранится на сервере 3 суток;
- до 500 запросов на распознавание в час и до 5 проверок статуса в секунду.
Шаг 1. Сервисный аккаунт и ключ
- В консоли Yandex Cloud создайте сервисный аккаунт. Асинхронное распознавание работает только от имени сервисного аккаунта.
- Выдайте ему роль
ai.speechkit-stt.userна каталог. Если будете загружать файлы в бакет, добавьте рольstorage.uploader. - Создайте для аккаунта API-ключ. При создании ограничьте его область действия значением
yc.ai.speechkitStt.execute, тогда ключ подойдёт только для распознавания речи. - В n8n создайте доступ типа Header Auth: Name
Authorization, ValueApi-Key ВАШ_КЛЮЧ.
Шаг 2. Подготовить файл
Получите запись из телефонии узлом HTTP Request с форматом ответа File. Затем добавьте узел Extract From File с операцией Move File to Base64 String и укажите Destination Output Field audio.
Если записи больше 60 МБ, загрузите их в бакет Object Storage узлом S3. В доступе S3 укажите S3 Endpoint https://storage.yandexcloud.net, регион ru-central1 и статический ключ сервисного аккаунта. В запросе на распознавание тогда вместо content передаётся поле uri со ссылкой вида https://storage.yandexcloud.net/имя_бакета/путь/к/файлу.mp3.
Шаг 3. Отправить на распознавание
Узел HTTP Request:
- Method:
POST - URL:
https://stt.api.cloud.yandex.net/stt/v3/recognizeFileAsync - Authentication: Header Auth из шага 1
- Send Body: JSON
{
"content": {{ JSON.stringify($json.audio) }},
"recognitionModel": {
"model": "general",
"audioFormat": {
"containerAudio": { "containerAudioType": "MP3" }
},
"textNormalization": {
"textNormalization": "TEXT_NORMALIZATION_ENABLED",
"literatureText": true
},
"languageRestriction": {
"restrictionType": "WHITELIST",
"languageCode": ["ru-RU"]
}
}
}
Для WAV укажите WAV, для OGG OGG_OPUS. Нормализация переводит числа и даты в цифры, а literatureText расставляет знаки препинания. Если результат не срочный, укажите модель deferred-general. Это отложенный режим. Файл встаёт в очередь с низким приоритетом, а распознавание стоит дешевле.
В ответе придёт объект операции. Её идентификатор лежит в поле {{ $json.id }}.
Шаг 4. Дождаться результата
Соберите цикл из трёх узлов:
- Wait на 30–60 секунд.
- HTTP Request с методом
GETна адресhttps://operation.api.cloud.yandex.net/operations/{{ $json.id }}. При первом проходе цикла$json.idпридёт из шага 3. В следующих проходах его вернёт сам этот запрос. - If с условием
{{ $json.done }}равноtrue. Ветку false верните в узел Wait.
Не проверяйте статус чаще, чем позволяет лимит. Для звонков длиной в несколько минут хватает паузы в минуту.
Шаг 5. Забрать текст по каналам
Узел HTTP Request:
- Method:
GET - URL:
https://stt.api.cloud.yandex.net/stt/v3/getRecognition?operationId={{ $json.id }} - Options → Response: Response Format
Text, Put Output in Fielddata
Ответ состоит из нескольких JSON-объектов подряд, по одному на каждую фразу и событие. Номер канала лежит в поле channelTag. Так SpeechKit разделяет текст стерео-записи: в канале 0 одна сторона разговора, в канале 1 другая. Сам по голосу он реплики не делит, поэтому для анализа звонков стерео-запись заметно удобнее.
Узел Code в режиме Run Once for Each Item собирает из ответа диалог:
// Ответ getRecognition: несколько JSON-объектов подряд
function splitJson(raw) {
const out = [];
let depth = 0, start = 0, inStr = false, esc = false;
for (let i = 0; i < raw.length; i++) {
const c = raw[i];
if (inStr) {
if (esc) esc = false;
else if (c === '\\') esc = true;
else if (c === '"') inStr = false;
} else if (c === '"') {
inStr = true;
} else if (c === '{') {
if (depth === 0) start = i;
depth++;
} else if (c === '}') {
depth--;
if (depth === 0) out.push(JSON.parse(raw.slice(start, i + 1)));
}
}
return out;
}
const roles = { '0': 'Менеджер', '1': 'Клиент' }; // проверьте на своей телефонии
const phrases = [];
for (const msg of splitJson($json.data)) {
const r = msg.result || {};
const alt = r.finalRefinement?.normalizedText?.alternatives?.[0];
if (!alt || !alt.text) continue;
phrases.push({
channel: r.channelTag,
start: Number(alt.startTimeMs),
text: alt.text,
});
}
phrases.sort((a, b) => a.start - b.start);
const dialog = phrases
.map((p) => `${roles[p.channel] || 'Канал ' + p.channel}: ${p.text}`)
.join('\n');
return { json: { dialog } };
Код берёт нормализованный текст из finalRefinement. Если вы не включали нормализацию, берите r.final вместо r.finalRefinement?.normalizedText. Какой канал принадлежит менеджеру, зависит от телефонии. Проверьте на паре звонков.
Шаг 6. Передать текст в нейросеть
Поле {{ $json.dialog }} готово для языковой модели. Передайте его в запрос к YandexGPT через JSON.stringify($json.dialog) с инструкцией: проверить звонок по чек-листу скрипта, выписать возражения и договорённости, предложить следующий шаг. Так устроен наш сценарий анализа звонков.
В разговорах почти всегда есть имена, телефоны и адреса. Это персональные данные, и требования 152-ФЗ к ним действуют. По документации SpeechKit по умолчанию не сохраняет данные, отправленные по API. Перед отправкой текста в языковую модель часть данных можно замаскировать.
Сколько стоит
Оплата идёт за длительность аудио. По правилам тарификации единица учёта равна секунде двухканального аудио, а минимум составляет 15 секунд на каждые два канала. Отложенный режим в несколько раз дешевле обычного. На октябрь 2026 года прайс давал около 0,6 ₽ за минуту в обычном режиме и около 0,15 ₽ в отложенном. Цены меняются, перед расчётом проверьте страницу тарифов.
Частые ошибки
- 401 или 403. Неверный ключ, у сервисного аккаунта нет роли
ai.speechkit-stt.userили область действия ключа не включает распознавание. - Пустой результат. Указан неверный тип контейнера или в записи нет речи. Сверьте
containerAudioTypeс форматом файла. - Ошибка размера. Файл больше 60 МБ. Загрузите его в Object Storage и передайте
uri. - Реплики перепутаны. Каналы менеджера и клиента в вашей телефонии идут в другом порядке. Поменяйте местами значения в
roles. - Результат не найден. С момента распознавания прошло больше 3 суток. Сохраняйте текст в своей базе сразу после получения.
Частые вопросы
Сколько ждать расшифровку?
По документации минута одноканального аудио распознаётся примерно за 10 секунд. Но при нагрузке асинхронное распознавание может длиться от нескольких минут до 24 часов, поэтому сценарий должен уметь ждать.
Стерео-запись стоит дороже моно?
По текущим правилам тарификации единица учёта равна секунде двухканального аудио, поэтому один или два канала стоят одинаково. Проверьте актуальные правила на странице тарифов SpeechKit.
Можно ли распознать звонок без Object Storage?
Да. API v3 принимает файл до 60 МБ прямо в теле запроса. Бакет нужен для файлов больше этого размера.