Память ИИ-агента: как устроена долговременная память
Как устроена долговременная память ИИ-агента: файловое дерево заметок вместо базы, доступ через read, grep и list, ранжированный манифест поверх дерева, ночная дистилляция сессий раз в сутки. Три неочевидных решения: заметка ключуется темой, а не сессией; факт проходит проверку на выводимость из транскрипта и при сомнении не пишется; устаревшее не удаляется, а тускнеет по периоду полураспада. Плюс раздел, которого почти нигде нет: почему собственная память агента является каналом доставки инъекций.
Сам Решу
20 августа 2026 г. · 7 мин чтения
Содержание
Коротко. Долговременная память агента у нас устроена как среда, а не как база данных: файловое дерево заметок, по которому агент ходит теми же действиями, что и по любым другим файлам — read, read_many, grep, list. Раз в сутки сессии дистиллируются в это дерево фоновой задачей. Три решения в этом контуре стоит назвать отдельно, потому что они неочевидны: заметка ключуется темой, а не сессией; каждый факт проходит проверку на выводимость из транскрипта и при сомнении не пишется; ничего не удаляется — устаревшее тускнеет в ранжировании по периоду полураспада. И ещё один раздел, который в разговорах про память агентов почти не звучит: почему запись в собственную память является каналом доставки инъекций.
Задача: десятая сессия должна быть умнее первой
Человек работает с агентом месяцами и сотнями сессий. К десятой сессии система обязана быть заметно полезнее, чем в первой — помнить, что за проект, кто в нём участвует, какие форматы человек предпочитает, чем закончился прошлый разбор. Если каждый разговор начинается с нуля, это не сотрудник, а поисковая строка с хорошими манерами.
Проблема при этом двойная. Агенту мало знать факт — ему нужно знать, что этот факт вообще существует и как до него дойти. И, дойдя, нужно понимать, актуален ли он и откуда взялся. Одинокий факт без источника и без соседей стоит мало: его нельзя перепроверить и нельзя обновить.
Два очевидных решения оба плохи. Свалить все файлы памяти в контекст — размен точности на полноту: контекст забивается всё менее релевантным, а урезание бьёт по качеству ответа. Положить всё во внешнюю базу — гибко, но навигация ложится на агента: векторное хранилище отдаёт разрозненные обрывки, граф требует уметь его запрашивать.
Память как среда
Мы отдаём память агенту в том виде, в котором он уже умеет работать: как дерево файлов. Никакого специального интерфейса к памяти — любой такой интерфейс это ещё один язык, которому модель должна научиться и в котором она будет ошибаться.
Действий четыре, и все они файловые: read — прочитать заметку целиком, read_many — забрать несколько за один ход, grep — найти по содержимому, list — посмотреть, что вообще есть. Поверх дерева лежит манифест: компактный список того, что известно, с ранжированием. Он отвечает на первый из двух вопросов — «что этот факт вообще существует», — и без него grep бесполезен: нельзя искать то, о существовании чего не знаешь.
Связи между заметками считаются детерминированно, а не пишутся моделью: ребро по общему навыку, ребро по общей теме, ребро по близости. Модель, расставляющая ссылки в тексте, ошибается ровно там, где ошибается вообще — на редких и важных случаях; арифметика по навыкам и темам не ошибается нигде, зато и не догадывается. Мы сознательно выбрали второе: граф памяти — это навигация, а не содержание, и в навигации предсказуемость дороже находчивости.
Ночная дистилляция
Синтез — это не то же самое, что разговор, и внутри пользовательского хода ему не место. Раз в сутки, в 04:00 UTC, задача фанится по организациям и разбирает накопившиеся сессии: что из сказанного является долгоживущим фактом, к какой теме он относится, что в существующих заметках он отменяет.
Записывается результат атомарным батчем: либо приезжает весь набор правок, либо не приезжает ничего. Полуприменённая память хуже пустой — в ней заметки ссылаются на то, чего нет.
Ничего из этого не удаляет заметки. Дистилляция переписывает и добавляет; вычёркивание — отдельный разговор, и мы к нему ещё вернёмся.
Три решения, которые мы принимали осознанно
| Как у нас | Что это даёт | |
|---|---|---|
| Ключ записи | тема, а не сессия: два разговора об одном сливаются в одну заметку | хранилище держит картину того, что известно, а не журнал того, когда узнали |
| Проверка перед записью | гейт на выводимость факта из транскрипта, при сомнении — не писать | ошибка не доживает до следующего месяца в виде «собственного знания» агента |
| Устаревшее | не удаляется: тускнеет в ранжировании по периоду полураспада | заметку не судят по сигналу, который она не могла заработать |
Заметка ключуется темой, а не сессией. Это свойство самого ключа, а не результат последующей уборки: два диалога об одном и том же попадают в одну заметку и сливаются. Разница видна на длинной дистанции — журнал растёт линейно с числом разговоров, картина растёт с числом тем. Через полгода работы это разница между «памятью, в которой можно найти» и «архивом, в котором можно утонуть».
Факт не попадает в память, если он не выводится из своего источника. Отдельный лёгкий судья проверяет утверждение против захваченного транскрипта — не перезапрашивая живые данные, потому что проверять надо ровно то, из чего утверждение возникло. Не подтвердилось или ответ судьи не разобрался — утверждение отбрасывается: гейт закрыт по умолчанию. Цена за строгость честная: часть верных фактов теряется. Мы считаем этот размен правильным, потому что ошибка в долговременной памяти живёт месяцами и переписывается в новые ответы как своя — её никто уже не отличает от того, что человек сказал сам.
Мы ничего не удаляем. Заметка, которую перестали читать, не выпиливается через месяц — она опускается в ранжировании манифеста по периоду полураспада. Причина простая и она про механику, а не про бережливость: удаление судит заметку по сигналу, который она физически не могла заработать. Заметку читают только если манифест её предложил, а каждая, которую мы раньше удаляли, имела ноль прочтений. Это был приговор за то, что её не показывали.
Память как канал отмывания инъекций
Про архитектуру памяти пишут много и охотно. Про то, что запись в собственную память превращает одноразовую цитату в стоячую инструкцию, не пишет почти никто — а это самое неприятное свойство любой памяти агента.
Механика такая. Всё, что агент читает из веб-страницы, чужого MCP-сервера или чужой CRM, приходит на один ход и в огороженном виде: это данные, не команды. Но в тот момент, когда агент записывает вычитанное в файл собственной памяти, тот же текст возвращается без ограды, в стабильном префиксе, под директивой, которая называет его собственной памятью агента. Злоумышленник для этого не нужен: достаточно добросовестного пересказа враждебной страницы.
Поэтому файл проектной памяти проходит тот же досмотр, что и файл правил проекта, а отфильтрованный вердикт отличается от пустого файла: если тело задержано, рендер обязан это сказать. Иначе директива «поддерживай этот файл в актуальном состоянии» плюс «файл пуст» складываются в приглашение затереть реальную память ничем.
Что дальше
Четыре вещи, по убыванию соотношения «эффект / работа»:
- Индекс памяти в первое сообщение. Манифест у нас уже есть, вопрос только в том, чтобы он приезжал в стартовый контекст, а не по запросу. Пока агент должен догадаться сходить за списком, часть памяти для него не существует.
- Цитаты как рёбра. Заметки проверяются против источника на входе, но ссылка на источник не живёт в теле заметки как навигационный элемент. Это разные вещи: первое — гарантия при записи, второе — возможность перепроверить через месяц.
- Staged-дерево при ночной дистилляции. Мы пишем атомарным батчем, но набор правок собирается по одной заметке за раз. Обновлять граф целиком правильнее, особенно когда правки взаимозависимы: новая заметка и переписанная соседняя должны приезжать как одно изменение.
- Парный замер. Самое неудобное и самое нужное. У нас нет числа, которое говорит, сколько стоит наша память — включённая против выключенной, на одних и тех же вопросах. Пока такого числа нет, любые улучшения памяти делаются на вкус.
Честно о границах
Своих цифр мы здесь не приводим ни одной, и это не скромность: парного замера «память включена / выключена» у нас пока нет, а любые другие числа — про нагрузку и объём, а не про пользу. Всё, что написано выше, — это описание архитектуры и обоснование принятых решений, а не обещание процентов. Пункт 4 в списке выше стоит последним по объёму работы и первым по важности ровно поэтому.
Частые вопросы
Чем это отличается от RAG? Ретривал достаёт куски, похожие на запрос. Заметка — это компилируемый офлайн артефакт: она написана заранее, привязана к теме и к источнику, связана с соседями и переживает много сессий. Ретривал отвечает «что похоже на вопрос», память — «что известно по этой теме». Одно не заменяет другое: сырьё для заметок всё равно нужно искать.
Почему файлы, а не база?
Потому что агент уже умеет в файлы. Любой специальный интерфейс к памяти — это ещё один язык, которому модель должна научиться и в котором она будет ошибаться. grep она уже знает.
Почему обновление памяти делается ночью, а не в диалоге? Потому что хорошая заметка требует пройти несколько сессий, сравнить их, отбросить противоречия и переписать существующее. Внутри пользовательского хода на это нет ни времени, ни контекста, а результат должен пережить сам ход.
Что происходит, когда факт меняется — например, человек сменил работу? Заметка о теме переписывается, а не дополняется. Старое утверждение не остаётся рядом с новым как альтернативная версия — иначе в памяти окажутся оба, и агент будет выбирать между ними случайно.
Видит ли агент чужую память? Нет. Дистилляция фанится по организациям, и дерево заметок принадлежит организации, а не общей куче.
Применить на практике
Подключите свои сервисы и поручите эту задачу ИИ-агенту — без ручной рутины и таблиц.