Искусственный интеллект | Олег Брагинский, Марина Строева
Основатель «Школы траблшутеров» Олег Брагинский и ученица Марина Строева разбирают, как с помощью ИИ-агента удалось стабилизировать процесс остановки сервера, устранить ошибку несовместимого кодека, восстановить отправку аудио и ликвидировать зависание.
Отладка медиа-серверов – одна из самых сложных задач в backend-разработке. Здесь нет простых ошибок «упал сервер – починил», а есть цепочки из RTP, кодеков, нативных библиотек, сетевых задержек и асинхронных событий.
Ключевая особенность процесса – вся работа велась через чат с агентом, который анализировал логи, предлагал гипотезы и вносил изменения в код. Это позволило пройти путь от «ничего не работает» до стабильной системы за несколько итераций.
Речь идёт о Go-сервере, который управляет звонками и взаимодействует с нативным медиа-стеком (через bridge и mediasoup). В системе есть несколько ключевых компонентов: обработка входящего аудио (STT), генерация синтетического аудио (TTS), и их доставка в звонок через SFU.
На практике это означает, что любая проблема может возникнуть на произвольном уровне – от кодека до сетевого соединения. Поэтому отладка строится не через догадки, а через последовательный разбор логов и проверку гипотез.
Основные задачи, которые нужно было решить:
- восстановить исходящее аудио между агентом и пользователем
- убедиться, что входящее аудио от пользователя к STT работает
- стабилизировать поведение при перезапуске сервера
- устранить зависания при обработке звонков.
Шаг №1
Первый симптом выглядел типично: пользователь говорит, STT отрабатывает, но ответа собеседника не слышно. Это сразу указывает на асимметричную проблему – входящий поток работает, исходящий нет. Промпт, с которого началась диагностика:
«Посмотри логи сервера»
Агент начал с анализа логов и быстро нашёл ключевую ошибку:
invalid params.opusMaxAverageBitrate
При попытке отправить аудио в медиа-сервер (mediasoup), параметры кодека не проходили валидацию. В результате трек просто не создавался, а система молчала. Решение оказалось точечным, агент предложил убрать неподдерживаемый параметр из конфигурации кодека:
- удалить opusMaxAverageBitrate из audioCodecOptions
- пересобрать нативную библиотеку
После этого в логах появилась ключевая строка:
speak_in_call pushed pcm_samples=120929
PCM-данные начали успешно передаваться в медиапайплайн.
Шаг №2
После фикса важно было не просто увидеть лог, а убедиться, что вся цепочка действительно работает. В медиа-системах часто бывает, что один этап проходит, но дальше данные теряются. Агент проверил несколько признаков корректной работы:
- наличие STT-фраз (входящий поток жив)
- успешный push PCM (исходящий поток отправляется)
- отсутствие ошибок produce
Дополнительно были замечены повторяющиеся строки:
addTrack skip already_consumed
На первый взгляд это выглядело как ошибка, но агент корректно интерпретировал это как нормальное поведение при повторной синхронизации состояния. Важный момент: в отладке медиа-систем важно уметь отличать шум от реальной проблемы.
Шаг №3
Следующая проблема была сложнее. После некоторого времени работы система переставала реагировать:
- звонки не обрабатываются
- сообщения игнорируются
- в логах почти пусто
Такое поведение особенно опасно, потому что выглядит как «всё работает, но ничего не происходит».
Следующий промпт был таким:
«В логах тишина, сервер не отвечает ни на что, как будто клиент умер. Найди, где зависание»
Агент начал анализ не с кода, а с архитектуры потоков. Выявил, что ключевая горутина (loopEvents), отвечающая за получение событий через long-poll, перестаёт обрабатывать события.
Причина оказалась нетривиальной:
- внутри обработки событий держится mutex (domainPayloadMu)
- под этим mutex вызывается нативная функция connect
- connect может зависать из-за сети, TLS или DNS.
В итоге:
- новые события не обрабатываются
- mutex не освобождается
- система «замирает»
Шаг №4
После обнаружения причины задача свелась к изменению архитектуры выполнения. Нужно было разорвать цепочку «долгая операция под lock».
Промпт:
«Вынеси connect из-под domainPayloadMu и сделай его асинхронным, чтобы long-poll не блокировался»
Агент предложил корректное решение:
- запускать HeadlessEnsureRoomConnected в отдельной горутине
- оставить под mutex только быстрые операции
- обработку треков выполнять отдельно.
Это привело к тому, что long-poll перестал блокироваться, события снова начали поступать, система «ожила» без рестарта.
Шаг №5
После стабилизации рантайма всплыла ещё одна проблема – уже на этапе остановки сервера. Симптом:
State 'stop-sigterm' timed out. Killing.
То есть сервер получает SIGTERM, не успевает завершиться и systemd убивает его через приблизительно 90 секунд.
Промпт для решения:
«Почему сервер не останавливается и висит до SIGKILL?»
Агент нашёл цепочку:
planet.Shutdown()
→ stop()
→ wg.Wait()
→ loopEvents не завершился
И снова корень оказался в том же месте – зависшая горутина. Решение было таким: изменить порядок shutdown (сначала HTTP, потом внутренние сервисы), добавить таймаут на planet.Shutdown и ограничить общее время graceful shutdown.
В итоге вся отладка заняла несколько итераций, каждая из которых снимала отдельный слой проблемы. Это типично для real-time систем: одна ошибка редко бывает единственной.
В результате работы были достигнуты следующие улучшения:
- исправлена архитектурная блокировка mutex
- стабилизирован процесс остановки сервера
- устранена ошибка несовместимого кодека
- восстановлена отправка аудио в звонок
- ликвидировано зависание long-poll.
Отладка медиа-сервера – не поиск одной ошибки, а последовательное снятие слоёв проблем. ИИ-агент в этом процессе оказался особенно полезен не как генератор кода, а как системный аналитик, который удерживает в голове сразу несколько уровней архитектуры.
Но ключевой фактор успеха – правильные промпты. Чем точнее формулируется проблема («где блокируется поток», «почему produce падает»), тем быстрее агент выходит на реальную причину, а не лечит симптомы.
Кейс показывает: даже сложные real-time системы можно отлаживать быстро, если разбивать проблему на этапы и последовательно проверять каждую гипотезу.