Память агента: что стоит хранить, а что вредно
В прошлой главе агент получил руки: инструменты, протоколы, интеграции. Но между вчерашней сессией и сегодняшней он не помнит ничего. Совсем ничего. И вот здесь начинается самая распространённая инженерная ошибка в работе с агентами — попытка «дать агенту память», не разобравшись, что этим словом называют шесть разных механизмов с разной ценой, разным временем жизни и разной степенью опасности.
Тезис главы, который стоит проговорить в первом же абзаце, потому что из него выводится всё остальное:
У агента нет памяти. Есть текстовые файлы и есть политика, по которой их содержимое попадает в окно контекста. «Агент запомнил» всегда означает «кто-то записал, и кто-то другой прочитал обратно».
Это не педантизм. Из этой формулировки немедленно следуют три практических вывода: у памяти есть цена в токенах на каждом ходу; у памяти есть владелец, который отвечает за истинность записи; и ошибка в записи живёт дольше, чем ошибка в сессии, потому что переживает перезапуск.
Шесть вещей, которые называют одним словом
Начнём с того, чтобы развести понятия. Когда инженер, продавец и автор блог-поста произносят «память агента», они говорят о разных механизмах.
Разница между ветками этого дерева — не в терминологии, а в том, кто отвечает за истинность записи и во сколько обходится её чтение.
| Механизм | Кто пишет | Когда попадает в окно | Цена | Кто ловит ложь |
|---|---|---|---|---|
| Веса модели | провайдер при обучении | всегда, неявно | входит в цену токена | никто, поэтому всё из весов — гипотеза |
| Окно контекста | харнесс по ходу сессии | по определению уже там | растёт квадратично по ходам | вы, пока не случилась компакция |
| Проектный контракт | вы, вручную | каждый ход, целиком | размер × число ходов | вы на ревью контракта |
| Постоянные заметки | вы или агент | только когда прочитаны | вызов поиска плюс объём найденного | вы на ревью заметки |
| Артефакты репозитория | вы или агент | когда агент их открыл | как обычное чтение файла | CI: ложное утверждение падает на прогоне |
| Автопамять инструмента | харнесс, часто молча | по правилам харнесса, непрозрачно | зависит от реализации | никто, если запись не видна в git |
Последняя колонка — главная. Единственная строка, где ложь ловится машиной, — артефакты репозитория. Всё остальное держится на человеческом внимании, а внимание человека — самый дорогой ресурс в работе с агентом (об этом отдельная глава про цену).
Единственный механизм: текст, попавший в окно
Полезно один раз проговорить механику до конца, чтобы не строить мифологию. Модель не имеет состояния между запросами: каждый ход агента — это новый HTTP-запрос, в который харнесс кладёт весь транскрипт заново. Никакого «внутреннего блокнота», переживающего запрос, у модели нет. Всё, что выглядит как память, реализовано одним из двух способов:
- Всегда включать. Текст лежит в стабильном префиксе запроса — системном промпте или проектном контракте. Агент физически не может его не увидеть.
- Доставать по запросу. Текст лежит в файле, и агент читает его инструментом, если догадается искать и если поиск сработает.
У обеих политик своя цена, и обе цены реальные:
- Политика «всегда включать» платится размером, умноженным на число ходов. Контракт в 2500 токенов за сорокаходовую сессию — это 100 000 входных токенов, потраченных только на контракт. Кэширование префикса снижает денежную часть этого счёта (документация Anthropic по кэшированию промпта), но не отменяет второй расход: место в окне и конкуренцию за внимание модели. Механику этой конкуренции мы разобрали в главе о контексте.
- Политика «доставать по запросу» платится вызовом поиска и риском не найти. Заметка, которую агент не нашёл, эквивалентна отсутствующей заметке — с той разницей, что за её написание и поддержку вы уже заплатили.
Промежуточных вариантов немного: подгрузка по триггеру (файл читается, когда агент трогает определённый каталог), иерархические контракты (общий плюс локальный для подкаталога), сжатые сводки прошлых сессий. Все они — комбинации тех же двух политик, и все наследуют обе цены.
Дообучение — не память
Отдельно стоит закрыть частый вопрос: «а нельзя ли дообучить модель на нашей кодовой базе, чтобы она помнила?» Технически можно, практически это плохой канал для фактов. Нет отзыва: неверный факт в заметке правится одной строкой диффа, а впитанный при дообучении убирается только новым циклом обучения. Нет адреса: модель, обученная на вашем коде, не скажет «файл такой-то, строка такая-то», она выдаст правдоподобную реконструкцию — а вам нужен адрес. Устаревание: кодовая база меняется каждый день, цикл дообучения — нет.
Дообучение решает другие задачи — формат вывода, стиль, узкую доменную терминологию. Разбор компромиссов — в главе про тонкую настройку трека инженерии ИИ. Для памяти о фактах системы это дорогой и неуправляемый инструмент.
Куда писать: приоритет мест хранения
Прежде чем обсуждать, что писать, надо договориться, куда. Здесь есть строгий порядок предпочтения, и он важнее любого списка правил: память — последнее место, куда стоит писать факт.
Если знание можно выразить исполняемым артефактом, оно должно жить там:
- Инвариант «этот метод нельзя звать без транзакции» — это тест, а лучше тип или проверка на старте.
- «У нас в проекте всегда двойные кавычки» — это конфиг форматтера, а не строка в контракте.
- «Мы отказались от библиотеки X из-за лицензии» — это ADR в репозитории, а не заметка.
- «Собирается командой
make build» — этоMakefile, а в контракте достаточно строки «команды смотри в Makefile».
Почему такой порядок: исполняемый артефакт проверяется машиной. Ложный тест падает, ложный тип не компилируется, ложная схема ломает миграцию. Ложная заметка не делает ничего — она просто лежит и тихо убеждает всех, кто её читает, включая агента. Записанное в память утверждение получает статус факта, не проходя ни одной проверки: это и есть главный риск всей темы.
Отсюда рабочее определение: постоянная заметка — это то, что не удалось сделать исполняемым. Хорошие кандидаты остаются, их немного, и о них следующий раздел.
Путь факта от наблюдения до применения
Между «агент выяснил» и «агент этим воспользовался в следующий вторник» лежит цепочка переходов, и на каждом теряется часть.
вывод команды, чтение файла"] --> B{"Это переживёт
текущую задачу?"} B -->|нет| Z1["Оставить в сессии.
Не писать никуда"] B -->|да| C{"Можно выразить
тестом, типом, конфигом?"} C -->|да| D["Артефакт репозитория.
Проверяет CI"] C -->|нет| E{"Выводится из репозитория
одной командой?"} E -->|да| F["Записать команду,
а не её результат"] E -->|нет| G{"Сработал один из
пяти триггеров?"} G -->|нет| Z2["Не писать.
Молчание дешевле мусора"] G -->|да| H["Заметка: утверждение,
доказательство, дата"] H --> I{"Агент найдёт её,
когда понадобится?"} I -->|нет| J["Заметка есть,
пользы нет"] I -->|да| L{"Не устарела ли
с даты проверки?"} L -->|устарела| M["Хуже, чем ничего:
ложь с видом факта"] L -->|актуальна| N["Экономия хода
или блокировка ошибки"]
Обратите внимание, сколько ветвей ведёт не к пользе. Это честная картина: подавляющее большинство наблюдений агента не должны становиться постоянными записями, а из тех, что стали, часть не найдётся, а часть протухнет. Именно поэтому дисциплина «что не писать» важнее дисциплины «как писать».
Что стоит хранить: пять триггеров
Дальше я опираюсь на пакет правил, который лежит в этом же репозитории портала: products/workbench/templates/memory/. Это наш собственный рабочий стандарт, а не отраслевой — так к нему и относитесь. Ценность его в том, что каждое правило сформулировано вместе с наблюдаемым нарушением: правило, которое нельзя нарушить проверяемым образом, из пакета выброшено. Идентификаторы (MEM-10, RSN-02) нужны, чтобы на ревью ссылаться на номер, а не пересказывать прозу. Запись в постоянную память оправдана ровно в пяти случаях, и все они — события, ни одного расписания.
1. Дорого добытый неочевидный факт (MEM-10). Агент установил что-то про систему, на что ушло больше одного вызова инструмента, и чего нет нигде в подконтрольных вам файлах. Пример: «этот сервис отвечает 200 на невалидный вход, потому что валидация отключена флагом в конфиге деплоя, а не в коде». Записывается утверждение и доказательство: путь, строка, команда.
2. Опровергнутое убеждение (MEM-11). Самая ценная категория и единственная обязательная. Каждый раз, когда агент (или вы) утверждал X, а потом наблюдение показало не-X, это повод для записи. Не «мы починили баг», а «мы считали, что причина в кэше; наблюдение показало, что причина в порядке миграций; вот команда, которая это показала».
Почему именно опровержения важнее всего: положительное знание агент часто может добыть заново, потратив ход. Отрицательное знание — «этот путь не работает по такой-то причине» — он заново не добудет, он просто пойдёт по нему снова. Записанное опровержение — единственный механизм, который прерывает цикл повторения одной и той же ошибки в разных сессиях.
3. Решение с дорогим откатом (MEM-12). Выбор, отменить который дороже, чем сделать работу заново: формат хранения, схема идентификаторов, граница модуля. Основной артефакт здесь — ADR в репозитории, который владеет решением; заметка в памяти — указатель на него, а не копия. Копия разъедется с оригиналом, и вы не узнаете, какая версия правильная.
4. Межпроектный инвариант (MEM-13). То, что выяснено в проекте A и ограничивает проект B: особенность общего внешнего API, поведение корпоративного прокси, ограничение платформы деплоя. Это единственный класс знания, у которого нет «домашнего» репозитория, и главная причина, по которой хранилище памяти вообще существует.
5. Владелец сказал запомнить (MEM-14). Явная инструкция человека. Записывается дословно и с датой — потому что через три месяца формулировка «всегда делай так» без контекста и даты сама превращается в проблему.
Всё, что не попало в эти пять пунктов, не записывается. Не «записывается на всякий случай в отдельную папку» — не записывается.
Что хранить вредно: шесть запретов
Здесь важно понимать механизм вреда. Лишняя заметка — это не просто ноль пользы: она облагает налогом контекст, если лежит в контракте; шумит в поиске, если лежит в хранилище (агент находит десять нерелевантных записей и не находит одну нужную); и главное — становится источником ложной уверенности. Записанное утверждение агент склонен принимать как установленный факт и подгонять под него наблюдения, вместо того чтобы проверить.
Запрет 1. Волатильное состояние (MEM-20). Всё, чья истинность меняется без правки самой записи: имя текущей ветки, номер версии зависимости, статус тикета, кто дежурит, зелёная ли сборка. Проверка простая: если на вопрос отвечает одна команда — ответ живёт в выводе этой команды, а не в заметке. Вред конкретный: заметка «мы на Postgres 14» переживает апгрейд до 16, и агент честно пишет код под ограничения четырнадцатой версии, ссылаясь на вашу же память.
Запрет 2. Секреты и машинно-локальные детали (MEM-21). Токены, ключи, персональные данные, абсолютные пути с чужой машины, внутренние хостнеймы и адреса. Файл памяти — это обычный текстовый файл, который уезжает в модель целиком и часто попадает в git. Он подчиняется тем же правилам, что и остальной репозиторий: управление секретами не делает исключения для «это же просто заметки для агента». Сканер секретов должен ходить по каталогу памяти так же, как по исходникам.
Запрет 3. Всё, что выводится из репозитория (MEM-22). Если ответ даёт git log, исходник, тест или сгенерированная документация — заметка становится дубликатом с худшим путём обновления. Пересказ тела функции в памяти устареет на первом же рефакторинге, и обнаружите вы это тогда, когда агент напишет вызов по устаревшей сигнатуре. Ссылайтесь на хеш коммита или путь, не копируйте содержимое.
Запрет 4. Журналы сессий (MEM-23). «Что я сделал сегодня», списки задач, состояние передачи между сессиями. Это рабочие заметки, они живут в задаче, ветке или трекере и умирают вместе с ними. В постоянной памяти они через месяц дают археологический слой, по которому невозможно понять, что из этого ещё верно.
Запрет 5. Непроверенные догадки (MEM-24). Утверждение, которое агент не проверил, не становится постоянной записью. Если идею жалко терять, она пишется с явным маркером «не проверено» и с указанием проверки, которая её решит. Без маркера гипотеза через неделю неотличима от факта — и это ровно тот механизм, которым агент отравляет собственную память.
Запрет 6. Пересказ чужой документации. Копия куска доков библиотеки в вашей памяти — это форк документации, который никто не будет мержить. Через два релиза он расходится с оригиналом, а агент верит форку, потому что он «свой». Ссылка с датой проверки лучше копии.
Отравленная память: главный отказ этой темы
Все прочие проблемы памяти — про эффективность. Эта — про корректность.
Три вещи стоит вынести из этой схемы.
Ошибка в памяти самовоспроизводится. В сессии 2 агент не соврал — он честно процитировал вашу же запись. Ложь была внесена в сессии 1 и прошла ревью, потому что выглядела как безобидная строка документации. Именно поэтому диф каталога памяти читается на ревью так же внимательно, как диф кода: там нет компилятора, который поймает за вас.
Защита — не запрет, а формат. Требование «у каждого утверждения есть адрес доказательства и дата проверки» ловит эту ситуацию на входе: заметку «повторы включены» без указания файла и строки просто нельзя написать по правилам, а с указанием — рецензент видит, что вывод не следует из источника.
Удалять нельзя, помечать нужно. В сессии 3 старая запись не стирается, а получает статус «опровергнута» и ссылку на опровержение (MEM-32). Удаление уничтожает единственное, что защищает от повторения ошибки: свидетельство о том, что этот вывод уже делали и он был неверен.
Отдельная разновидность того же отказа — внесение записи извне. Агент прочитал README зависимости, комментарий в issue или содержимое веб-страницы, и текст оттуда содержал инструкцию, а агент положил её в память. С этого момента чужой текст участвует в каждой вашей сессии. Отраслевые каталоги угроз для агентных систем выделяют отравление памяти в отдельный класс — см. материалы OWASP GenAI Security Project по агентным угрозам и по Top 10 для LLM-приложений. Практическое правило: в память не попадает текст, источник которого вы не контролируете, — только ваша формулировка со ссылкой на источник. Подробнее об инъекциях — в главе про безопасность.
Формат записи: что делает заметку проверяемой
Формат — это не бюрократия, а способ сделать нарушение видимым. Минимально достаточный набор полей:
---
id: 202607161042
verified-on: 2026-07-16
confidence: verified
status: active
---
# Отключение валидации живёт в конфиге деплоя, а не в коде сервиса
## Утверждение
Сервис orders принимает невалидный payload и отвечает 200, потому что
middleware валидации выключается переменной окружения, которая выставлена
в манифесте деплоя. В коде сервиса признаков этого нет.
## Доказательство
- `services/orders/app/middleware.py:41` — условие включения middleware
- `deploy/prod/orders.yaml:63` — переменная выставлена в значение off
- `curl -s -o /dev/null -w '%{http_code}' -XPOST .../orders -d '{}'` -> 200
## Связи
- ограничивает [[Контракт API orders обещает 422 на невалидный вход]]
- противоречит [[Валидация включена во всех сервисах платформы]]
Что здесь делает каждое поле:
- Идентификатор назначается один раз и не меняется никогда. Он не кодирует тему: тема меняется, ссылки на идентификатор — нет.
- Заголовок — утверждение, а не тема. Проверка на месте: если перед заголовком нельзя поставить «неверно, что» и получить осмысленное предложение, это не заголовок заметки, а название папки. «Валидация» — тема; «отключение валидации живёт в конфиге деплоя» — утверждение, которое можно оспорить. И ровно одно на заметку: если описание защищает два тезиса, которые принимаются по отдельности, это две заметки.
- Доказательство — адрес, а не пересказ. Путь со строкой, команда с выводом или URL с датой обращения. Всё остальное — воспоминание модели, то есть непроверенное утверждение.
- Дата проверки. Знание о внешней системе имеет срок годности. Двухлетняя заметка про чужой API — гипотеза, как бы уверенно она ни была сформулирована.
- Связи, включая противоречия. Хранилище, где все записи согласуются друг с другом, — эхо-камера. Явная ссылка «противоречит» — то место, где конфликт в понимании системы становится видимым.
Процедура допуска в псевдокоде
Решение «писать или не писать» полезно свести к процедуре, которую можно поместить в контракт агента дословно.
процедура допустить_в_память(факт):
если факт содержит секрет или локальный путь:
отказ # MEM-21
если истинность факта меняется без правки записи:
отказ, записать команду-источник вместо факта # MEM-20
если факт выводится из репозитория одной командой:
отказ, записать адрес вместо содержимого # MEM-22
если факт выразим тестом, типом или конфигом:
отказ, создать артефакт # приоритет мест
если у факта нет адреса доказательства:
отказ либо пометка «не проверено» # MEM-24
если сработал триггер из {MEM-10..MEM-14}:
допустить, но не более N записей за сессию # MEM-60
иначе:
отказ
Ограничение «не более N за сессию» — не эстетика. Заметки дёшевы в создании и дороги в поддержке: писать их агент умеет быстрее человека, а сопровождать получившееся всё равно человеку. Разумное значение по умолчанию — три; выбирать его надо по своей ситуации и проверять вторым правилом: если за пару недель новых заметок больше, чем принятых изменений в коде, ведение заметок стало основным продуктом и пора не писать, а чистить.
Аудит хранилища скриптом
Проверять правила глазами не нужно: часть из них механическая.
"""Аудит хранилища заметок: записи без доказательств, без даты и протухшие.
Скрипт видит форму, а не смысл: адрес, который ничего не доказывает, он пропустит."""
import datetime as dt
import pathlib
import re
STORE = pathlib.Path("memory")
STALE_DAYS = 180 # срок годности знания о внешних системах
EVIDENCE = re.compile(r"[\w./-]+:\d+|\$ |http[s]?://") # путь:строка, команда, URL
def audit(store: pathlib.Path, today: dt.date) -> list[str]:
"""Список нарушений формы. Время O(N * L): N файлов длиной L.
Память O(N): храним только заголовки, чтобы ловить дубли."""
problems: list[str] = []
seen: dict[str, pathlib.Path] = {}
for note in sorted(store.glob("*.md")):
text = note.read_text(encoding="utf-8")
found_title = re.search(r"^#\s+(.+)$", text, re.MULTILINE)
title = found_title.group(1).strip() if found_title else ""
if len(title.split()) < 4: # тема вместо утверждения
problems.append(f"{note}: заголовок не похож на утверждение")
if title in seen:
problems.append(f"{note}: дубликат заголовка из {seen[title]}")
seen[title] = note
if not EVIDENCE.search(text) and "confidence: unchecked" not in text:
problems.append(f"{note}: нет адреса доказательства и нет пометки unchecked")
found = re.search(r"^verified-on:\s*(\d{4}-\d{2}-\d{2})", text, re.MULTILINE)
if not found:
problems.append(f"{note}: нет даты проверки")
elif (age := (today - dt.date.fromisoformat(found.group(1))).days) > STALE_DAYS:
problems.append(f"{note}: проверено {age} дней назад, нужна перепроверка")
return problems
if __name__ == "__main__":
for line in audit(STORE, dt.date.today()):
print(line)
Сложность линейна по объёму хранилища, так что скрипт спокойно живёт в пре-коммит-хуке. Важнее другое: он проверяет форму, а не содержание. Заметка с адресом, из которого утверждение не следует, пройдёт аудит и не пройдёт ревью — и это правильное разделение труда.
Проектный контракт: память, которую читают всегда
CLAUDE.md, AGENTS.md, правила редактора — это часть памяти с самой высокой ценой и самым надёжным доступом. Формат и имена файлов зависят от инструмента и от версии: смотрите документацию своего харнесса, например память Claude Code, правила Cursor или межинструментальную конвенцию AGENTS.md. Принципы при этом общие.
Что в контракт входит. Правила, которые нельзя вывести из репозитория взглядом, и цена нарушения которых высока:
- границы: чего агент не делает без вопроса (миграции, зависимости, публичные интерфейсы);
- протокол отчётности: что считается доказательством выполнения; указатели: где команды, где ADR, где хранилище памяти;
- локальные соглашения, которые противоречат общепринятым, — именно потому, что модель по умолчанию сделает общепринятое.
Что в контракт не входит. Всё, что противоречит запретам выше, плюс два специфических пункта. Первый — пересказ структуры проекта: дерево каталогов устаревает быстрее, чем вы его дописываете, а получить его агент может одной командой. Второй — длинные объяснения «почему»: контракт это набор правил, а не эссе, обоснования живут в ADR, на которые контракт ссылается.
Проверка контракта. Раз в несколько недель проходите по нему построчно с одним вопросом: как я замечу, что этот пункт стал ложным? Если ответа нет, пункт удаляется. Контракт, в котором половина утверждений не соответствует репозиторию, хуже пустого: он учит агента (и людей) не доверять контракту.
Размер контракта — инженерное решение с измеримой ценой. Посчитайте его токенизатором своего провайдера, умножьте на типичное число ходов в сессии, посмотрите на результат. Абсолютных рекомендаций «не больше N строк» я давать не буду — они зависят от модели, задачи и цены токена у вас; но само число вы должны знать. И отдельно: правка контракта в середине сессии сбрасывает кэш префикса, то есть следующий ход оплачивается по полной ставке.
Жизненный цикл записи
Заметка — не константа. У неё есть состояния, и переходы между ними — то место, где хранилище либо остаётся полезным, либо превращается в свалку.
Два перехода здесь неочевидны.
«Проверено» → «под подозрением» происходит по времени, а не по событию. Никто не приходит сообщить, что ваша заметка про поведение чужого API устарела. Поэтому дата проверки — обязательное поле, а не украшение: она позволяет скриптом отделить знание от воспоминания.
Из хранилища ничего не уходит. Опровергнутая запись — самый ценный вид записи, потому что она документирует ошибку вместе с тем, как её обнаружили. Обратная сторона честная: хранилище растёт монотонно и рано или поздно начинает шуметь в поиске. Лечится это не удалением, а слиянием дублей в более старый идентификатор и пометкой новых как заменённых — и запускается по триггеру («поиск по частому термину даёт слишком много мусора»), а не по календарю.
Где хранить: карта решений
Сведём выбор места хранения к двум осям: как часто знание меняется и во сколько обходится его забыть.
Верхний правый квадрант — самая частая ошибка. Знание там важное, и рука тянется записать; но оно меняется без вашего участия, и запись становится ложью раньше, чем вы к ней вернётесь. Правильный ответ — не запись, а команда: агент должен спросить систему в момент, когда ответ нужен.
Автопамять инструментов: удобство против прозрачности
Многие харнессы умеют записывать что-то самостоятельно: по команде «запомни это» или по собственной эвристике. Конкретные механизмы меняются от версии к версии — проверяйте документацию своей. Обсудить стоит не механизм, а свойства.
Даёт автопамять одно: дисциплину не надо поддерживать руками. Забирает четыре вещи. Прозрачность — вы не всегда видите, что именно записано и по какому поводу, а диф, которого нет, невозможно отревьюить. Ревью — если запись не идёт через git, у команды нет механизма поймать ошибку до того, как она сработает. Границу с секретами — автоматическая запись не отличает важный факт от токена, случайно оказавшегося в выводе команды. Переносимость — память в формате конкретного инструмента не переезжает вместе с вами.
Практический компромисс: автопамять допустима как личный кэш одного разработчика; всё, что должно влиять на работу команды, живёт в файле в репозитории и проходит ревью. Простой критерий: если запись не видна в git diff, это не память проекта.
Поиск по памяти: когда grep, когда что-то сложнее
Отдельный соблазн — построить над заметками векторный поиск. Прежде чем строить, посчитайте. Хранилище из двухсот заметок полностью обыскивается ripgrep за миллисекунды, и этот поиск прозрачен: видно, по какому слову нашлось. Векторный поиск начинает окупаться, когда записей тысячи и формулировки расходятся настолько, что точное слово не работает. Цена перехода честная: вы добавляете инфраструктуру (индекс, эмбеддинги, их пересборку) и новый класс отказов — выдачу, которая выглядит релевантной, но не является. Механика и компромиссы разобраны в главе про RAG и в главе про векторные базы соответствующих треков.
Правило принятия решения: пока полнотекстовый поиск по каталогу отвечает быстро и находит нужное, дополнительный слой поиска — это лишняя система с собственными отказами. Начинайте с rg и переходите дальше по измеренной необходимости, а не по интересности задачи.
Академические работы про архитектуры памяти агентов читать полезно — MemGPT (Packer et al., 2023) про управление уровнями памяти по аналогии с ОС, Generative Agents (Park et al., 2023) про поток наблюдений с рефлексией и оценкой значимости. Но переносить их на инженерную работу с кодовой базой надо с осторожностью: обе поставлены на задачах, не похожих на «поддерживать сервис в проде», и обе оценивались в своих постановках. Для рабочего репозитория дисциплина записи даёт больше, чем архитектура хранилища.
Где агент врёт про память
Типология отказов, специфичных именно для памяти. Общая типология — в главе про отказы; здесь пять локальных.
1. Цитата, которой нет. «Как мы решили ранее, повторные запросы не нужны». Проверка: попросите идентификатор заметки и дату. Реконструкция по смыслу отличается от цитаты тем, что у неё нет адреса.
2. Искажение при цитировании. Заметка говорит «валидация отключена на эндпоинте A», агент цитирует как «валидация отключена». Ловится сверкой с оригиналом — одна команда rg по идентификатору.
3. Отчёт о записи вместо записи. «Я сохранил это в память» — это заявленное действие, а не результат. Единственная проверка — git status или git diff по каталогу памяти. Правило из нашего пакета формулируется жёстко: принятое, поставленное в очередь и начатое — не результаты (RSN-13).
4. Универсальный отрицательный ответ. «В памяти про это ничего нет». Без указания, чем искали и что этот поиск не покрывает, утверждение бессодержательно (RSN-06): grep по точному слову не находит синонимов, а поиск в одном каталоге ничего не говорит о другом.
5. Цитата без даты. Заметка годичной давности про чужой API процитирована как текущий факт. Требование простое: цитируешь память — приноси дату проверки (MEM-52).
Проверочный набор команд, который закрывает почти всё перечисленное:
# 1. Что агент реально записал за сессию — а не что он об этом сказал.
git status --short memory/ && git diff -- memory/
# 2. Существует ли процитированная заметка и та ли у неё формулировка.
rg -n "202607161042" memory/
# 3. Не протухла ли запись: дата проверки против даты правки самого кода.
rg -n "^verified-on:" memory/ | sort -t: -k3
git log -1 --format=%ad -- services/orders/app/middleware.py
Ни одна из этих команд не требует агента. Это принципиально: проверка утверждения о памяти не должна проходить через тот же механизм, который мог соврать. Тема доказательств вместо обещаний развёрнута в главе про проверяемость.
Память в команде
Как только заметки перестают быть личными, появляются вопросы, которых нет у одного разработчика:
- Заметка — часть репозитория и проходит ревью как код. Отдельный CODEOWNERS на каталог памяти — разумная мера: у записи должен быть человек, отвечающий за её истинность.
- Конфликт двух заметок — это конфликт в понимании системы. Не мержите его молча: явная ссылка «противоречит» превращает скрытое расхождение в обсуждаемый факт.
- Общий контракт и личные дополнения — разные файлы. Командный
AGENTS.mdв git; личные предпочтения — локально, чтобы не влиять на чужие сессии. Уборка в хранилище — по триггеру, а не по расписанию: пришло опровержение — кто-то помечает старую запись, зашумился поиск — кто-то сливает дубли.
Организационная сторона разобрана подробнее в главе про командную работу, а общие правила ревью — в главе про код-ревью и стандарты трека принципов.
Когда память не нужна вовсе
Трезвый раздел, без которого глава была бы рекламой.
Разовая задача. Починить один баг в незнакомом репозитории, куда вы больше не вернётесь, — писать нечего: всё знание уйдёт вместе с задачей, и это нормально. То же с короткими сессиями одного человека: пока задача укладывается в одну сессию и не повторяется, стоимость записи не окупается.
Хорошо документированный репозиторий. Если поведение описано тестами, границы — типами, а решения — ADR, отдельное хранилище памяти добавит немного. Это признак здоровья, а не пробел.
Факт добывается дешевле, чем ищется. Если выяснить заново стоит одну команду и десять секунд, а написать и поддерживать заметку — пятнадцать минут вашего времени, заметка окупится только при многократном повторении. Считайте ожидаемое число повторов честно: обычно оно меньше, чем кажется в момент энтузиазма.
И общая оговорка, которую наш собственный пакет правил формулирует прямо: это дисциплина, а не механизм. Контролируемых исследований, показывающих, что ведение связанных заметок улучшает результат работы, нет; за методом стоят одна впечатляющая биография и большое количество самоотчётов. Что защитимо без всяких исследований: записанное с доказательством опровержение не повторяется молча, а утверждение с адресом источника может перепроверить кто-то, кроме автора. Этого достаточно, чтобы дисциплина имела смысл, и недостаточно, чтобы обещать прирост производительности в процентах.
Мини-эксперимент на полчаса
- Посчитайте размер своего контракта токенизатором провайдера. Умножьте на типичное число ходов вашей сессии. Запишите число.
- Пройдите по контракту построчно и разметьте каждый пункт:
проверяемый/выводится из репозитория/волатильный/не знаю, как проверю. - Удалите волатильное и «не знаю». Выводимое замените адресом («команды — в Makefile»).
- Прогоните одну и ту же задачу на старом и новом контракте, в чистых сессиях. Сравните три вещи: число ходов, число ваших правок в ревью, счёт токенов.
Честная оговорка: это не эксперимент с контролем, а санитарная проверка на выборке из одного прогона. Стохастичность модели даст разброс, и одна пара прогонов не доказывает ничего, кроме того, что вы посмотрели на свои числа. Но посмотреть на свои числа — уже больше, чем делает большинство.
Типичные ошибки
- «Дам агенту память» вместо «выберу, что попадёт в окно». Память — это политика отбора текста, а не подключённый сервис.
- Писать в память то, что должно быть тестом. Тест проверяется, заметка — нет.
- Хранить волатильное состояние. Версии, ветки, статусы устаревают без правки записи и превращаются в аргументированную ложь.
- Копировать в память содержимое репозитория. Форк без мержа: разойдётся молча.
- Заметки без адреса доказательства и без даты проверки. Утверждение без пути, команды или ссылки — это воспоминание модели, а не факт; а срок годности знания о внешних системах надо видеть.
- Удалять неверные записи. Вместе с ошибкой удаляется прививка от её повторения.
- Принимать «я записал» на слово. Заявленное действие — не результат; смотрите диф.
- Строить векторный поиск над двумя сотнями файлов. Инфраструктура и новый класс отказов там, где хватает
rg.
Мини-итог
- У агента нет памяти: есть файлы и политика их попадания в контекст. Всё остальное — метафора, которая мешает считать цену.
- Политик две: «всегда в контракте» (платится размером × числом ходов) и «достать по запросу» (платится вызовом поиска и риском не найти). Обе цены реальны.
- Приоритет мест хранения: тест, тип, конфиг, ADR, README — и только потом заметка. Постоянная запись — это то, что не удалось сделать исполняемым.
- Пять поводов писать: дорого добытый факт, опровержение, решение с дорогим откатом, межпроектный инвариант, прямое указание владельца. Опровержения — самая ценная категория.
- Шесть поводов не писать: волатильное состояние, секреты, выводимое из репозитория, журналы сессий, непроверенные догадки, копии чужой документации. Дисциплина «чего не писать» важнее дисциплины «как писать».
- Ложная запись хуже отсутствующей: она проходит без проверки, звучит как факт и переживает перезапуск. Защита — адрес доказательства, дата проверки и ревью дифа каталога памяти.
- Ничего не удаляем: опровергнутое помечается и остаётся, потому что документированная ошибка — единственный механизм против её повторения.
- Проверка утверждений агента о памяти делается вне агента:
git diff,rgпо идентификатору, дата проверки против даты правки кода. А если запись вообще не видна вgit diff, это личный кэш инструмента, а не память проекта.
Источники
products/workbench/templates/memory/— наш пакет правил:MEMORY-PROTOCOL.md(MEM-nn— что писать, чего не писать, как обновлять),AGENT-MEMORY-CONTRACT.md(короткий блок для вставки в контракт агента),REASONING-DISCIPLINE.md(RSN-nn— маркировка утверждений и требования к доказательствам),VERIFIED-CODE.md(CODE-nn),COMPOSITION-AND-LAWS.md(LAW-nn). Рабочий стандарт портала, не отраслевой.- Anthropic, Prompt caching — как стабильный префикс влияет на счёт и что его сбрасывает.
- Anthropic, Effective context engineering for AI agents — инженерный разбор отбора содержимого контекста.
- Claude Code: управление памятью и Cursor: правила — форматы файлов контракта; сверяйтесь с версией своего инструмента.
- AGENTS.md — межинструментальная конвенция файла инструкций для агентов.
- Michael Nygard, Documenting Architecture Decisions — исходная формулировка ADR: место, где живут решения с дорогим откатом.
- OWASP GenAI Security Project и OWASP Top 10 для LLM-приложений — каталоги угроз, включая отравление памяти и инъекции через недоверенный вход.
- Packer et al., MemGPT — уровни памяти по аналогии с виртуальной памятью ОС; Park et al., Generative Agents — поток наблюдений, оценка значимости, рефлексия. Обе постановки далеки от инженерной, читать с поправкой.
- Liu et al., Lost in the Middle — почему объём записи в контексте влияет на то, будет ли она использована.
- Sönke Ahrens, «How to Take Smart Notes» — источник дисциплины связанных заметок; сравнение методов ведения заметок разобрано в главе про инструменты организации работы.
Что дальше
Память отвечает на вопрос «что агент знает до начала работы». Следующий вопрос — что он делает с этим знанием в первую минуту задачи: строит план, декомпозирует или бросается писать код. И, что важнее, в какой момент он обязан остановиться и задать вопрос вместо того, чтобы угадать: Планирование и декомпозиция: когда агент должен остановиться и спросить.