Когда транскрибация даёт сбой, многие сначала меняют модель или сомневаются в качестве распознавания, хотя большинство проблем возникает на четырёх уровнях: файл, аутентификация, сеть и лимиты. Интерфейс транскрибации принимает загрузку multipart/form-data, поэтому цепочка длиннее, чем у текстового API, и порядок диагностики тоже длиннее: сначала HTTP-статус и тип ошибки, затем сам запрос, и только потом аудио и промпт.

Ниже разбираем 401, ошибку формата 400, слишком большой файл 413, лимиты 429, тайм-ауты, а также пустой текст и «мусор» в ответе; даём путь диагностики и переиспользуемый код обработки. Ограничения файлов, имена моделей и формат ошибок могут меняться; ориентируйтесь на официальную документацию и материалы на сайте, а цены — на страницу тарифов.

Примечание: OpenAI и Whisper — товарные знаки соответствующих правообладателей; статья носит технический характер. Ограничения интерфейса, поддерживаемые форматы и имена моделей могут меняться; ориентируйтесь на официальную документацию и материалы на сайте.

1. Сначала выстройте порядок диагностики

Симптом Частый статус Частая причина Направление действий
Ошибка аутентификации 401, 403 Неверный ключ, нет Bearer, нет прав на аудио Исправить учётные данные, не повторять
Формат не поддерживается 400 Кодек не в списке, расширение не совпадает с кодировкой Перекодировать и отправить снова
Файл слишком большой 413 или 400 Превышен лимит на файл Сжать или нарезать
Слишком частые запросы 429 Превышены запросы в минуту, параллелизм или квота Задержка, очередь, разделение ключей
Ошибка сервера 500, 502, 503 Сбой или перегрузка upstream Задержка и ограниченные повторы
Тайм-аут Нет ответа или обрыв Большой файл, медленная загрузка, долгая обработка Нарезка, настройка тайм-аутов
Пустой текст 200 Тишина, низкая громкость, неверный язык, проблема кодирования Проверять аудио, а не повторять

Если в заголовках ответа или теле ошибки есть request-id, обязательно сохраните его. Для поддержки один request ID и минимальное воспроизводимое аудио полезнее десяти скриншотов.

2. 401: сначала заголовок, потом ключ

401 означает, что аутентификация не прошла, и обычно не связана с балансом или возможностями модели. Проверяйте по порядку:

  1. Заголовок имеет вид Authorization: Bearer <key>, не потеряны ли Bearer и пробел.
  2. Нет ли пробелов, переносов строк или кавычек до и после ключа. Это частая проблема при копировании из чата.
  3. Действительно ли загружены переменные окружения. Можно вывести только префикс, но не пишите полный ключ в журнал.
  4. Не отозван ли ключ, относится ли он к текущему проекту, есть ли у аккаунта права на аудио.
  5. Правильный ли base URL — https://ai.zhongshangqihang.com/v1, и путь — /v1/audio/transcriptions.
  6. Если это шлюз, уточните, открыта ли на этом входе аудиотранскрибация: не предполагайте, что при работающем текстовом интерфейсе аудио тоже доступно.

Ещё одна частая ошибка — положить файл в JSON-тело. Интерфейс транскрибации требует multipart/form-data; в командной строке используйте curl -F "file=@meeting.mp3", а не -d. При неверном формате тела иногда возвращается 400, а иногда 401, если валидные учётные данные не были прочитаны.

3. 400 и 413: формат и размер

В 400 чаще всего виноват неподдерживаемый формат. В официальной документации перечислены mp3, mp4, mpeg, mpga, m4a, wav, webm и другие; полный список и требования к кодировке смотрите в актуальной документации. Переименование расширения не заменяет перекодирование: если переименовать .amr в .mp3, сервер прочитает исходный кодек и всё равно вернёт ошибку.

