Memory Write Policy

Память делает ошибки долговечными: неверная запись переживает исходный запуск и всплывает через недели в другом контексте. Поэтому путь записи в память — чувствительный путь записи, а не удобная автоматика.

Суть

Разница между ошибкой в ответе и ошибкой в памяти — во времени жизни. Плохой ответ виден сразу и стоит одного запуска. Плохая запись невидима, участвует в десятках следующих ответов и к моменту обнаружения уже не восстанавливается по одной трассе.

Типичная механика отказа: пользователь однажды написал «не тратьте время на уточнения, сразу эскалируйте», агент сохранил это как устойчивое предпочтение — и через две недели, когда ситуация требовала как раз уточнения, уверенно эскалировал.

Самый опасный путь — запись в горячем пути

Модель ответила, рантайм тут же вызывает сохранение в память. На короткой дистанции выглядит красиво. Системные проблемы этого пути:

  • запись происходит под давлением задержки, поэтому проверять некогда;
  • никто не валидирует, что вообще достойно сохранения;
  • нет шага нормализации и удаления лишнего;
  • нет отдельной политики изоляции арендаторов;
  • потом невозможно объяснить, почему факт оказался в памяти.

Скучное правило, которое это закрывает: запись в долговременную память по умолчанию либо явно разрешена политикой, либо вынесена в фоновый конвейер (Context Compaction).

В горячем пути разумно оставить минимальное состояние сессии, короткие рабочие заметки, временные записи с понятным сроком жизни и то обновление, без которого текущий процесс действительно ломается.

Фоновый конвейер не подтверждает запись

Вынос записи из горячего пути — правило выше — покупается ценой, которую стоит назвать прямо: вызов возвращает идентификатор задачи, а не результат. Факта в памяти в момент возврата ещё нет, и когда он там появится, вызывающая сторона не знает.

Следствий три, и каждое своё:

  • Чтение сразу после записи не находит только что записанного. Сценарий «сохранил предпочтение и тут же на него сослался» при фоновом разборе не работает, а тест, написанный последовательно, будет проходить через раз.
  • Разбор может решить, что фактов в присланном тексте нет, и не записать ничего. Код ответа при этом успешный. Это молчаливый холостой ход на пути, который заметка целиком объявляет чувствительным.
  • Ошибка разбора видна только тому, кто отдельно спросит про задачу. К этому моменту ответ пользователю давно отдан.

Лечится тем же, чем лечится молчание вызова инструмента: исходов у записи не два, а три — записано, фактов не извлечено, ошибка разбора, и они обязаны различаться в ответе и в журнале (Agent Failure Modes — три статуса вызова, разведённые по той же причине). Средний исход нужен именно отдельным: «ничего не извлечено» — это нормальная работа разбора и одновременно самый частый способ потерять запись незаметно.

Слой памяти по умолчанию интерпретирует, а не хранит

Внешний слой памяти — не хранилище, куда кладут текст. Он вызывает модель, извлекает из присланного факты и хранит их (Mem0). Значит, прочитанное не обязано совпадать с записанным, и это поведение по умолчанию, а не особый режим.

В интерфейсе это оформляется переключателем: извлечение включено изначально, дословное сохранение просят явно. Выбор определяется тем, что именно записывается. Реплика диалога, из которой нужен вывод, идёт через извлечение. Формулировка, у которой важна точность буквы — условие договора, значение параметра, цитата с чужими словами, — сохраняется дословно: иначе слой перескажет её своими словами и потеряет ровно то, ради чего её сохраняли.

Запись, которую слой не имеет права переписать

Второе следствие интерпретации — автоматическое разрешение противоречий: новый факт, противоречащий старому, вытесняет его без спроса. Для предпочтений пользователя это правильное поведение и отдельное достоинство слоя (Mem0 — вытеснение устаревшего). Для части записей — неправильное, и отличаются они не важностью, а тем, чем запись является:

  • проверенный факт с известным происхождением, у которого источником обновления может быть только та же процедура проверки;
  • значение с внешним владельцем: лимит, тариф, условие договора, ограничение политики — его меняет владелец, а не вывод модели из реплики;
  • запись, на которую уже сослались в принятом решении: переписав её, систему лишают возможности объяснить прошлый ответ.

