Уявіть: користувач говорить у мікрофон, а текст з'являється на екрані із затримкою менше півсекунди. Це нетривіальне інженерне завдання, але з Deepgram Nova-2 воно вирішується. Модель дає медіанну затримку близько 300 мс і WER близько 5% на російській мові. Такі показники недоступні класичним batch-рішенням. У цій статті розберемо, як налаштувати WebSocket-транскрибацію на iOS та Android, які параметри критичні та як уникнути типових помилок.
Типові труднощі — нестабільне з'єднання, дублювання interim-результатів, неправильна конфігурація аудіокодека. Наша команда з 10+ річним досвідом впровадження ASR (5+ років на ринку, понад 50 проектів) гарантує стабільне з'єднання та низьку затримку. На реальному проекті клієнт скаржився на дублювання interim: кожне нове слово додавалося до попереднього. Після впровадження патерну заміни буфера проблема зникла повністю.
Порівняння: Deepgram Nova-2 кращий за Whisper у 6-10 разів за швидкістю стримінгу.
Які параметри впливають на якість транскрибації?
Deepgram Nova-2 забезпечує низьку затримку на стримінгу: медіана близько 300 мс від кінця фрази до тексту. Whisper такого не вміє в принципі — він синхронний. Якщо задача «користувач говорить — текст з'являється на екрані» із затримкою менше секунди, це Deepgram.
| Характеристика | Deepgram Nova-2 | Whisper (синхронний) |
|---|---|---|
| Затримка фіналу | 300–500 мс | 2–5 секунд |
| Стримінг | Асинхронний, потоковий | Синхронний, batch |
| Interim результати | Так | Ні |
| Підтримка російської | Відмінно | Добре |
| Ціна (за годину) | $0.01/хв | Безкоштовно (self-hosted) |
Для мобільного сценарію Deepgram виграє у 6–10 разів за швидкістю. Крім того, Nova-2 показує WER ≤5% на російській мові, у той час як Whisper large-v2 — близько 7%.
Як налаштувати протокол підключення?
Deepgram працює через WebSocket. Endpoint:
wss://api.deepgram.com/v1/listen?model=nova-2&language=ru&encoding=linear16&sample_rate=16000&channels=1&interim_results=true Параметри критичні: encoding=linear16 означає сирий PCM 16-bit little-endian. Будь-який інший формат без явного зазначення кодека — ризик 1008 Policy Violation. interim_results=true включає часткові результати — саме вони створюють відчуття реального часу.
iOS: AVAudioEngine + URLSessionWebSocketTask
class DeepgramStreamer { private var audioEngine = AVAudioEngine() private var webSocket: URLSessionWebSocketTask? func start() throws { let session = URLSession(configuration: .default) var request = URLRequest(url: URL(string: "wss://api.deepgram.com/v1/listen?model=nova-2&language=ru&encoding=linear16&sample_rate=16000&channels=1&interim_results=true")!) request.setValue("Token \(apiKey)", forHTTPHeaderField: "Authorization") webSocket = session.webSocketTask(with: request) webSocket?.resume() receiveLoop() let inputNode = audioEngine.inputNode let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 16000, channels: 1, interleaved: false)! inputNode.installTap(onBus: 0, bufferSize: 4096, format: format) { buffer, _ in guard let channelData = buffer.int16ChannelData else { return } let frameLength = Int(buffer.frameLength) let data = Data(bytes: channelData[0], count: frameLength * 2) self.webSocket?.send(.data(data)) { _ in } } try audioEngine.start() } private func receiveLoop() { webSocket?.receive { [weak self] result in if case .success(let message) = result, case .string(let text) = message { // Decode Deepgram JSON response self?.handleTranscript(text) } self?.receiveLoop() } } } Важлива деталь: AVAudioEngine.inputNode на iOS 16+ вимагає явного запиту мікрофона через AVAudioSession.sharedInstance().requestRecordPermission. І обов'язково AVAudioSession.setCategory(.record, mode: .measurement) — режим .measurement вимикає AEC та AGC, які можуть спотворити сигнал для транскрипції.
Android: AudioRecord + OkHttp WebSocket
class DeepgramStreamer(private val apiKey: String) { private val client = OkHttpClient() private var webSocket: WebSocket? = null private var audioRecord: AudioRecord? = null fun start(onTranscript: (String, Boolean) -> Unit) { val request = Request.Builder() .url("wss://api.deepgram.com/v1/listen?model=nova-2&language=ru&encoding=linear16&sample_rate=16000&channels=1&interim_results=true") .header("Authorization", "Token $apiKey") .build() webSocket = client.newWebSocket(request, object : WebSocketListener() { override fun onMessage(webSocket: WebSocket, text: String) { val json = JSONObject(text) val channel = json.getJSONObject("channel") val alternatives = channel.getJSONArray("alternatives") val transcript = alternatives.getJSONObject(0).getString("transcript") val isFinal = json.getBoolean("is_final") if (transcript.isNotEmpty()) onTranscript(transcript, isFinal) } }) val bufferSize = AudioRecord.getMinBufferSize(16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize) audioRecord?.startRecording() Thread { val buffer = ShortArray(bufferSize / 2) while (audioRecord?.recordingState == AudioRecord.RECORDSTATE_RECORDING) { val read = audioRecord!!.read(buffer, 0, buffer.size) if (read > 0) { val byteBuffer = ByteBuffer.allocate(read * 2).order(ByteOrder.LITTLE_ENDIAN) buffer.take(read).forEach { byteBuffer.putShort(it) } webSocket?.send(byteBuffer.array().toByteString()) } } }.start() } } ByteOrder.LITTLE_ENDIAN обов'язковий. Deepgram очікує LE PCM. Якщо відправити BE, транскрипція працюватиме, але з помітно гіршою якістю.
Як уникнути типових помилок при стримінгу аудіо?
-
Дублювання interim: ніколи не накопичуйте всі interim як окремі рядки. Зберігайте поточний utterance у буфері та перезаписуйте його при кожному новому interim. При
is_final: trueфіналізуйте буфер. - Втрата з'єднання: впровадьте reconnect з експоненційною затримкою (1,2,4,8 сек). Deepgram не підтримує відновлення сесії, тому після reconnect потрібно почати новий потік.
- Неправильна частота дискретизації: використовуйте строго 16000 Гц. Вища частота збільшує трафік без виграшу в якості, нижча — погіршує розпізнавання.
Обробка interim-результатів
Deepgram повертає два типи повідомлень: з is_final: false (interim) та is_final: true (фінальний). Правильний патерн UI:
- Interim відображаємо сірим або курсивом — користувач бачить, що йде розпізнавання.
- При отриманні
is_final: trueзамінюємо всі попередні interim цього utterance фінальним текстом. -
speech_final: trueозначає кінець паузи — хороший момент для початку обробки фрази.
Параметри Nova-2, які змінюють якість
| Параметр | Значення | Опис |
|---|---|---|
| model | nova-2 | Модель розпізнавання |
| encoding | linear16 | Кодування аудіо |
| sample_rate | 16000 | Частота дискретизації |
| interim_results | true | Включити часткові результати |
| utterance_end_ms | 1000 | Фіналізація паузою |
| diarize | false | Розділення дикторів |
| punctuate | true | Автоматична пунктуація |
| smart_format | true | Форматування чисел та дат |
-
utterance_end_ms: 1000— Deepgram сам фіналізує utterance після 1 секунди тиші. Корисно для диктування без явних команд «стоп». -
diarize: true— розділення по спікерах, додаєspeakerу кожен word. -
punctuate: true— автопунктуація. Без неї текст іде без крапок та ком. -
smart_format: true— форматує числа, дати, телефони. «двадцять п'яте березня» → «25 березня».
Що входить у роботу
- Налаштування WebSocket-підключення з авторизацією
- Захоплення аудіо через AVAudioEngine / AudioRecord з правильним форматом
- Обробка interim та фінальних результатів без дублювання
- Reconnect при обриві мережі з експоненційною затримкою
- Тестування на реальних пристроях (iOS/Android)
- Документація та навчання команди
Процес роботи
- Аналіз — вивчаємо архітектуру додатку та вимоги до транскрибації
- Проектування — обираємо модель, протокол, параметри
- Реалізація — кодова інтеграція WebSocket, аудіозахоплення, UI
- Тестування — навантажувальне тестування, перевірка на різних пристроях і мережах
- Деплой — публікація в App Store та Google Play
Строки
Базова інтеграція WebSocket + AudioRecord/AVAudioEngine + вивід тексту — 4–7 днів. Додавання діаризації, обробки перемикання мережі (reconnect), фонового режиму, експорту результату — 8–14 днів.
Економія на розробці власного ASR-двигуна може досягати 70%, а вартість транскрибації становить $0.01 за хвилину аудіо. Отримайте консультацію з інтеграції Deepgram для вашого мобільного проекту. Ми проаналізуємо архітектуру, підберемо оптимальну конфігурацію та реалізуємо low-latency транскрибацію під ключ. Замовте демо-версію з низькою затримкою вже сьогодні. Зв'яжіться з нами для обговорення вашого проекту.
Інтеграція Deepgram дозволила нам скоротити затримку транскрибації з 5 секунд до 300 мс — відгук клієнта з фінансового сектору.







