- BrainTools - https://www.braintools.ru -

Меня зовут Сергей Врулин, я Team Lead в команде агентов VK AI Space [1] — корпоративной платформы для создания и запуска AI-агентов.
В июле 2026 года VK AI Space представила [2] многоуровневую память [3] для корпоративных AI‑агентов. Она позволяет агентам сохранять и переиспользовать информацию на всех этапах работы — в текущем диалоге и в рамках долгих проектов. Потенциально это позволит ускорить подготовку ответов и уменьшить число повторных задач.
В этой статье я во всех деталях расскажу, как именно мы проектировали память для агентов: от теории к реализации, с разбором ключевых решений и компромиссов.
Типичный LLM-агент — это функция без состояния. Контекстное окно модели ограничено, и как только диалог заканчивается, всё исчезает. То есть каждый следующий разговор начинается с чистого листа: пользователь заново объясняет контекст, агент переоткрывает файлы, повторяет поиск, снова угадывает предпочтения.
В потребительских сценариях это терпимо — чат-боту не обязательно помнить, о чем его спрашивали месяц назад. Но в корпоративном сегменте отсутствие памяти убивает смысл «цифрового сотрудника». Чтобы понять, насколько это критично, разберем несколько ситуаций.
Разбор инцидента. Агент помог разобрать сбой в продакшене. Через неделю — похожий инцидент. Без памяти агент начнёт с нуля: заново будет искать логи, выяснять, какие сервисы связаны, предлагать гипотезы. С памятью — «вспомнит» прошлый разбор и сразу предложит проверить ту же цепочку.
Подготовка тендера. Сотрудник с агентом собирает ответы на требования, а через месяц — новый тендер от того же заказчика. Без памяти агенту придется делать всё заново. С памятью — агент достанет готовые формулировки и адаптирует их.
Проектный отчёт. Агенту можно поручить сбор ежедневного дайли-репорта. Без памяти ему каждый день придется объяснять формат, источники, получателей. С памятью — агент уже знает, как вчера выглядел отчёт, и повторяет формат.
Поэтому мы захотели, чтобы агент помнил профиль сотрудника, накопленные решения по проекту и собственный опыт [4] — и переиспользовал это между сессиями.
Для начала рассмотрим, какие виды памяти бывают в агентах. Выделим пять основных:
|
Тип памяти |
Что хранит |
Пример |
Горизонт |
|
Кратковременная (working) |
Текущий диалог |
История сообщений сессии |
Одна сессия |
|
Эпизодическая |
Конкретные события |
«Пользователь просил …, я сделал …» |
Дни/недели |
|
Семантическая |
Факты о мире/домене |
Правила проекта, спецификации |
Долго |
|
Процедурная |
Информацию о том, как делать задачи |
Навыки, инструменты, подходы |
Долго |
|
Профильная |
Данные о пользователе |
Предпочтения, роль, контекст |
Долго |
И интересно понять, как эти виды реализованы в других проектах — например, в MemGPT [5], LangGraph [6], CrewAI [7], Cline [8], Claude Code [9]. Рассмотрим подходы в каждом из них.
MemGPT использует paging-модель, где контекстное окно выступает «основной памятью», а долгая живет во внешних хранилищах и подгружается по требованию через специальные функции (core_memory_append, recall_memory_search). Но модель сама решает, что загружать, из-за чего тратит итерации LLM без гарантии своевременного извлечения данных.
LangGraph опирается на checkpointing состояния графа для кратковременной памяти [10] и низкоуровневое key-value хранилище для долгосрочной. Но семантику сборки контекста разработчик вынужден строить самостоятельно.
CrewAI явно разделяет память на краткосрочную (диалог), долгосрочную (векторные эпизоды) и профильную, но сталкивается с проблемой прозрачности: векторные эмбеддинги нечитаемы для человека.
Cline («Memory Bank») и Claude Code (CLAUDE.md [11]) используют наборы Markdown-файлов как читаемый источник контекста проекта вместо скрытых баз данных.
Анализируя подходы этих инструментов, можно столкнуться с ограничениями в корпоративном контуре. Выделю основные:
Прозрачность. Векторное хранилище — чёрный ящик. Сотрудник не может посмотреть, что именно агент «запомнил»: эмбеддинги нечитаемы, а восстановить исходный текст по вектору нельзя. Для корпоративного сегмента это блокер: compliance требует, чтобы человек мог в любой момент увидеть, какие данные о нём и о проекте хранятся.
Изоляция. Большинство фреймворков не имеют встроенной модели изоляции по проектам/пользователям/агентам. Её нужно строить поверх — и легко ошибиться так, что память одного проекта «протечёт» в другой.
Управляемость. Исправить неверный факт в векторной БД — это не «открыть и поправить строчку». Нужно найти нужный чанк, удалить, переэмбеддить, перезаписать. Человек не может просто взять и отредактировать то, что помнит агент.
Исходя из этого, мы сделали выбор в пользу модели «память как обычные текстовые файлы» (plain text / Markdown), а не эмбеддингов в векторной БД. У такого подхода несколько весомых преимуществ.
Читаемость по умолчанию. Файл USER.md [12] или SOUL.md [13] можно открыть и прочитать — без инструментов, без декодирования. Что написано, то агент и помнит. Никакого зазора между «что хранится» и «что видит человек».
Редактирование в обычном редакторе. Память правится так же, как любой документ: открыл в редакторе, исправил формулировку, удалил лишний абзац, сохранил. Не нужно ни API векторной БД, ни специальных инструментов агента — сотрудник работает с памятью напрямую, как с текстом.
Diff и версионирование. Текст естественно ложится на привычные инструменты: видно, что и когда изменилось, можно откатить. Для эмбеддингов такого нет.
Предсказуемость для LLM. Модель получает контекст ровно в том виде, в каком он записан в файле — без «примерного» семантического поиска, который может вернуть не тот чанк.
При этом мы сознательно отказались от семантического поиска «по смыслу» на больших объёмах: plain text хорош, пока память соразмерна контекстному окну. Для нашего сценария (профиль пользователя, правила проекта, эпизоды команды) этого достаточно, а прозрачность и управляемость важнее.
К самой памяти мы сформулировали несколько требований:
Персистентность между сессиями. Важно, чтобы агент помнил контекст после перезапуска. Профиль сотрудника, накопленные решения по проекту, собственный опыт — всё должно переживать завершение диалога.
Нулевые итерации на загрузку. Контекст должен быть доступен с первого сообщения, чтобы агент не тратил вызовы LLM на чтение файлов памяти. Это и про скорость, и про предсказуемость: если загрузка памяти — это вызов инструмента, модель может его «забыть» сделать.
Единый интерфейс записи. Хотели, чтобы агент писал память как обычные файлы, без специализированных инструментов. Меньше инструментов — меньше когнитивная нагрузка на LLM, меньше кода — меньше точек отказа.
Изоляция и безопасность. Было важно, чтобы агент видел только те данные, которые доступны сотруднику по роли. Проектные файлы — только участникам проекта. Персональные — только владельцу.
Управляемость и прозрачность. Требовалось, чтобы человек мог посмотреть, что помнит агент, отредактировать или удалить. Память не должна быть чёрным ящиком.
Последние два пункта — это требования compliance для корпоративного сегмента. Без их выполнения продукт не выйдет на рынок.
Исходя из этого, в своей реализации мы решили скомбинировать разные типы памяти. Сделали это следующим образом.
|
Тип памяти |
Реализация у нас |
Где хранится |
Как попадает в контекст |
|
Кратковременная (working) |
История сообщений текущей сессии |
В памяти раннера (session scope) |
Инъектится при сборке контекта в основном рантайме агента |
|
Эпизодическая |
Файлы событий в проектной области, дописываемые post-hook’ами |
S3, scope=project |
Инъекция через pre-hook и чтение через sandbox |
|
Семантическая |
SOUL.md [13] — идентичность и характер агента, знания о том, «кто он есть |
S3, scope=agent |
Инъекция через pre-hook (содержимое) |
|
Процедурная |
AGENTS.md [14] — правила и инструкции «как делать»; TOOLS.md [15] — как работать с инструментами |
S3, scope=agent |
Инъекция через pre-hook (содержимое) |
|
Профильная |
USER.md [12] |
S3, scope=user |
Инъекция через pre-hook (содержимое) |
При этом мы используем четыре, управляемых агентом, системных файлы, которые несут семантическую (SOUL.md [13]), процедурную (AGENTS.md [14], TOOLS.md [15]) и профильную (USER.md [12]) память. Плюс — произвольные файлы в workspace проекта (эпизодическая память: события, дописываемые post-hook’ами).
Здесь стоит отметить, что граница между типами условна — один файл может нести оттенки нескольких. Более того, у нас «под капотом» нет пяти разных подсистем памяти — всё это лишь разные конфигурации одного механизма, логика [16] которого живет в pre/post-хуках (post-hook дописывает эпизоды, pre-hook инжектит всё остальное).
Изоляцию памяти мы построили на scope-модели. Семь типов и для каждого свой префикс и свои правила доступа в S3-бакете:
|
Scope |
S3-префикс |
Что хранит |
Поля, определяющие scope |
|
session |
projects/{pid}/sessions/{sid}/workspace/ |
Файлы текущей сессии |
ProjectID, SessionID |
|
user |
projects/{pid}/users/{uid}/workspace/ |
Профиль пользователя ( |
ProjectID, UserID |
|
agent |
projects/{pid}/agents/{aid}/workspace/ |
Системные файлы агента ( |
ProjectID, AgentID |
|
agent_user |
projects/{pid}/agents/{aid}/users/{uid}/workspace/ |
Пересечение агент+пользователь |
ProjectID, AgentID, UserID |
|
project |
projects/{pid}/workspace/ |
Файлы проекта (эпизодическая память) |
ProjectID |
|
skills |
projects/{pid}/skills/ |
Скиллы проекта |
ProjectID |
|
hooks |
projects/{pid}/hooks |
Скрипты хуков проекта |
ProjectID |
Для памяти задействованы agent, user и project. Префикс строится функцией BuildS3Prefix — единая точка правды для формата ключей. Валидация строгая: если для scope не хватает обязательного поля (например, AgentID для agent scope) — возвращается доменная ошибка [17].
При этом:
семантическая память изолируется по AgentID — разные агенты не пересекаются;
процедурная — по AgentID (те же префиксы, что у SOUL.md [13]);
профильная — по UserID (строгая изоляция между пользователями);
эпизодическая — по ProjectID (доступны всем участникам проекта);
кратковременная — по SessionID (только текущая сессия).


Lakehouse-платформа для аналитики и ML
Объединяйте данные из разных систем и снижайте расходы на хранение в 7–10 раз
Как я уже упомянул раньше, в качестве основного подхода мы определили, что вся память хранится как файлы. При этом на текущем этапе развития платформы специализированные memory-БД, векторное хранилище или набор инструментов просто лишние — агент будет работать с файлами через песочницу OpenSandbox. Аргументов в пользу такого решения сразу несколько.
Sandbox-подобный (openclaw-подобный) подход даёт гибкость. Песочница монтирует файлы из S3, агент работает с ними как с локальной файловой системой, а sandbox синхронизирует изменения обратно. Это та же модель, по которой работают современные coding-агенты (Claude Code, OpenCode и подобные): агент не знает про S3 и API — он просто пишет в файлы, а инфраструктура разбирается с персистентностью.
Меньше кода — меньше точек отказа. Не нужно реализовывать специализированные memory-инструменты (write_memory_file, list_memory_files), их factory-регистрацию, ToolTypeID, prompt hints. Sandbox уже есть, файловые операции уже есть — переиспользуем.
Файлы монтируются как volume (read-only или read-write). Изменения синхронизируются обратно в S3 при завершении работы. Подходит для файлов, которые агент должен обновлять в процессе — USER.md [12], файлы проекта.
Жизненный цикл записи выглядит следующим образом:
Шаг 1. Агент вызывает write_file для /workspace/user/USER.md [12].
Шаг 2. Sandbox FS watcher замечает изменение в volume-mount.
Шаг 3. Файл выгружается обратно в projects/{pid}/users/{uid}/workspace/.
Шаг 4. Сессия завершается, sandbox уничтожается.
Шаг 5. Следующий запуск: sandbox mount уже содержит обновлённый USER.md [12].

Одной из задач при построении многоуровневой памяти для AI-агентов было определение способа передачи содержимого памяти в контекст.
Упрощенно здесь возможно несколько вариантов.
Способ подразумевает создание специализированных инструментов: write_memory_file, list_memory_files, read_memory_file. Чтение — on-demand через read_memory_file, запись — через write_memory_file, навигация — через list_memory_files.
У такого подхода есть два преимущества:
memory-специфичная валидация (лимиты, разрешённые имена) инкапсулирована в инструменте;
чёткая ответственность — каждый инструмент делает одно.
Вместе с тем, есть и ощутимые недостатки:
новые инструменты существенно повышают когнитивную нагрузку на LLM (модель должна выбрать между read_memory_file и load_storage_file_content);
агент тратит итерации на on-demand загрузку каждого файла;
есть риск, что агент забудет вызвать read_memory_file для USER.md [12] и сгенерирует ответ без учета контекста пользователя;
write_memory_file дублирует файловые операции sandbox.
Вариант подразумевает чтение on-demand через существующий load_storage_file_content, а запись — через новый write_memory_file. При этом для навигации предполагается использовать prompt hint с фиксированными путями.
Главное преимущество способа — необходимость создания только одного инструмента и переиспользование существующего load_storage_file_content.
Но минусов больше:
те же итерации на загрузку;
write_memory_file всё ещё дублирует sandbox;
нет навигации по файлам проекта (только фиксированные пути).
В этом случае все системные memory-файлы инжектятся в system prompt через managed pre-agent hook, а агент получает контекст с первого сообщения, то есть не требуется дополнительных итераций на загрузку. Запись осуществляется через стандартные файловые операции sandbox.
Такой подход имеет некоторые недостатки. Например:
все файлы в system prompt — расход ~2–5K токенов;
USER.md [12] до 2000 chars увеличивает prompt;
валидация размеров при sandbox→S3 sync, а не на уровне инструмента;
зависимость от sandbox для записи.
Но преимущества более весомые:
0 итераций LLM на загрузку памяти. Благодаря этому можно экономить около 1.5–6K токенов на запрос при 3 on-demand файлах, и время (каждая итерация — это ~1–3 секунды latency LLM). При стоимости итерации около 500–2000 токенов это существенная экономия.
Sandbox — уже существующий механизм. Можно не создавать параллельный путь поверх файловой системы. Вместо этого реализуется последовательность sandbox → файловая система → LLM, без надстройки.
Устранён риск, что агент не загрузит память. Hook гарантирует инъекцию, поэтому агент не может ничего «забыть» или «пропустить».
Меньше кода. Не нужно создавать factory-регистрацию, ToolTypeID, prompt hints для on-demand загрузки.
В результате для своей реализации мы выбрали именно этот подход.
Память — это не только хранение данных, но и логика их сборки в нужный момент. В нашем проекте за этот процесс отвечает механика хуков с четырьмя триггерами:
|
Триггер |
Когда срабатывает |
Что можно делать |
|
pre |
До запуска агентного цикла |
Подгрузить файлы, собрать контекст, установить переменные окружения, проверить права |
|
post |
После ответа агента |
Отправить уведомление, опубликовать результат, дополнить память |
|
tool |
При вызове инструмента |
Залогировать аргументы, валидировать, подменить результат |
|
error |
При ошибке агента |
Отправить алерт, записать трейс, запустить retry-логику |
Хук представляет собой произвольный скрипт (Python, bash). Конфигурация сводится к простому манифесту: ядро предоставляет среду выполнения (runtime), а пользователь пишет логику.
Для работы с памятью ключевую роль играют pre- и post-hook: первый собирает контекст перед запуском (context assembly), второй сохраняет итоги диалога. Однако они способны на большее: например, post-hook может обновлять процедурную память (дописывать правила в AGENTS.md [14]), профильную (уточнять настройки в USER.md [12]) или даже семантическую (корректировать тон в SOUL.md [13]). Более того, по умолчанию система настроена на автозапись только эпизодических событий, так как это самый частый сценарий, но архитектурных ограничений нет. Это универсальная модель: файлы служат хранилищем, а хуки управляют всей логикой.
Для наглядности рассмотрим, как именно pre-agent hook собирает контекст на практике.
Так, типовой платформенный хук представляет собой Python-скрипт, который читает подмонтированные из S3 файлы (/workspace/agent/SOUL.md [13], /workspace/user/USER.md [12]) и формирует единый текст для системного промпта.
Конфигурация описывается простым YAML-манифестом:
# hook.yaml
name: context-assembly
description: Сборка memory-файлов в SystemPromptInjection
command: python3 index.py
Скрипт исполняется внутри песочницы. Его работа строится на нескольких принципах:
Семантические секции, а не дамп файлов. Модель видит размеченные блоки <agent_identity>…</agent_identity>, а не сырые заголовки SOUL.md [13]. Она не знает, что данные пришли из файловой системы.
XML-теги как границы блоков. Для четкого разделения контекста используются теги <agent_identity>, <user_profile>, <tool_notes>, <instructions>.
Graceful degradation (устойчивость к сбоям). Отсутствующие или пустые файлы пропускаются. У только что созданного агента все секции будут пустыми — это штатная ситуация.
Мягкие лимиты с маркером усечения. При превышении размера содержимое файла обрезается. Для SOUL.md [13] и AGENTS.md [14] отсекается конец текста, для логов (append-only) — начало. В контекст добавляется метка […truncated…].
Диагностика в stderr. Информация о том, какие файлы найдены, пропущены или усечены, выводится в stderr. Эти технические подробности не попадают в контекст LLM.
Кастомизация через env CONTEXT_FILES. Список читаемых файлов можно переопределить через переменную окружения, не переписывая сам Python-скрипт.
Теперь для наглядности рассмотрим на конкретном пользовательском кейсе, как связка pre+post hooks реализует эпизодическую память в рамках сценария подготовки дайли-репорта.
Так, всё сводится к следующему алгоритму:
Пользователь просит агента: «Сделай дайли-репорт по вчерашним инцидентам».
Агент собирает данные, формирует отчёт.
После ответа срабатывает post-agent hook, который извлекает из ответа ключевые факты (дата, тема, формат), дописывает в файл /workspace/project/incidents-log.md [19] запись: «2026-07-25 — дайли-репорт по инцидентам, опционально — публикует сам отчёт через Bot API.
При следующем запросе «Сделай дайли-репорт» срабатывает pre-agent hook, который читает incidents-log.md [19] и инжектит в контекст: агент уже знает, что в прошлый раз использовался определенный формат, и переиспользует его. Контекст не нужно собирать заново.

То есть post-hook пишет события в проектную область, pre-hook подхватывает их при следующем запуске. Никакой жёсткой логики в ядре — пользователь сам настраивает хуки под свой сценарий.
Почему так, а не через векторную БД? Причины две:
Эпизодические события — это структурированные записи (дата + что произошло + артефакт), их логичнее хранить как log-файл, а не как embedding.
Пользователь должен видеть этот лог и иметь возможность его почистить. Работа с файлами позволяет это делать, с векторной БД — нет.
В процессе работы с памятью мы сформулировали несколько инсайтов.
Injection лучше on-demand. Агент не должен тратить итерации LLM на то, что система может собрать за него. Контекст с первого сообщения — это и про экономию токенов, и про предсказуемость (модель не может «забыть» загрузить память).
Sandbox лучше специализированных инструментов. Такой подход позволяет не плодить сущности поверх файловой системы — агент пишет память как обычные файлы, инфраструктура разбирается с персистентностью. Меньше кода, меньше точек отказа, единый интерфейс.
Хуки лучше жёсткой логики. Можно не строить отдельную подсистему для каждого типа памяти. Например, вместо этого мы реализовали универсальный механизм: pre-hook собирает контекст перед запуском агента, post-hook дописывает события после ответа. Разделение на семантическую (SOUL.md [13]), процедурную (AGENTS.md [14]) и профильную (USER.md [12]) память — это лишь дефолтная конфигурация хуков, которую можно переписать под любой сценарий компании.
При этом нам удалось выстроить реализацию, которая отвечает на все классические запросы при работе с AI: пользователь видит и контролирует файлы памяти напрямую через админ-панель, агент работает с ними через привычный интерфейс файловых операций в песочнице, изоляция гарантируется на уровне S3-префиксов, а гибкость достигается настройкой pre/post-хуков. И эти возможности многоуровневой памяти уже можно протестировать в VK AI Space [1].
Автор: triumphpc
Источник [20]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/34834
URLs in this post:
[1] VK AI Space: https://tech.vk.ru/ai-space/
[2] представила: https://www.anti-malware.ru/news/2026-07-23-111332/50794
[3] память: http://www.braintools.ru/article/4140
[4] опыт: http://www.braintools.ru/article/6952
[5] MemGPT: https://arxiv.org/abs/2310.08560
[6] LangGraph: https://langchain-ai.github.io/langgraph/concepts/memory/
[7] CrewAI: https://docs.crewai.com/en/concepts/memory
[8] Cline: https://docs.cline.bot/best-practices/memory-bank
[9] Claude Code: https://code.claude.com/docs/en/memory
[10] кратковременной памяти: http://www.braintools.ru/article/9493
[11] CLAUDE.md: http://CLAUDE.md
[12] USER.md: http://USER.md
[13] SOUL.md: http://SOUL.md
[14] AGENTS.md: http://AGENTS.md
[15] TOOLS.md: http://TOOLS.md
[16] логика: http://www.braintools.ru/article/7640
[17] ошибка: http://www.braintools.ru/article/4192
[18] Получить консультацию: https://cloud.vk.ru/data-platform/?utm_source=habr&utm_medium=referral&utm_campaign=vkcloud_article_1074798
[19] incidents-log.md: http://incidents-log.md
[20] Источник: https://habr.com/ru/companies/vktech/articles/1074798/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1074798
Нажмите здесь для печати.