Тупость, ломающая контекст агента
Симптом
Пользователь LLM-агента последовательно присылал скриншоты слайдов и просил перевести текст на английский. Первые N ходов - корректный перевод присланного слайда. С N+1 агент начал отвечать переводом постороннего слайда: сначала выдумал кейс про компанию, которой на слайде не было, потом на прямое указание «переводи строго то, что на скриншоте» ответил переводом самого первого слайда чата.
Контекст
Мультимодальный агент собирает контекст хода из истории сообщений и артефактов
чата. Все vision-артефакты чата - загрузки пользователя и сгенерированные
картинки - складываются в один список, сортируются по uuid7 и уходят в модель
как content-parts. N - конфигурируемый лимит артефактов, уходящих в модель.
Планировщик при этом отрабатывал корректно, в steps лежал осмысленный промпт. Ошибок не было.
Корневая причина
Функция сбора путей к картинкам обрезала список срезом с головы
paths[:max_images] вместо paths[-max_images:], то есть оставляла десять
самых старых картинок и выбрасывала самые свежие.
Claude и Codex просмотрели. Разработчик просмотрел. Я просмотрел.
Ошибочное предположение
Просто тупо просмотрели.
Обнаружение
Юнит-тест, который кладёт в контекст строго больше MAX_IMAGES картинок и
ассертит, какие именно пути ушли в модель. Он и оказался достаточным: на
откаченном коде красный, на починенном зелёный.
Второй, независимый сигнал - warning уже писался в лог при каждой обрезке
(Multi-image turn truncated: %d images...), но никуда не агрегировался. Алерт
на рост этого warning’а показал бы, что обрезка вообще регулярно срабатывает в
проде, задолго до того, как это заметил пользователь.
Исправление
В функции сбора путей к картинкам срез изменён с paths[:max_images] на
paths[-max_images:]; текст warning’а исправлен с «sending first» на «sending
most recent»; рядом оставлен комментарий с причиной и упоминанием инцидента.
Это настоящий фикс исходного дефекта, а не обход.
Проверка
Добавлены два юнит-теста на сбор картинок:
- лимит понижен до 3, в контексте 5 картинок, ассерт - ушли ровно три последние и warning об обрезке вызван один раз;
- лимит 2, четыре картинки поданы в перемешанном порядке - ассерт, что сортировка по идентификатору восстанавливает хронологию и направление среза сохраняется.
Оба проверены на откате: на старом коде красные, на новом зелёные. Проверка «тест красный до фикса» здесь обязательна - предыдущий тест на ту же функцию был зелёным при обоих направлениях среза.
Общий вывод
Читать код всё ещё нужно внимательно, а тестировать нужно все границы лимитов.