Практическая форма — признак неизменности у записи, запрещающий системе трогать её самостоятельно; обновление такой записи идёт только явным вызовом. Слой памяти без такого признака годится для профильной памяти и не годится для проверенных фактов — то же разделение слоёв, что в Memory Boundaries, но выведенное со стороны записи.

Только создание: вытеснение выражается новой записью

Запрет переписывать отдельную запись — половина решения; вторая половина в том, чтобы перезаписи не было в интерфейсе вообще. Хранилище, у которого есть только операция создания, выражает устаревание фактa новым документом со ссылкой на замещаемый, а не правкой старого на месте.

Разница видна не в момент записи, а в момент разбора. При перезаписи вопрос «что здесь было до вредной записи» не имеет ответа: прежнего текста нет нигде, и восстановление вытесненного факта невозможно в принципе (Memory Poisoning). При создании-только прежняя запись остаётся на месте, просто перестаёт быть актуальной, и откат сводится к снятию пометки — операции, которая ничего не теряет.

Цена честная и её надо назвать: объём растёт монотонно, а чтение обязано понимать цепочку замещений, иначе выдаст устаревший факт как текущий. Это плата за обратимость, и она ниже, чем кажется: записей, замещающих друг друга, в реальном профиле немного, а именно они и есть самое спорное содержимое хранилища.

Хорошая память пишет меньше, чем хочется

Отбор важнее объёма. В память стоит писать только то, что пригодится в будущих запусках, имеет понятного владельца и арендатора, объяснимо человеку, не тащит лишних чувствительных данных и не превращает подсказку в свалку.

Проверка перед каждой записью формулируется одним вопросом:

Если этот фрагмент всплывёт через три недели в другом контексте, мне будет комфортно объяснить, почему он здесь?

Неуверенный ответ означает, что запись не нужна.

Но «меньше» — не та ось для рабочих артефактов

Правило строгого отбора верно для профильной памяти и проверенных фактов: там лишняя запись переживает запуск и начинает влиять на решения. Для объёмных рабочих данных ось другая — не «сколько писать», а «что именно хранить».

Кейс из Agent Memory: наивный агент-химик израсходовал 20 млн токенов там, где агент с указателями обошёлся 1234 — разница около 16 000×. Экономия достигнута не отказом от записи, а тем, что в контексте остался короткий идентификатор, а содержимое ушло во внешнее хранилище и подтягивается по запросу.

Отсюда уточнение к правилу: перед тем как решать «писать или нет», стоит проверить третий вариант — записать ссылку. Он снимает главное возражение против записи (раздувание подсказки), сохраняя доступность данных, и потому для результатов инструментов, длинных документов и промежуточных артефактов почти всегда предпочтительнее обеих крайностей (Context Compaction).

Минимальная политика

memory:
  allowed_kinds:
    - profile_preference
    - validated_fact
    - session_summary
  deny_sources:
    - raw_user_prompt
    - external_html
    - unvalidated_tool_output
  require_tenant_id: true
  reject_if_contains:
    - secrets
    - access_tokens
    - payment_card_data
  write_mode:
    profile_preference: background_review
    validated_fact: immediate_if_trusted
    session_summary: background_only

Ценность не в «уме» правил, а в том, что они видимы и поддаются аудиту. Обратите внимание на deny_sources: сырой пользовательский промпт и непроверенный вывод инструмента запрещены как источники — именно они дают большинство плохих записей.

Правила чтения и записи почти никогда не совпадают

Если развести только хранилище, но не пути, система скатывается в странную логику: всё, что однажды записалось, потом читается почти откуда угодно.

Разведение выглядит так:

  • запись в долговременную память требует проверки, происхождения и фонового разбора;
  • чтение из неё разрешено только через фильтры извлечения (Agent Retrieval Policy);
  • запись в профильную память требует явного сигнала или высокой уверенности;
  • чтение профильной памяти разрешено слою персонализации, но не движку политик (Agent Control Plane).

