В первой статье я разобрал как MAX собирает ваш IP, определяет VPN и какие сайты из вашей сети. Как MAX помогает РКН строить железный занавес: VPN-детект, сбор IP и проверки на Госуслуги
Дальше - про звонки. В MAX встроена система распознавания ключевых слов (KWS - Keyword Spotting). Нейросеть, которая прогоняет аудио с микрофона прямо во время разговора.
Я не утверждаю, что MAX прямо сейчас слушает ваши разговоры в поисках слов вроде «Путин», «митинг» или «VPN». Текущая модель распознавания обучена только на фразу «не слышу» - по задумке это для определения плохой связи. Прямо сейчас функция выключена на сервере.
Но....я разобрал архитектуру, проследил весь код от микрофона до отправки на сервер - и вот что важно:
KWS (Keyword Spotting) - распознавание конкретных слов в аудиопотоке. Вы с этим знакомы: «Окей, Google», «Привет, Алиса», «Hey Siri» - всё это KWS.
Принцип простой: нейросеть берёт звук с микрофона, режет на кусочки по 10 миллисекунд и на каждом решает - это ключевое слово или нет?
VK встроил такую штуку прямо в свой модифицированный WebRTC. Нейросеть крутится локально на устройстве и слушает аудио во время звонка.
Я скачал модель с серверов VK (https://st.okcdn.ru/static/calls_android/1-0-1/kws_270525.zip), разобрал её архитектуру и запустил.
Текущий функционал:
Для звонков это имеет смысл: собеседник говорит «не слышу» - значит со связью проблемы, приложение может это подхватить.
Распознавать «не слышу» - безобидно. А вот как это устроено внутри - уже нет.
Нейронка приходит с сервера. KWS не зашит в код приложения. При запуске приложение получает от сервера конфиг с URL модели, забирает файл и грузит в нейросетевой движок. Сервер VK решает:
VK может подменить модель хоть завтра - на любые другие слова. Обновлять приложение не нужно. Спрашивать пользователя - тоже.
Приложение не проверяет, что именно нейронка распознаёт. Если завтра VK положит на CDN модель, обученную на слово «протест» - приложение скачает её и запустит точно так же.
А что в политике конфиденциальности? Я проверил оба документа - Политику конфиденциальности (legal.max.ru/pp) и Пользовательское соглашение (legal.max.ru/ps). Упоминания KWS или анализа аудио во время звонков - ноль.
При этом в Пользовательском соглашении есть описание того, как голосовые и видеосообщения переводятся в текст.
Когда нейронка считает, что услышала ключевое слово, происходит следующее:
VK видит: в таком-то звонке у такого-то пользователя сработал детектор, уверенность такая-то. Всё привязано к userId как и call_id (vchat.clientStats отправляется в привязке к конкретной VoIP-сессии)
Во время анализа я перехватил реальный ответ сервера VK с конфигурацией KWS. Вот что сервер присылает приложению:
Я забрал модель по этому URL - без авторизации, без cookies. Любой может это сделать прямо сейчас. MD5 совпадает с тем, что прислал сервер.
Ещё я побрутил CDN в поисках других моделей - перебрал 200+ вариантов путей. Нашёл только одну модель в трёх версиях SDK (1-0-1, 1-0-2, 1-0-3) - все три идентичны (одинаковый MD5). На данный момент VK использует только одну модель для фразы «не слышу».
Работает только во время звонков - закрыли приложение, и всё. Кода для фоновой работы KWS я не нашёл.
С голосовыми сообщениями не связан - те переводятся с аудио в текст на серверах VK, это другой процесс, другой код.
Сейчас выключен (use: false). Нейронка уже на устройствах, но не запущена. И ищет она только «не слышу», а не что-то «опасное».
KWS - не единственная интересная вещь в работе звонков MAX:
Все звонки идут через сервер VK. P2P-соединений я не увидел - все медиаданные проходят через TURN-сервер VK. Шифрование DTLS-SRTP есть, но от вас до сервера, не от вас до собеседника. На relay-сервере шифрование заканчивается - ключи у VK.
Флаг записи аудио. В коде есть PMS-ключ calls-sdk-log-audio - если VK его включит, аудио звонка пишется в файл. Управляется с сервера.
Модифицированный WebRTC. VK не использует стандартный WebRTC - они его модифицировали. В модификации добавлены: нативная запись аудио в Opus (nativeAudioStartRecord, nativeAudioWriteFrame), KWS-интеграция, и кастомные параметры.
Ниже - код, конфиги и результаты реверс-инжиниринга. Версия APK 26.12.1 (6679).
KWS встроен в модифицированный WebRTC внутри нативной библиотеки libjingle_peerconnection_so.so. Точка входа - JNI-метод:
Java_org_webrtc_PeerConnectionFactory_nativeSetKeywordSpotterParams
Это вызов из Java в нативный код. Принимает два параметра: isEnabled (включить/выключить) и filePath (путь к модели на устройстве).
Полная цепочка от сервера до нейросети:
Пакет: ru.ok.android.externcalls.sdk.audio - это SDK звонков.
/ru/ok/android/externcalls/sdk/audio/KeywordSpotterManagerImpl.java
Файл: calls_kws.tflite из kws_270525.zip
Результаты запуска модели на тестовых данных:
config.cfg
Вот полная карта: какие серверы участвуют, какие запросы идут и в каком формате.
Шаг 1 - Получение конфига (при подключении к серверу):
"android.wordspotter.config" → {"turn_off_in_ms": 60000}"android.mlfeatures.ws_0" → {"url": "https://st.okcdn.ru/static/calls_android/1-0-1/kws_270525.zi...", "cs": "00320292950aa4896ccc057550442789", "use": false}
Шаг 2 - Скачивание модели (при первом запуске или обновлении URL):
Шаг 3 - Работа KWS (во время звонка, если use: true):
Шаг 4 - Отправка результата (после/во время звонка):
{"metric": "bad_call_detected_by_audio_spotter", "string_value": "не слышу", "double_value": 0.95} + userId, sessionId, call_id
Медиаданные самого звонка:
Файл: defpackage/ConversationKwsStat.java
При срабатывании формируется событие:
Имя "не слышу" зашито в код - но модель определяется URL с сервера. Если VK ее заменит, код по-прежнему будет отправлять "не слышу" как строку, даже если реальная модель будет детектить совершенно другую фразу. Или VK обновит и код в следующей версии.
/ru/ok/android/externcalls/sdk/stat/kws/ConversationKwsStat.java
Проверяйте сами:
curl -O https://st.okcdn.ru/static/calls_android/1-0-1/kws_270525.zip
Содержимое config.cfg:
Я проверил три версии SDK на CDN:
Одна и та же модель во всех версиях. Имя файла kws_270525 предполагает дату создания 27.05.2025. Просканировал 200+ вариантов путей - других моделей на CDN не нашёл.
Цепочка замены модели:
На стороне приложения нет проверки того, что именно модель распознаёт. Нет whitelist допустимых слов. Нет уведомления пользователя. MD5 проверяет только целостность файла - что скачалось без ошибок.
Запись аудио через серверный флаг: PMS-ключ calls-sdk-log-audio (key 129) может включить запись аудио звонка в файл. Плюс JNI-методы nativeStartAecDump / nativeStopAecDump позволяют дампить raw-аудио в файловый дескриптор. Всё управляется сервером.
Все звонки через relay VK: Все медиаданные идут через TURN-сервер VK (155.212.206.115:43210). Шифрование DTLS-SRTP - от вас до сервера, не от вас до собеседника. Сертификат сервера: QRtpServer 1.1.10.
Типы ML-фич в конфиге:
MLFeaturesManagerImpl поддерживает несколько типов моделей. Сейчас WS и NS, но подцепить новый тип - дело пары строк.
/ru/ok/tamtam/android/prefs/PmsKey.java - Коротко и наглядно: вот он, переключатель записи аудио, управляемый с сервера.
/one/video/calls/audio/opus/FileWriter.java - Это нативная запись аудио в файл.
Если вы дочитали до сюда - вы уже поняли суть.
Скажу одно: разница между «детектором плохой связи» и «детектором произвольных слов» - это один URL в JSON-конфиге. Модель, код, процесс отправки на сервер - всё одно и то же. Меняется только файл на CDN.
Я не знаю, планирует ли VK это использовать иначе. Но я знаю, что в политике конфиденциальности об этом ни слова, согласие не спрашивается, а модель можно скачать и проверить прямо сейчас. Ссылка выше.
Код - вот он. Модель - в открытом доступе. Проверяйте.
Фотография Юрия Гагарина через несколько часов после приземления.
Здесь нет его знаменитой «гагаринской» улыбки.
Есть человек, который, наконец, осознал, что произошло, и каким чудом он остался жив.
«Ещё на земле случилось несколько нештатных ситуаций.
Сначала взвешивание космонавта в скафандре показало, что он весит больше расчётного на 14 кг.
Пришлось срочно придумывать, - как уменьшить вес: срезали два датчика со скафандра.
После того, как задраили люк, оказалось, что он негерметичен.
До пуска оставалось мало времени, но успели раскрутить и закрутить 30 винтов, и запуск состоялся.
Дальше все опять было не по плану. Вторая ступень, с которой никогда не было проблем, отработала на 13 секунд больше, и космический корабль не вышел на расчётную орбиту, Гагарину об этом не сказали, но он понимал, что ситуация нештатная.
Несколько раз он спрашивал ЦУП, что случилось, но ответа не было.
Все ждали момента, сработает ли двигатель торможения.
Он сработал.
Но спусковая капсула от него не отделилась, и вместе они начали падать на землю.
«Я горю! Прощайте!»
Об этих словах Гагарина долго не говорили, но именно так он сказал, когда увидел в иллюминаторе языки пламени и ручейки расплавленного металла.
Он думал, что это гибель.
Но это обгорала обшивка, и благодаря высокой температуре капсула спуска наконец-то отсоединилась и началось штатное снижение.
Все мы знаем, что Гагарин должен был катапультироваться из капсулы вместе с креслом.
Катапультировался, летит, отброшено кресло, раскрылись парашют, вот она, Земля!
Нет, опять «нештатка».
Запас воздуха в скафандре закончился, а открыть его он не может, чтобы дышать уже земным воздухом.
6 минут....
И только после приземления это удалось.
Много ещё было мелких и не очень неурядиц, но Юрий Гагарин выжил и стал первым из первых!
Давайте помнить, какой ценой!
С Днём космонавтики!!!
Есть же здесь люди с опытом. Продвинем законодательную инициативу. Ещё немного и они предложат вешать гражданам бирки в уши как скоту, обосновывая это большим удобством, чем верёвка на шее. Доплаты не предусматриваются, у них нет риска как у полиции, МЧС и медиков. Также нет и ответственности за принятые решения.