В Дне 1 ты узнал, что LLM stateless. В Дне 6 — что агент работает циклом. Сегодня — как эти две идеи живут вместе. И почему «контекст» — это не туманная концепция, а конкретный массив, который ты собираешь руками каждую итерацию.
Память агента — это не свойство модели. Это массив messages, который твой код пересобирает перед каждой итерацией: туда кладутся все предыдущие шаги (мысли модели, вызовы инструментов, их результаты), и весь массив отправляется в LLM целиком. Модель смотрит на эту «фотографию текущего состояния» и решает, что делать дальше. Понимание этой механики — ключ к управлению агентом.
В Дне 1 был главный инсайт: модель ничего не помнит между запросами. В Дне 6 ты увидел: агент работает в цикле и явно опирается на предыдущие шаги. Как одно совместимо с другим?
Очень просто. Модель — по-прежнему stateless. Память — у твоего кода вокруг неё. Каждую итерацию цикла твой код пересобирает массив сообщений: туда кладётся вся история ходов агента до этого момента. Этот собранный массив целиком отправляется в LLM как новый запрос. Модель видит «фотографию» — и отвечает.
Память агента — это не способность модели. Это массив messages, который твой код пересобирает заново перед каждым шагом.
Из этого следует, что у тебя есть полный контроль над тем, что модель «помнит». Что ты положишь в массив — то она и увидит. Не положишь — не увидит. Положишь криво — увидит криво.
В каждой итерации цикла массив сообщений выглядит примерно так:
Главные наблюдения:
Кликни по табу — увидишь подробнее, что и зачем кладётся в каждую роль:
Это политика на всю задачу. Сюда идёт описание роли агента, перечень инструментов с их назначением, ограничения («не вызывай send_email больше одного раза», «не более 10 запросов»), формат внутренних размышлений.
system пишется один раз перед запуском агента и не меняется на протяжении всего цикла. Это стабильная часть контекста, по которой модель ориентируется, кто она и что ей можно.
Что важно: чем длиннее system — тем больше токенов уходит на каждую итерацию. Это плата за «правила игры». Поэтому system должен быть точным, без воды.
Запрос пользователя, который запустил агента. Кладётся один раз, в начале. В большинстве архитектур больше user-сообщений за время работы агента не появляется — только если ты делаешь интерактивного агента, который может уточнить что-то у пользователя посреди работы.
Тонкость: в продвинутых системах user-запрос предобрабатывается до того, как попасть в агента — обогащается контекстом из памяти, RAG-документами, метаданными. Об этом будет в следующих кластерах.
На каждой итерации модель добавляет одно assistant-сообщение. В нём — её рассуждения (внутренний монолог: «нужно сделать X, потому что Y») и вызов инструмента (или финальный ответ, если задача решена).
Это история «мыслей агента». Когда на 7-й итерации модель смотрит назад, она видит, как она сама рассуждала на шагах 1-6 — и это помогает ей не повторять одни и те же ошибки и не возвращаться к уже отброшенным гипотезам.
Концептуально важное: модель видит свои прошлые ходы и должна вести себя последовательно. Если на шаге 3 она решила «попробуем подход А», на шаге 7 она не должна вдруг сказать «попробуем подход А» снова. Хороший system prompt явно про это напоминает.
После каждого вызова инструмента в массив идёт сообщение с ролью tool (или function, зависит от провайдера) — содержащее результат выполнения. Текст веб-страницы, JSON от API, содержимое файла.
На следующей итерации модель видит эти результаты как «то, что я узнала на шагах 1-N» — и принимает следующее решение на их основе.
Самая большая опасность: tool-результаты — это та часть контекста, которая растёт быстрее всего и непредсказуемо. Один fetch_url может вернуть 50 000 токенов. И это повторится на каждой следующей итерации, потому что весь массив отправляется заново. Об этом — следующий раздел.
Самое неочевидное и самое дорогое свойство агента — контекст растёт квадратично относительно числа итераций. Не линейно. Это критически важно понять.
Представь, что каждая итерация добавляет в массив новый блок длиной N токенов (мысль модели + результат инструмента). Тогда:
За 10 итераций суммарно ты отправил: N + 2N + 3N + ... + 10N = 55N токенов. Не 10N (как было бы при линейном росте), а 55N. На 20 итерациях это уже 210N. На 50 — 1275N.
Из-за квадратичного роста сразу несколько вещей становятся важными:
Запуск агента в 5-10 раз дороже одного обычного запроса, иногда — на порядок. Это нужно закладывать в архитектуру с самого начала.
Каждая итерация = ожидание модели. Агент с 10 итерациями работает не 1 секунду, а 20-60. Пользователю нужно показывать прогресс.
Даже при 200K-токенном окне можно его исчерпать на длинной задаче. Когда упрёшься — нужны стратегии сжатия (День 9).
Чем длиннее контекст, тем хуже модель помнит то, что в его середине. На 30-й итерации модель может «забыть» важное из шага 5.
Поэтому AI-инженер, работающий с агентами, обязан думать о контексте как о ресурсе. Это не «деталь оптимизации» — это основной навык. Дни 8-10 как раз об этом.
Часто на каждой итерации модель пишет внутренние рассуждения — длинные, на 300-500 токенов: «думаю, нужно сделать X, потому что Y...». Эти рассуждения помогают модели на текущем шаге, но раздувают контекст на следующих.
Решение: отделить «мысли» от «действий». В контекст на следующую итерацию класть только реально принятые решения и результаты инструментов — а длинные рассуждения сжимать или вообще обрезать. Многие фреймворки делают это автоматически.
Когда контекст приближается к окну модели — нужно что-то выбрасывать. Самое простое — выбрасывать самое старое (первые итерации). Но именно там часто лежит критически важная информация: исходный запрос, ранние находки.
Правильнее — выбрасывать наименее релевантное на данный момент: подробные тексты статей, которые уже прочитаны и из которых уже извлечены факты; промежуточные ошибочные шаги, от которых модель отказалась; повторяющиеся вызовы. Об этом подробно — в Дне 9.
«Модель помнит, что мы делали на шаге 3». Нет — модель видит запись о шаге 3 в массиве сообщений, который ты ей подал на 7-й итерации. Перестанешь подавать — она перестанет «помнить». Это твой массив, твоя ответственность.
Эта мысленная модель меняет всё: ты не уговариваешь модель «не забывать», ты решаешь, что в её контексте на этом шаге будет.
«Раз инструмент вернул — пусть модель видит». В итоге контекст забит сырыми данными: HTML-разметка, JSON-ответы API, длинные тексты. Модель не успевает их обрабатывать, выбирает нерелевантное, запутывается.
Привычка: результат инструмента — это сырой материал, который твой код должен обработать перед тем, как класть в контекст. Извлеки главное, отбрось мусор, ужми. Это не «фича оптимизации», это часть архитектуры агента.
Агент работает, отвечает пользователю — а ты видишь только финальный ответ. Когда он «не сработал» или сделал не то — у тебя нет данных, чтобы разобраться где именно модель свернула не туда.
Лечится привычкой: логировать каждую итерацию агента полностью — что было в reason, какой инструмент вызван, что вернулось. Это твой единственный способ отладки. Без логов агент — чёрный ящик.
В современных ассистентах есть режимы, где можно увидеть «технические детали» работы агента. В Claude — раздел «thinking» в развёрнутом виде; в ChatGPT — «Show tools». Сделай запрос, требующий нескольких шагов («найди три цены на ноутбуки и сравни»). Посмотри, как разворачивается цепочка: рассуждение → инструмент → результат → новое рассуждение → новый инструмент → ... Это и есть массив messages в действии.
Возьми гипотетического агента с 20 итерациями. Каждая итерация добавляет 1000 токенов (думаю + результат). На 20-й итерации в LLM улетает 20 000 input-токенов. Суммарно за всю задачу — 210 000 input-токенов. Цена на gpt-4o ~$0.50, на gpt-5 ~$2-5. Это одна задача. Прикинь, что будет на тысяче задач в день. Это даст тебе ощущение, почему в Кластере 02 столько внимания контексту.
В плейграунде вручную собери массив сообщений с несколькими assistant-сообщениями (как будто это был многоходовой диалог). Подмени один из ассистент-ответов на фальшивый — например, скажи «я уже выяснила, что ответ — 42». Запусти следующий ход. Увидишь, как модель опирается на «свои» прошлые слова, даже если они на самом деле никогда не были произнесены. Это и есть демонстрация того, что массив messages — это конструкция твоего кода, не реальная история.