Глава 05 · Долгосрочная память
Память живёт в файлах, не в окне
Контекстное окно — это снимок одного запроса: к следующей сессии модель забывает всё. Долгосрочная память — это файлы на диске и индекс MEMORY.md, который агент сам пополняет и сам выборочно читает.
Почему контекст не годится для памяти
В предыдущей главе мы установили: «память» внутри одного хода агента — это массив messages. Он живёт в RAM процесса, передаётся в API как полный список и пересобирается на каждый вызов. Но как только сессия закончилась — этот массив выбрасывается. Следующий запуск агента начинается с пустого окна. AI не имеет памяти между сессиями. Совсем. Это формулировка Олега Беловы из «Красной книги»: каждая новая сессия — новый процесс, который ничего не знает о предыдущих, и это фундаментальный факт, определяющий всю архитектуру.
Anthropic в документации к memory tool описывает это в терминах операций: «agents store what they learn in memory and pull it back on demand. This keeps the active context focused on what's currently relevant, critical for long-running workflows where loading everything at once would overwhelm the context window». Перевод: контекстное окно — это рабочая память, дешёвая по доступу, но летучая. Долгосрочная память — это внеконтекстный носитель, к которому модель обращается через инструменты, как программа обращается к диску. И этот носитель — обычные файлы.
Два процесса: кто пишет, кто читает
Хорошая ментальная модель — две главы Олега подряд, «Two-process model» и «Shared state and files». Человек и AI рассматриваются как два сопроцессора с радикально разной архитектурой: человек — persistent memory, AI — большая, но летучая. Слабость одного — сила другого. Канал между ними — файлы в репозитории: «Files — единственное пересечение долгосрочной памяти. Всё в голове человека невидимо для AI. Всё, что AI понял в сессии, исчезнет. Только файлы переживают обоих».
Внутри самого агента эта же идея воспроизводится в claude-cli как два разных процесса, оба программных. Fast loop — быстрый агентский цикл, синхронный: тот самый while из главы 2, который отвечает пользователю в реальном времени. Он только читает memdir: подгружает MEMORY.md в системный промпт и выборочно вытаскивает релевантные файлы под текущий запрос. Slow extractor — фоновый sub-agent, который запускается fire-and-forget в конце хода главного цикла. Это единственный, кто пишет в memdir: создаёт новые файлы, правит существующие, обновляет индекс. У него отдельный токен-бюджет, отдельный модель-вызов и жёсткое ограничение тулов: Edit/Write разрешены только внутри memdir.
Разделение не косметическое. Если бы главный цикл писал в память во время ответа пользователю, было бы две проблемы: задержка (запись — это ещё один turn модели) и риск повредить контекст ответа (модель «думала» бы про две задачи сразу). Вынесение записи в отдельный фоновый процесс решает обе и даёт надёжный point of truth: главный агент видит только то, что extractor успел записать до этой сессии.
Memdir: файловая структура памяти
Конкретная форма этой памяти у Anthropic называется memdir — директория /memories с markdown-файлами и YAML-фронтматтером. Anthropic формализовал контракт memory tool: client-side инструмент с шестью командами, через которые модель работает с этой директорией.
view— посмотреть содержимое директории или конкретного файла (опционально по диапазону строк);create— создать новый файл (ошибка, если файл уже есть);str_replace— заменить уникальный фрагмент текста в файле;insert— вставить строку в указанную позицию;delete— удалить файл или директорию рекурсивно;rename— переименовать или переместить файл/директорию.
Важный нюанс: memory tool — это client-side инструмент. Anthropic не хранит память за вас: «You control where and how the data is stored through your own infrastructure». Модель только выпускает tool-use-блоки с командами, а реализация на стороне приложения должна валидировать пути (обязательный префикс /memories, защита от path traversal), писать на диск (или в S3, БД, что угодно) и возвращать стандартизованные строки результата. Это та же модель, что и у любого инструмента: модель «просит», harness исполняет.
Системный промпт, который Anthropic автоматически вставляет при включении memory tool, выглядит так: ALWAYS VIEW YOUR MEMORY DIRECTORY BEFORE DOING ANYTHING ELSE. Дальше следует короткий «memory protocol»: сначала view, потом работа, по ходу записывать прогресс и решения, «assume interruption» — контекст может быть сброшен в любой момент, всё несохранённое в memdir будет потеряно.
Четыре типа памяти
Anthropic описывает форму контракта, но не таксономию того, что туда писать. claude-cli и Олег закрывают эту дыру одинаковой четырёхэлементной схемой, закреплённой в коде как const-кортеж MemoryType = 'user' | 'feedback' | 'project' | 'reference'. Неизвестные типы молча отбрасываются — таксономия не разрастается случайно.
user — кто пользователь и как с ним работать. Роль, специализация, предпочтения по стилю. Пример: «SRE в финтехе, предпочитает CLI и markdown, просит писать на „вы“». Пишется один раз и редко меняется.
feedback — корректировки подхода, накопленные через диалог. «Не править снапшоты без запроса», «перед коммитом запускать тесты», «не делать bundling — одна правка, один коммит». Каждая запись — это предотвращённый регресс в будущей сессии; этот тип нельзя восстановить из кода, он живёт только в памяти.
project — контекст текущей работы: фаза, цели, дедлайны, блокеры. «Q2 запуск edge-кеша, фаза P2 verification, дедлайн 30 июня, блокеры — issue #12 и #15». Это аналог WAL из «Красной книги»: компас, не карта, короткий горизонт.
reference — указатели на внешние ресурсы: Linear, Grafana, Slack-каналы, runbook-ссылки. Файл не содержит данных, он содержит пути к данным.
MEMORY.md как ангар, не склад
Если у вас сто записей в memdir, и каждый запрос будет тащить их все в системный промпт, контекст съест эта самая память — и места для собственно задачи не останется. Поэтому есть второй уровень иерархии: MEMORY.md — индекс, который всегда в контексте, и тело каждой записи — в отдельном файле, который подтягивается по требованию.
claude-cli держит MEMORY.md в системном промпте с двумя жёсткими лимитами: не более 200 строк и не более 25 KB. Каждая запись — одна строка вида имя_файла.md: однострочное описание, не более ~150 символов. Если индекс пытается перерасти лимит, claude-cli не молча режет, а вставляет в промпт явное уведомление: «индекс был обрезан, потому что превысил лимит X, перепиши его компактнее». Тихое усечение — анти-паттерн: модель бы продолжала писать в индекс, не понимая, что половина уже не видна.
Главное правило, которое emit в системный промпт буквально:
«Never write memory content directly into MEMORY.md».
Сохранение — два шага: сначала создать тематический файл с фронтматтером (имя, описание, тип), потом добавить в индекс однострочный указатель. Это и есть «индекс, а не память»: индекс отвечает на вопрос «есть ли у меня что-то на эту тему», а не «что именно я знаю».
Recall: не всё тащим в контекст
«Нужна ли память X для текущего запроса» — отдельная задача, и она не решается «загрузить всё». В claude-cli это сделано так: на каждом ходу findRelevantMemories сканирует заголовки файлов memdir (только фронтматтер, не тело), формирует одностроковый манифест и отдаёт его маленькой модели через sideQuery: «выбери до пяти файлов, релевантных текущему запросу». Маленькая модель возвращает JSON-список имён, и harness подгружает тела этих файлов как user-сообщения.
Бюджет жёсткий: не больше пяти файлов плюс постоянно загруженный индекс — так стоимость recall не зависит от размера памяти (до потолка в 200 файлов). Уже использованные в этом ходу файлы исключаются. Каждый подтянутый файл сопровождается system-reminder с возрастом: «эта память — снимок N дней назад, не live state, проверь по коду перед утверждением». Устаревшую память не прячут, её показывают с пометкой. Симметрично работает запись: slow extractor не пишет, если ход — это subagent, если главный агент сам что-то записал с прошлого extract, или если сработал throttle.
Manus-приём: filesystem-as-context vs typed memory
В блоге Manus есть параллельный приём, который легко спутать с memdir, но это другая практика. «File system as context»: вместо аггресивного сжатия больших наблюдений (вёрстка страницы, PDF, длинный лог) агент сохраняет их в файлы и держит в контексте пути, а не содержимое. «URLs preserve web page access without maintaining content; document paths enable access without retained contents» — контекст сжимается обратимо, без потерь.
Разница с типизированной памятью принципиальная. Manus folder — временный кеш для крупных артефактов в рамках одной задачи: имена не следуют таксономии, тип не указывается, индекса нет. Это разгрузка контекстного окна, а не долгосрочное знание. Memdir Anthropic / claude-cli — durable typed knowledge, переживающий сессию: жёсткие четыре типа, обязательный индекс, два-шаговое сохранение, отдельный фоновой процесс для записи.
Правило: артефакт нужен только до конца задачи — это filesystem-as-context (Manus). Артефакт описывает пользователя, его правила, проекты или внешние ссылки и должен пережить сессию — это typed memory. Архитектурно это разные слои; в зрелом harness они оба есть, но реализованы по-разному.
MEMORY.md — это индекс, а не память. Никогда не пишите содержимое памяти прямо в MEMORY.md. Сохранение — два шага: сначала создайте тематический файл с фронтматтером, затем добавьте однострочный указатель в индекс.
Источники главы
- [olg] Два процесса, одна задача — Красная книга AI-инженера, гл. 1 (модель сопроцессоров, IPC через файлы, persistent vs volatile memory)
- [olg] Shared state: файлы как IPC — Красная книга, гл. 2 (три уровня IPC, WAL, протокол конфликтов)
- [olg] Архитектура памяти — Красная книга, гл. 3 (иерархия памяти, WAL как checkpoint, решения vs факты)
- [ant] Memory tool — Anthropic Docs (контракт view/create/str_replace/insert/delete/rename, client-side хранение, security)
- [cli] Конспект: Memory & Recall (четыре типа памяти, MEMORY.md-индекс, relevance recall, фоновой extractor)
- [man] Context Engineering for AI Agents — Manus (file system as context, recitation через todo.md)