Последняя строка — самая важная: если непроверенная память участвует в решении политики, персонализация превращается в канал влияния на права.

Происхождение хранится по умолчанию

Для каждой записи, живущей дольше одного запуска, минимальный набор полей: source_type, source_id, writer_identity, tenant_id, written_at, confidence или validation_state.

Выглядит бюрократией ровно до первого спора о том, откуда взялся «факт», который агент уверенно повторил в другом контексте.

Идентичность пишущего задаёт среда, а не вызов. Поле writer_identity теряет смысл, если его значение приходит аргументом от того, кто пишет: провенанс тогда сообщает не «кто записал», а «кем записавший назвался». Практичная форма — брать идентичность из конфигурации процесса на старте и не принимать её в параметрах операции; тем же значением ограничивается и выдача на чтении.

Импорт сначала staged, потом durable

Чужой каталог памяти, transcript или backup не должен сразу попадать в автоматический context injection. Безопасный путь: импортировать во временную область, показать preview с provenance, проверить tenant/чувствительные данные и только затем явно повысить записи до durable. До promotion они доступны разбору, но не влияют на обычные ответы.

Тот же provenance ограничивает обещание «забыть». Система может надёжно удалить записи и производные, для которых сохранилась lineage; она не может честно гарантировать удаление копий без происхождения, внешних backup и уже экспортированных данных. Поэтому forget возвращает область выполненного удаления и известные остаточные границы, а не безусловное «всё забыто».

Траектория — свидетельство, insight — производная

Experiential memory часто сохраняет два разных объекта, и склеивать их в одну запись нельзя:

  • сырая траектория — вход, действия, результат, точное решение человека accept / reject / edit, его правка и идентификаторы запуска;
  • извлечённый insight — короткая формулировка, которую модель вывела из одной или нескольких траекторий: правило, область действия, polarity, confidence и ссылки на источники.

Первая запись отвечает «что произошло» и остаётся неизменяемым evidence. Вторая отвечает «чему, возможно, стоит научиться» и является гипотезой. Её можно пересобрать, объединить с дублем или отклонить, не переписывая исходное событие.

Решение ревьюера тоже не равно истине. accept подтверждает полезность комментария в конкретном контексте, reject может означать ошибку агента, локальное исключение или нехватку времени, а edit смешивает техническую поправку со стилем формулировки. Поэтому один feedback-event не должен автоматически превращаться в обязательную инструкцию следующих запусков. Безопасный поток выглядит так:

trajectory + feedback → proposed insight → дедупликация и проверка конфликтов → управляемое правило

До последнего перехода insight извлекается как подсказка с provenance, а не как policy. Для повторяющихся сигналов система агрегирует независимые source_review_id, хранит противоположные примеры и не выдаёт model-generated confidence за частоту подтверждений. Эта граница позволяет использовать идею ExpeL — извлекать опыт без изменения весов — и не превращать ошибочную интерпретацию одного ревью в долговечный стандарт (Reflexion, Skill Change Control).

Практичная реализация держит три слоя, а не два: immutable raw trajectory; per-run analysis с outcome, первым ошибочным шагом и ссылками на evidence; cross-run candidate с областью применимости, поддерживающими и противоречащими случаями. Формальное знание выпускается только из третьего слоя после transfer-проверки. Так случайный успех не превращается в правило лишь потому, что красиво объяснён постфактум (Continual Agent Evolution).

Память — это контекст, а не инструкция

Разделение путей чтения и записи отвечает на вопрос «кто читает». Есть второй вопрос, который задают реже: чем прочитанное становится. Запись памяти, попавшая в контекст, ничем внешне не отличается от правила — тот же текст на том же языке, и модель обращается с ней так же.

Отсюда граница, которую держат явно: запись памяти рождается непроверенной и не может тихо стать правилом, умением или политикой. Формально это выражается полем доверия со значением «не проверено» у каждой новой записи и отсутствием перехода, который меняет это значение автоматически.

Неочевидная часть — что делает человек, когда запись всё же признана верной. Он не повышает доверие записи: принятое знание переносится в управляемый артефакт другого слоя — правило, решение в журнале решений, runbook, документацию репозитория. Хранилище памяти при этом источником истины не становится никогда и остаётся тем, чем было: подсказкой, у которой есть происхождение.

Это дороже, чем поднять флаг, и разница именно в том, ради чего затевалось. Флаг «проверено» на записи оставляет знание в хранилище, куда пишет агент; перенос кладёт его туда, где действует обычный порядок изменения — владелец, ревью, история. Второе — свойство слоя, первое — свойство строки.

Почему повышение не может выполнить агент

Формулировка, которая делает границу проверяемой: агент с доступом к оболочке не может считаться независимой границей человеческого одобрения. Если повышение записи до правила — это операция, доступная агенту, то согласование выполняет тот же, кто предлагает, и одобрение перестаёт что-либо означать.

Следствие практическое и жёсткое: автоматического перехода к повышению не должно существовать в интерфейсе, а не только в правилах пользования. Отсутствующая операция не обходится ни промптом, ни ошибкой конфигурации; запрещённая — обходится и тем, и другим (Approval Path, Human in the Loop).

Та же логика приложима шире записи памяти: она объясняет, почему право применить proposal к умению отделено от права его подготовить (Skill Change Control), и почему фильтр маршрутизации, выбираемый вызывающим, нельзя называть границей прав — он сужает выдачу по просьбе того же, кого ограничивает.

Повышение, которое уничтожает происхождение

Правило «переносить, а не поднимать флаг» подтверждено второй независимой реализацией. Там повышение урока команды — отдельная команда с перечислимыми порогами: уверенность не ниже 0,90, не меньше пяти голосов «за», не меньше двух участников, возраст не меньше двух недель. Результат пишется в клон основной ветки и попадает туда только через обычный запрос на слияние — то есть ровно перенос в управляемый артефакт.

Та же реализация показывает ловушку, и она стоит на самом осторожном шаге. Текст урока при повышении переписывает модель, и промпт переписывания предписывает убрать происхождение и ужесточить утверждение: для умения — «убери ссылки на конкретные даты, людей и разовые случаи», для правила — «убери контекст случая и сформулируй ограничение как абсолютный стандарт».

Последствие механическое: наблюдение из одного случая, с его оговорками, становится абсолютным правилом без даты и без автора, и ревьюер запроса на слияние видит только эту версию. Исходник не теряется — урок получает пометку, куда его повысили, — но в запросе на слияние его нет, то есть нет там, где принимается решение.

Это прямо против раздела «Происхождение хранится по умолчанию» выше, и случается именно на переходе, ради которого весь раздел затевался: перенос в управляемый слой помогает, только если перенесённое можно сверить с наблюдённым. Отсюда два требования к шагу повышения: запрос на слияние несёт исходник рядом с переписанным текстом, и переписанный текст не категоричнее источника — перевод наблюдения в правило остаётся решением ревьюера, а не свойством промпта (Paraphrase Drift, Context Compaction).

Связано с

  • Agent Memory — какие вообще бывают виды памяти
  • Memory Poisoning — что происходит, когда политика записи отсутствует
  • Memory Boundaries — разные слои с разными правилами
  • Context Compaction — фоновый конвейер, куда выносится запись
  • Agent Retrieval Policy — фильтры на чтении
  • Agent Control Plane — почему память не должна влиять на решения политики
  • Multi Tenancy AI — изоляция арендаторов в записи
  • Mem0 — внешний слой, извлекающий факты из диалога, и его вытеснение устаревших фактов
  • Agent Failure Modes — три исхода вызова, по образцу которых разводятся исходы фоновой записи
  • Skill Change Control — та же граница на слое умений: подготовить предложение и применить его — разные права
  • Approval Path — подтверждение относится к точному артефакту, а не к намерению
  • Human in the Loop — где в цепочке стоит человек и что именно он подтверждает
  • Reflexion — словесный разбор опыта и риск закрепления ошибки оценщика
  • Continual Agent Evolution — offline-консолидация нескольких оценённых траекторий в versioned candidate