413 обычно означает превышение лимита на файл. Часто упоминается лимит 25 МБ, точное значение смотрите в документации. Есть два способа:

  • Сжатие: привести к 16 кГц моно; для речи высокий битрейт не нужен. Подойдёт ffmpeg -i input.mp3 -ac 1 -ar 16000 -b:a 64k output.mp3.
  • Нарезка: резать по 10 минут, транскрибировать по частям и собирать. Точки реза выбирайте по тишине, оставляйте небольшое перекрытие, чтобы не разрывать слова.

Не пытайтесь «впихнуть» длинное аудио только сжатием. Чем больше файл, тем медленнее загрузка, выше риск тайм-аута и дороже повтор после сбоя. Параллельная нарезка стабильнее, но контролируйте параллелизм, иначе 413 превратится в 429.

4. 429: лимит — это не всегда нехватка баланса

429 означает превышение лимита частоты, параллелизма или квоты. При одном и том же статусе тип ошибки может отличаться: иногда это превышение частоты, иногда недостаточная квота. Сначала посмотрите тип, затем решайте: ждать, снижать параллелизм или пополнять баланс.

Типичные причины: слишком много параллельных пакетных задач; несколько сервисов используют один ключ; немедленный повтор после сбоя превращается в шторм повторов; длинное аудио надолго занимает соединение; у бесплатного или младшего тарифа небольшая квота в минуту.

Порядок действий: проверьте Retry-After в ответе, затем повторяйте с экспоненциальной задержкой и случайным разбросом; ставьте пакетные задачи в очередь и ограничивайте число одновременных запросов; разделяйте ключи по проектам; задайте предел повторов и оповещение, не допускайте бесконечного цикла. Транскрибация тарифицируется по длительности, повторная отправка может привести к двойному списанию, поэтому до повтора убедитесь, что предыдущий запрос не завершился успешно.

5. Тайм-ауты: медленная загрузка или долгая обработка

Тайм-аут может произойти на трёх этапах: установка соединения, загрузка файла, обработка на сервере. Если тайм-аут случается и на маленьких файлах, проверяйте сеть, прокси и base URL; если стабильно на больших — нарезайте и сжимайте.

Клиент должен явно задавать тайм-аут, не полагаясь на значения по умолчанию. Время транскрибации зависит от длительности, размера файла и очереди; задайте достаточно щедрый, но конечный лимит, например две–три минуты, и по журналу задач различайте «отправлено, но не вернулось» и «вообще не ушло». С автоматическим повтором после тайм-аута будьте осторожны: если сервер уже завершил обработку, повтор даст второе списание и дубликат результата.

6. 200, но пустой текст или мусор

В этих случаях HTTP-ошибки нет, и направления диагностики совершенно разные.

Пустой текст обычно означает: полная тишина или слишком низкая громкость; параметр языка не соответствует аудио; проблемы с каналами, основной канал пуст; файл загружается, но декодируется некорректно. Сначала проверьте громкость, явно передайте language=ru, затем приведите аудио к 16 кГц моно и попробуйте снова.

Мусор или большое число ошибок обычно означает: неверно определён язык, сильный диалектный акцент, музыка и голос смешаны, несколько человек перебивают друг друга, микрофон слишком далеко. Можно передать в prompt имена, названия продуктов и термины, а после транскрибации заменить по словарю. Если само аудио непригодно, смена модели не поможет — возвращайтесь к записи и предобработке.

7. Повторы и нарезка: переиспользуемый код

Повторы подходят только для временных ошибок. Для 401, 400 и 413 нужно менять запрос; слепой повтор только усугубляет проблему.

import os
import random
import time
from openai import (
    OpenAI,
    APIConnectionError,
    APIStatusError,
    APITimeoutError,
    RateLimitError,
)

client = OpenAI(
    api_key=os.environ["WHISPER_API_KEY"],
    base_url="https://ai.zhongshangqihang.com/v1",
    timeout=180,
)

