Есть такой момент в холодном звонке — трубка уже поднята, человек говорит, ты киваешь. Но внутри тишина. Ни слова не понял.
Особенно если на том конце британец с каким-то специфическим акцентом или индиец, который говорит очень быстро и очень уверенно. Оба варианта — моя реальность в первые недели работы брокером в Дубае.
Я дизайнер. Умею проектировать сложные системы. Понимаю продукт. Но телефонный разговор с незнакомым акцентом — это отдельный навык, и он не появляется от одного желания.
Можно было просто тренироваться и ждать. Я решил построить инструмент.
Что такое CallCoach
Нативное iOS-приложение на Swift с Apple Intelligence и Live Translation.
Во время звонка оно в реальном времени распознаёт речь собеседника и показывает текст прямо на экране. Анализирует что он сказал. Предлагает подсказки — что ответить, как продолжить, какой вопрос задать.

Это не транскрипция ради транскрипции. Это скрипт-ассистент, который всплывает именно тогда, когда нужен — в живом разговоре, без задержки.
Почему Swift и Apple Intelligence
Мог взять любой веб-стек. Но веб не даёт нужной скорости и нативной интеграции с микрофоном и системными API.
Apple Intelligence — локальный языковой движок прямо на устройстве. Никаких запросов в облако, никакой задержки, никакой утечки данных разговора. Всё на телефоне.
Live Translation справляется с акцентированным английским с нужной точностью. Именно то, что нужно для мультинационального Дубая.

