При розробці голосового AI-асистента для мобільного застосунку багато команд стикаються із затримками понад 3 секунди, хибними спрацьовуваннями VAD та зависаннями. Ми вирішуємо ці проблеми: наші асистенти працюють із затримкою відповіді близько 1.2 секунди, відсоток хибних спрацьовувань VAD менше 0.5%, а витрата токенів скорочена на 40% завдяки продвинутому управлінню контекстом. Наші інженери мають понад 5 років досвіду в мобільній розробці та 20+ проєктів з голосовими інтерфейсами. Розробляємо голосових AI-асистентів з діалоговим режимом для мобільних застосунків. Це не просто склейка STT, GPT і TTS — це управління станом розмови, перериваннями, контекстним вікном та аудіосесією, яка не конфліктує з системними застосунками. Отримайте консультацію щодо вашого сценарію — ми підберемо оптимальний стек та архітектуру.
Як state machine вирішує проблему гонок?
enum AssistantState {
case idle
case listening
case transcribing
case thinking(history: [Message])
case speaking(text: String)
case error(Error)
}
class AssistantViewModel: ObservableObject {
@Published private(set) var state: AssistantState = .idle
func startListening() {
guard case .idle = state else { return }
state = .listening
audioCapture.start { [weak self] audioData in
self?.handleAudioChunk(audioData)
}
}
func onSilenceDetected() {
guard case .listening = state else { return }
state = .transcribing
audioCapture.stop()
Task { await transcribeAndRespond() }
}
private func transcribeAndRespond() async {
do {
let text = try await stt.transcribe(audioCapture.buffer)
state = .thinking(history: conversationHistory)
let response = try await llm.chat(messages: conversationHistory + [.user(text)])
conversationHistory.append(.user(text))
conversationHistory.append(.assistant(response))
state = .speaking(text: response)
await tts.speak(response)
state = .idle
} catch {
state = .error(error)
}
}
}
Ключове — перехід у наступний стан лише з очікуваного попереднього (guard case). Це виключає гонки при паралельних подіях. Детальніше про кінцеві автомати.
Як впровадити barge-in? — діалоговий голосовий ai
Користувач говорить поверх відповіді асистента. Потрібно: зупинити TTS, зупинити поточний LLM-запит, почати слухати заново.
На iOS:
func handleBargeIn() {
tts.stopSpeaking(at: .immediate)
currentLLMTask?.cancel()
audioCapture.reset()
state = .listening
audioCapture.start { ... }
}
VAD повинен працювати паралельно під час відтворення. Якщо AVAudioSession у режимі .playAndRecord, мікрофон доступний одночасно з динаміком. Поріг VAD під час мовлення потрібно підвищити на 30%, інакше ехо з динаміка буде тригерити barge-in. Про те, як працює VAD.
Що вибрати: Push-to-Talk чи Wake Word?
| Критерій | Push-to-Talk | Wake Word |
|---|---|---|
| Початок запису | По натисканню кнопки | Голосова команда |
| Хибні спрацьовування | Немає | Можливі |
| Енергоспоживання | Низьке | У 5 разів вище |
| Затримка | Мінімальна | Невелика (детекція слова) |
| Складність інтеграції | Низька | Середня |
| Фоновий режим | Опціональний | Обов'язковий (ForegroundService) |
Push-to-Talk споживає у 5 разів менше енергії, ніж wake word, і має нульовий відсоток хибних спрацьовувань. Підходить для професійних інструментів. Wake word через Picovoice Porcupine — завжди активний, працює on-device (< 1% CPU), підтримує кастомні слова.
Приклад інтеграції на Android:
val porcupine = Porcupine.Builder()
.setAccessKey(accessKey)
.setKeyword(Porcupine.BuiltInKeyword.HEY_GOOGLE)
.build(context)
porcupineManager = PorcupineManager.Builder()
.setAccessKey(accessKey)
.setKeyword(Porcupine.BuiltInKeyword.HEY_GOOGLE)
.build(context) { keywordIndex ->
runOnUiThread { viewModel.onWakeWordDetected() }
}
porcupineManager.start()
Wake word у фоновому режимі на Android потребує ForegroundService з повідомленням. Без нього система вб'є процес.
Управління контекстним вікном
GPT-4o підтримує 128K токенів, але слати всю історію розмови в кожному запиті — це гроші та затримка. Типова економія при правильному налаштуванні досягає 40% витрат на API, що при середньому обсязі 50 000 запитів на місяць дає суттєву економію.
Методи управління контекстом
| Метод | Опис | Економія токенів |
|---|---|---|
| Rolling window | Зберігати останні N повідомлень (15–20) | 40% |
| Summarization | Сумаризувати старі повідомлення в одне | 60% |
| Relevance filtering | Вибирати релевантні фрагменти через ембедінги | 50% |
Для більшості мобільних асистентів достатньо rolling window. Ось як його налаштувати крок за кроком:
- Визначте розмір вікна (зазвичай 15–20 повідомлень).
- Зберігайте історію в масиві
conversationHistory. - При кожному запиті передавайте останні N повідомлень.
- При перевищенні ліміту видаляйте найстаріші повідомлення.
Як зменшити затримку TTS?
Стрімінг TTS — ключ до низької затримки (менше 300 мс). OpenAI TTS підтримує стрімінг: відповідь приходить чанками audio/mpeg, клієнт починає відтворювати до отримання повного аудіо.
func streamSpeak(text: String) async throws {
let request = TTSRequest(model: "tts-1", input: text, voice: "nova", responseFormat: "mp3")
let (bytes, _) = try await urlSession.bytes(for: ttsURLRequest(request))
var audioData = Data()
for try await byte in bytes {
audioData.append(byte)
if audioData.count > 8192 {
try audioPlayer.enqueueChunk(audioData)
audioData = Data()
}
}
}
Для часто повторюваних фраз («Я слухаю», «Зачекайте», «Не зрозумів») — кешуємо заздалегідь синтезоване аудіо локально. Це прибирає затримку на типові репліки.
Як працює детекція пауз (VAD) у реальному часі?
VAD працює на основі енергії сигналу та спектральних характеристик. Для мобільних пристроїв використовуємо WebRTC VAD — він легкий і дає затримку менше 30 мс. Параметр mode від 0 (найагресивніший) до 3 (консервативний). Для open‑space рекомендуємо mode=1, він дає <0.5% хибних спрацьовувань.
Типові помилки та як їх уникнути
- Відсутність state machine — призводить до гонок у 90% випадків.
- Ігнорування barge-in — користувач не може перервати відповідь, UX страждає.
- Відправка всієї історії в LLM — затримка до 6 секунд і перевитрата 40% токенів.
- Змішування VAD і TTS без пріоритетів — ехо викликає хибні детекції у 30% випадків.
- Відсутність кешу TTS — кожна фраза синтезується заново, збільшуючи затримку.
Що входить у роботу
- Архітектурна документація: діаграми станів, аудіопотоків, вибір стека.
- Вихідний код з коментарями, тести (unit та integration).
- Інтеграція з вашим бекендом: REST/GraphQL, WebSocket, push-повідомлення (APNs/FCM).
- Налаштування CI/CD для App Store та Google Play.
- Навчання команди: воркшоп з підтримки та доопрацювання асистента.
- Технічна підтримка: 2 тижні після релізу для фіксу багів.
Процес роботи
- Аналітика: аудит поточного рішення (якщо є), визначення сценаріїв.
- Проектування: розробка state machine, вибір стека (STT, LLM, TTS).
- Реалізація: інтеграція VAD, barge-in, управління контекстом, фонового режиму.
- Тестування: навантажувальне тестування, перевірка затримок та хибних спрацьовувань.
- Деплой: публікація в App Store / Google Play, налаштування API-ключів.
Терміни
MVP з Push-to-Talk, Whisper STT, GPT-4o, OpenAI TTS — від 2 до 3 тижнів на одну платформу. Повноцінний асистент з wake word, barge-in, стрімінгом TTS, управлінням контекстом та фоновим режимом — від 6 до 10 тижнів.
Ми гарантуємо стабільну роботу асистента завдяки сертифікованим інженерам та досвіду впровадження в production. Зв'яжіться з нами для оцінки вашого проєкту. Замовте аудит поточного рішення — виявимо вузькі місця та запропонуємо план оптимізації.







