Блог

Нехватка памяти, спрятанная в подписи прогресса

Однажды вечером в конце сентября AI-агент в рабочем пространстве одного пользователя упал с ошибкой нехватки памяти посреди хода. Очевидные подозреваемые — слишком много субагентов, работающих одновременно, или слишком выросший контекст — оба оказались неверными. Настоящей причиной была строка статуса: пока модель передавала большую shell-команду по нескольку символов, мы при каждом фрагменте заново отправляли всю команду целиком как подпись прогресса. Одна команда размером 124 КБ превратилась примерно в 41 000 событий и около 690 МБ строк в очереди.

Как агент передаёт свою работу

Когда вы создаёте что-то в Mythex, агент работает внутри приватной облачной песочницы вашего проекта. Пока модель пишет, её вывод передаётся в ваш браузер: текст появляется слово за словом, а когда агент запускает инструмент — например, shell-команду, которая записывает файл, — вы видите подпись к нему, которая обновляется на лету, и понимаете, что он делает.

Модель передаёт данные маленькими кусками. С моделью, которую мы использовали, эти куски часто были всего три символа длиной.

Первые гипотезы оказались неверными

Падение выглядело как проблема масштабирования, поэтому мы начали с неё.

  • Параллельные субагенты? В ходе использовалось только два, и каждый занимал около 1,5 МБ состояния. Недостаточно.
  • Удерживаемые трассировки? Наша трассировка держала дерево запусков в памяти, но оно было далеко не такого размера, как куча в момент падения.

Оба объяснения были правдоподобны и одинаково легко принимались на веру. Ни одно не воспроизводило падение. Урок, который мы то и дело заново усваиваем: гипотеза — это ещё не причина, пока она не воспроизводит сбой.

Воспроизведение по-настоящему

Мы собрали одноразовую песочницу с теми же лимитами памяти, что в продакшене, и поддельную модель, которая передавала большой файл реалистичными кусками по три символа. Мы также сделали читателя на другом конце сокета медленным, как настоящая сеть.

Это сработало. Куча росла, и процесс упал с той же ошибкой.

Механизм стал простым, как только мы смогли за ним наблюдать. Пока модель писала большую команду — в данном случае heredoc, который записывал файл на 124 КБ, — наш код потоковой передачи обновлял подпись инструмента при каждом фрагменте. Подпись содержала не новые три символа, а всю команду целиком на данный момент. Поэтому события росли: 3 символа, потом 6, потом 9… до 124 КБ, примерно 41 000 раз. Сокет не успевал отправлять их так быстро, как они создавались, и они копились в его буфере записи в виде строк — около 690 МБ.

Исправление

  • Ограничить подпись. Строка статуса должна лишь сообщать, что происходит. Теперь она ограничена 200 символами, а частоту повторного сканирования частичной команды мы ограничиваем.
  • Убрать квадратичную конкатенацию. Склеивание переданных кусков повторной конкатенацией строк для больших выводов имеет сложность O(n²). Мы перешли на подход с чанками, который добавляет данные за линейное время.

После этого мы поискали другие места, которые могли бесконечно расти под потоком вывода, и ограничили их:

  • Вывод команд в реальном времени, показываемый в чате, пакетируется и хранится только последние 8 000 символов.
  • Отдельные строки событий ограничены 4 МБ.
  • Буфер воспроизведения, который браузер использует, чтобы догнать события после переподключения, ограничен 8 МБ на проект.
  • Старые чекпоинты обрезаются по ходу хода, сохраняя самое свежее состояние и опорные точки, которые нужны более ранним ходам.

Честное тестирование потоков

Две вещи сделали тестирование сложнее, чем должно было быть:

  1. Настоящие модели не сотрудничают. Попросите модель вывести огромный файл, и она обычно откажется, сократит или запишет его разумнее. Чтобы тестировать потоки, нужна скриптованная модель, которая делает ровно то, что вы задали, и работает через настоящий код агента.
  2. Скрипты должны вести себя как модели. Наша первая скриптованная модель использовала один и тот же ID ответа при каждом ответе, поэтому фреймворк агента воспринимал каждый ответ как замену предыдущего и завершал ходы раньше времени. Это выглядело как баг обновления интерфейса. Это был не он: тестовый стенд нас обманывал. Нам также пришлось задать в скрипте реальный лимит контекста модели, иначе цифры по памяти были нереалистичными.

Что мы из этого вынесли

  • Потоковые интерфейсы умножают мелкие ошибки. Всё, что отправляется «на каждый кусок», уходит десятки тысяч раз. Отправляйте дельты или ограничивайте объём данных.
  • Ограничивайте каждый буфер, который пересекает медленную границу. Сокеты, журналы воспроизведения и чекпоинты — всем нужен потолок.
  • Воспроизводите, прежде чем чинить. Наши первые два объяснения звучали убедительно и были неверны.

Если в последнее время в чате Mythex вы видели, как появляется очень длинный файл, а агент при этом не зависает, — отчасти поэтому.

Mythex · Pricing · Создавайте что угодно с ИИ · Шаблоны сайтов и приложений · Сравнения Mythex · Глоссарий по созданию приложений с ИИ · Бесплатные инструменты для создания приложений с ИИ · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)