Как это изменило работу
Когда видишь текст того, что говорит собеседник — тревога уходит. Перестаёшь тратить энергию на «что он сказал» и начинаешь думать про «что ответить».
Из режима выживания — в режим диалога. Принципиальный сдвиг.
Продолжаю развивать. Следующий шаг — база скриптов под разные сценарии: первый звонок, возражение, уточнение бюджета, договорённость о встрече. Подсказки, которые появляются не просто как транскрипция, а как умный контекстный ответ на конкретную фразу.
Почему я это рассказываю
CallCoach — пример того, как я думаю о продуктах.
Не «какую технологию попробовать», а «какую проблему решить». Проблема была конкретная и личная. Решение — нативное, быстрое, без лишних зависимостей.
Дизайнер, который умеет писать код — не разработчик. Но иногда именно это позволяет идее не умереть в очереди на реализацию.
Техническая реализация
С технической точки зрения CallCoach я собирал как реальный рабочий инструмент, а не как концепт.
Сначала я проработал саму идею и архитектуру вместе с ChatGPT: сформулировал продуктовую задачу, пользовательский сценарий и подготовил большой стартовый промт для Codex, чтобы он создал базовый Swift-проект, поднял зависимости и собрал первый рабочий каркас приложения.
Когда окно токенов у Codex закончилось, я передал проект в Claude Code. Дальше основная «мясная» часть разработки шла уже там — с моделью Opus 4.7.
Она оказалась очень сильной именно на длинной инженерной дистанции: много думает, тратит много токенов, но зато хорошо держит архитектуру, аккуратно пишет связанный код и помогает доводить приложение до рабочего состояния.
После этого я уже регулярно переходил в Xcode: собирал билд, запускал тесты, ловил падения, возвращал ошибки обратно в Claude Code и вместе с ним фиксировал проблемные места. Часть правок вносил уже прямо внутри Xcode, где тоже подключал Codex и Claude как помощников по месту.
Как устроена логика приложения
Само приложение построено вокруг локальной обработки речи на устройстве.
В обычном режиме CallCoach может слушать поток через микрофон и показывать живую транскрипцию. Но ключевым техническим решением стало разделение голосов.
Главная проблема была в том, что при звонке через динамики микрофон слышит и тебя, и собеседника одновременно. В результате транскрипция загрязняется обеими сторонами диалога и перестаёт быть полезной для коучинга.
Чтобы это решить, я сделал два режима работы.
Режим 1 — System Audio
Это основной и рекомендуемый режим.
Через ScreenCaptureKit приложение захватывает системный аудиовыход, то есть только то, что уходит в динамики. Это позволяет транскрибировать именно голос собеседника, не захватывая мой собственный голос из микрофона.
Дополнительно используется флаг excludesCurrentProcessAudio = true, чтобы исключить звуки самого приложения.
При первом запуске macOS запрашивает разрешение на запись экрана — без этого системный захват аудио невозможен.
Режим 2 — Microphone + Push-to-Mute
Это запасной сценарий.
В нём используется обычный захват с микрофона, но пока я говорю сам, можно зажать кнопку и временно не отправлять аудиобуферы в транскрибер.
То есть логика очень простая: говорю сам — удерживаю кнопку, закончил — отпустил, приложение снова слушает собеседника.
Что было написано в коде
Архитектурно приложение собрано из нескольких отдельных менеджеров, каждый из которых отвечает за свою часть пайплайна.
AudioCaptureManager
Отвечает за захват звука через AVAudioEngine.
- использует
installTapнаinputNode startRecording()возвращаетAsyncStream<AVAudioPCMBuffer>- поток буферов передаётся дальше без проблем с
actor crossing - параллельно считается уровень сигнала через RMS и dB metering
- обновления UI отправляются на
@MainActorчерезTask
SystemAudioCaptureManager
Это отдельный класс, который я добавил для захвата системного аудио через SCStream.
Именно он сделал возможным сценарий, где в транскрипцию попадает только голос собеседника из динамиков, а не весь акустический шум вокруг.
TranscriptionManager
Этот слой принимает поток аудиобуферов и передаёт его в SpeechAnalyzer из Speech framework.
Дальше он итерирует AsyncThrowingStream<SpeechTranscriptionResult>, разделяет partial и final transcription и явно управляет переходами на @MainActor.
То есть это уже не просто «распознать звук», а аккуратно встроить потоковую транскрипцию в живой интерфейс.
CoachingEngine
Это отдельный слой логики, который превращает обычную транскрипцию в полезный разговорный инструмент.
processNewTranscript(_:)использует debounce на 1.5 секунды- предыдущий
Taskотменяется, если приходит новый кусок речи LanguageModelSessionсоздаётся один раз на сессию и потом переиспользуется- история контекста сохраняется
- парсер разбирает ответы в формате
[ТИП]: текстс fallback на.phrase - определяется этап разговора по ключевым словам
- в модель отправляются последние примерно 300 слов, а не весь транскрипт целиком
Отдельно я подмешивал контекст под рынок Дубая: Emaar, DAMAC, Binghatti, ROI, бронь, встреча, Zoom и другие характерные для разговора сигналы.
ContentView
Здесь всё связывается в пользовательский интерфейс.
.taskзапрашивает разрешение на микрофон при запуске.onChange(segments.count)триггеритcoach.processNewTranscripttoggleSession()передаёт поток и формат вstartTranscription- ошибки менеджеров выводятся через отдельные
.onChange
Именно этот слой делает всё ощущение приложения живым: переключение источника звука, запуск и остановка сессии, mute-логика, обработка ошибок и обновление подсказок в реальном времени.
Как я обучал подсказки
Отдельно я подкармливал систему прикладным контекстом.
Для сценария холодных звонков использовал реальные sales scripts: приветствие, удержание разговора, базовая квалификация клиента, прояснение бюджета, работа с сомнениями и закрытие либо на встречу, либо на подробный Zoom.
Причём это были не абстрактные шаблоны из интернета, а логика, которой нас учили в агентстве SPI Dubai.
За счёт этого подсказки стали более прикладными. Не просто «ответь что-нибудь вежливое», а именно продолжение разговора в нужной структуре и на нужном этапе.
Второй сценарий — собеседования на продуктового дизайнера
Параллельно я сделал второе направление использования этого же подхода: прохождение собеседований на продуктового дизайнера.
Там я уже обучал систему на своих проектах, кейсах и портфолио, чтобы она помогала не в cold calling, а в другом типе разговора — когда тебе важно быстро, уверенно и структурно отвечать на вопросы о собственном опыте.
То есть по сути CallCoach стал не просто speech-to-text приложением, а универсальным conversational assistant, который можно адаптировать под конкретный сценарий общения.
Как выглядел реальный процесс разработки
И если совсем честно, для меня это ещё и важный пример того, как сегодня выглядит реальная продуктовая разработка.
Идея рождается из собственной боли.
Архитектура быстро собирается с помощью языковых моделей.
Тяжёлый код пишется в связке с агентами.
Финальная доводка происходит уже руками в Xcode — через сборки, падения, отладку и повторные итерации.
Не «магия нейросети», а нормальный инженерный процесс, просто ускоренный новыми инструментами.
Changelog / как собирался CallCoach
1. Формулировка идеи
- Зафиксировал конкретную проблему: в холодных звонках с британскими и индийскими акцентами часть речи просто не успевает распознаваться на слух.
- Определил задачу продукта: не просто транскрибировать разговор, а помогать вести его дальше.
- Вместе с ChatGPT проработал концепцию, user flow и структуру будущего приложения.
- Сгенерировал большой стартовый промт для Codex, чтобы быстро поднять каркас проекта на Swift.
2. Первичная сборка проекта
- Codex создал базовый Swift-проект.
- Были подняты основные зависимости.
- Подготовлена базовая архитектура.
- Собран первый runnable prototype.
3. Основная инженерная реализация
- После исчерпания окна токенов Codex проект был передан в Claude Code.
- Основная разработка продолжилась с использованием Opus 4.7.
- Через Claude Code была собрана «мясная» часть приложения: захват аудио, транскрипция, коучинговая логика, связка с интерфейсом.
4. Решение главной продуктовой проблемы — разделение голосов
- Выявлена ключевая проблема: микрофон слышит и пользователя, и собеседника.
- Реализованы два режима:
- System Audio — захват только системного аудиовыхода через
ScreenCaptureKit - Microphone + Push-to-Mute — fallback-режим с ручным отключением передачи буферов в транскрибер
- System Audio — захват только системного аудиовыхода через
- Добавлен
excludesCurrentProcessAudio = true, чтобы исключить звуки самого приложения.
5. Реализованные компоненты
SystemAudioCaptureManager.swift— новый класс для захвата системного аудио черезSCStreamAudioCaptureManager.swift— захват звука черезAVAudioEngine, добавленisMutedTranscriptionManager— потоковая транскрипция черезSpeechAnalyzerCoachingEngine— debounce, переиспользование сессии модели, разбор фраз, логика этапов звонкаContentView.swift— управление сессией, выбор источника аудио, ошибки, кнопки и интерфейсInfo.plist— разрешения для системного захватаproject.pbxproj— подключениеScreenCaptureKit.framework
6. Отладка и тесты
- Сборка и запуск шли через Xcode.
- При каждом падении приложения ошибка возвращалась в Claude Code.
- Исправления вносились итеративно: Xcode → crash/error → Claude Code → фикс → новая сборка.
- Часть быстрых правок делалась уже прямо внутри Xcode с подключёнными Codex и Claude.
7. Обучение под реальные сценарии
- Для cold calling в систему были добавлены реальные sales scripts:
- приветствие
- обычный small talk
- квалификация клиента
- выявление интереса
- закрытие на встречу или Zoom
- Скрипты опирались на реальную практику и обучение в SPI Dubai.
- Параллельно создан второй сценарий использования: помощь при собеседованиях на роль продуктового дизайнера.
- Для этого модель обучалась на моих проектах, кейсах и портфолио.
8. Что получилось в итоге
- локальная транскрипция без отправки данных в облако
- realtime-подсказки по ходу разговора
- отдельная логика для продаж и интервью
- нативный стек без веб-костылей
- рабочий продукт, собранный в связке ChatGPT + Codex + Claude Code + Xcode