def transcribe(path: str, model: str = "whisper-1", retries: int = 4) -> str:
    for attempt in range(retries):
        try:
            with open(path, "rb") as f:
                result = client.audio.transcriptions.create(
                    model=model,
                    file=f,
                    language="ru",
                    response_format="verbose_json",
                )
            text = (result.text or "").strip()
            if not text:
                raise ValueError("Пустая транскрибация: проверьте громкость, язык и каналы")
            return text
        except RateLimitError:
            wait = min(2 ** attempt, 30) + random.random()
            print(f"429, повтор через {wait:.1f} с")
            time.sleep(wait)
        except (APITimeoutError, APIConnectionError):
            time.sleep(min(2 ** attempt, 30) + random.random())
        except APIStatusError as exc:
            if exc.status_code >= 500:
                time.sleep(min(2 ** attempt, 30) + random.random())
            else:
                raise  # 400/401/413 требуют исправить запрос, а не повторять
    raise RuntimeError("Лимит повторов исчерпан")

print(transcribe("meeting.mp3"))

Имена моделей смотрите в документации на сайте; whisper-1 — лишь распространённый пример. Аудио сверх лимита сначала нарежьте через ffmpeg, затем вызывайте эту функцию для каждого фрагмента и при сборке не забудьте прибавить начальное время каждого фрагмента.

8. Журналы и список для обращения в поддержку

  1. Зафиксируйте время, часовой пояс, HTTP-статус и тип ошибки.
  2. Сохраните request-id и укажите, шёл запрос на официальный вход или через шлюз.
  3. Запишите имя файла, фактический кодек, длительность, размер, частоту дискретизации и число каналов.
  4. Запишите ключевые параметры: model, language, response_format.
  5. Зафиксируйте число повторов, время ожидания и итоговый результат.
  6. Отметьте, был ли ключ общим, каковы были параллелизм и масштаб пакетной задачи.
  7. Подготовьте минимальное воспроизводимое аудио на 10–30 секунд без персональных данных.

При обращении в поддержку не отправляйте полный ключ и не передавайте записи клиентов без разрешения. Минимальный стабильно воспроизводимый пример полезнее описания «иногда выдаёт ошибку».

Частые вопросы

413 и 400 — обе про файл. Как различить?

413 обычно означает превышение размера: помогает сжатие или нарезка. 400 чаще связан с форматом, параметрами или кодировкой: нужны перекодирование и проверка полей. Смотрите тип и сообщение, а не только статус.

Что делать, если 429 не проходит?

Сначала определите, превышение частоты это или нехватка квоты. При частоте снижайте параллелизм, ставьте в очередь и разделяйте ключи; при квоте нужно пополнение или другой вход. При постоянном 429 не наращивайте повторы — это только удлинит восстановление.

Стоит ли сразу повторять при тайм-ауте?

Сначала оцените, мог ли запрос уже завершиться на сервере. Транскрибация могла быть списана, и прямой повтор даст дубликат и вторую оплату. Проверьте состояние по журналу задач или идемпотентному дизайну, затем решайте.

Пустая транскрибация — модель сломалась?

Чаще нет. Проверьте громкость, участки тишины, параметр языка, каналы и фактический кодек. Тот же файл через другой вход поможет быстро понять, проблема в аудио или в сервисе.

Как отличить ошибку шлюза от ошибки официального API?

Сохраните request-id и тип ошибки и отправьте один и тот же запрос на оба входа. Если официальный вход работает, а шлюз выдаёт ошибку, проблема скорее в конфигурации или пересылке; если ошибка на обоих — проверяйте ключ, сеть и файл.

Следующий шаг

Начните с регистрации в ZSQH AI Gateway, отработайте на минутном аудио 401, 413, 429 и тайм-аут по одному разу, затем подключайте production-мониторинг. Доступность моделей и цены — на странице цен на модели.

Что читать дальше: