Перейти к содержимому

Память проекта: что сохранять между задачами с AI

· 10 мин
Содержание

TL;DR: В проекте уже различали неизвестную себестоимость и ноль. При новой реализации с агентом это различие потерялось, а тест закрепил неверное ожидание. Разбираю, какой смысл нужно сохранить между задачами, какие решения можно пересмотреть и почему даже подробная запись не гарантирует, что агент применит правило правильно.


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

В тесте финансового отчёта были две продажи. Себестоимость одной известна – 100. Для второй данных нет. Тест ожидал успешный расчёт и общую себестоимость 100.

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

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

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

Как неизвестная стоимость снова стала нулевой

До переработки отчёт о прибылях и убытках, P&L, не включал документы с неизвестной прибылью. В новой версии отбор изменился: в расчёт могли попасть документы, для которых выручка уже известна, а часть себестоимости ещё нет.

Себестоимость собиралась так:

g.Sum(x => x.CostPriceTotal ?? 0m)

С технической точки зрения всё просто: отсутствующее значение заменяется нулём, затем считается сумма. Но для этих данных "мы не знаем стоимость товара" не означает "товар ничего не стоил". Новая версия могла вернуть итог по неполным данным, не сообщив об этой неполноте.

Тест создавал две продажи из обычного бизнес-сценария: одну с известной стоимостью, вторую без неё. Успех и сумма 100 были записаны прямо в ожидании теста. Покрытие существовало, но поддерживало неверный смысл.

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

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

Почему совета "запишите правило" недостаточно

Инструкции проекта уже запрещали подставлять значения без надёжного бизнес-основания. А код отдельно сохранял различие между неизвестной стоимостью и нулём. Поэтому ещё один файл с правилами сам по себе ничего бы не исправил.

Чтобы понять причину, нужно было бы восстановить, какие материалы агент видел в той сессии. Видел ли он прежнее исправление? Если видел, почему счёл его неприменимым к новому P&L?

Здесь я называю памятью проекта материалы, к которым мы обращаемся в следующих задачах: инструкции, решения, заметки и сводки. В истории P&L подтверждены общий запрет на необоснованные подстановки и код, сохранявший неизвестность. Мы не нашли отдельной заметки агента с искажённым пересказом этого правила.

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

В статье про инженерный harness я разбирал проверку результата до слияния. Здесь нужно сначала выбрать, что сохранять при переработке P&L: различие между неизвестной стоимостью и нулём или весь прежний алгоритм, исключавший неполные отчёты. Первое ограничивает новое решение. Второе само может требовать пересмотра.

Какой смысл сохранять, а что решать заново

Из этой истории можно вынести три коротких правила. Они передают разный смысл.

Что записатьЧто формулировка сохраняет или теряет
"Если стоимости нет, ставим ноль"Теряется различие между неизвестным значением и известным нулём
"Если стоимости нет, всегда возвращаем ошибку"Теряется область правила: для какого отчёта, каких строк и зачем нужна полная стоимость
"Неизвестную стоимость нельзя выдавать за известный ноль"Смысл сохраняется; способ обработки нужно определить для конкретного сценария

Последняя формулировка задаёт ограничение, но не выбирает весь алгоритм. Старая версия исключала неполный документ из расчёта целиком. Новая могла учесть его выручку и одновременно подставить ноль вместо неизвестной себестоимости. При исправлении выбрали явный отказ. Изменился способ работы с неполными данными, но их смысл менять было нельзя.

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

Так же нельзя превратить урок в запрет любого ?? 0m. В исправленном обработчике выражение осталось: сначала проверяются недопустимые состояния, затем выполняется агрегация. Поэтому смысл ?? 0m зависит от того, какие строки прошли предварительную проверку.

Я бы сохранял, зачем действует правило и к каким данным и сценариям оно относится. Для способа обработки отдельно указывал бы статус: действующее решение, наблюдаемое поведение или предложение. Иначе память незаметно превращает временный выбор в обязательное устройство системы.

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

Но ссылка на код отвечает прежде всего на вопрос "что реализовано?". Чтобы ответить "что должно остаться при изменении?", иногда понадобится решение владельца. Даже безупречная ссылка на старый обработчик не делает его поведение новым продуктовым требованием.

Как могла бы выглядеть полезная запись

Для этого случая я составил бы короткую заметку. Это предлагаемый пример, а не найденная память агента и не уже внедрённая практика.

Что сохранитьЗапись
СмыслНеизвестная себестоимость не равна известному нулю
ПочемуПодстановка нуля может занизить затраты и завысить прибыль
ОбластьФинансовые отчёты, где стоимость товара влияет на итог
Историческое исправлениеНе строить точный итог, если для него не хватает стоимости
Из этого не следуетЛюбой null и любой ноль должны ломать любой отчёт
Пересмотреть, еслиИзменилась политика неполных отчётов или правило, по которому движение товара влияет на себестоимость

Внутри проекта к такой заметке нужна ссылка на исходное решение. Если краткая формулировка не покрывает все условия, перед изменением нужно открыть источник. Иначе заметка начинает притворяться полной спецификацией.

Статус тоже нужен по делу. Код показывает историческое исправление, но не заменяет отдельного решения владельца продукта. Если новая задача меняет способ обработки неполных данных, вопрос остаётся открытым, а не разрешается датой последнего редактирования заметки.

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

Когда запись нужно исправить, а когда заменить

При обнаружении противоречия я бы сначала выяснил, что изменилось. Сломалась реализация прежнего решения? Ошибочной была сама заметка? Или владелец действительно принял новое правило?

В P&L проверена потеря прежнего различия при переработке. Я не нашёл решения "отныне неизвестная стоимость считается нулевой", которое оправдывало бы новую трактовку. Поэтому описываю это как ошибку реализации, а не смену политики.

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

Простого "эта заметка новее" недостаточно. В новой заметке может сохраниться предложение, которое в конце обсуждения отклонили. А три сводки, пересказавшие одно сообщение, не дают трёх независимых подтверждений. При конфликте я бы возвращался к основанию и уточнял статус; если его не удаётся установить, оставлял бы вопрос владельцу.

Такой порядок требует работы. Нельзя обещать, что все зависимые пересказы найдутся автоматически. Но известную связь между исходным решением и сводкой стоит сохранить: иначе после исправления первоисточника в следующую задачу снова может попасть старый вывод.

Помогла бы подробная память?

После истории с P&L хочется выписать больше полей: источник, статус, область, условия пересмотра. А затем сказать, что такая запись защитила бы следующую задачу. Я решил проверить хотя бы первую часть этой идеи: даёт ли более подробная форма заметок более правильные решения?

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

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

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

Для статьи результат важен именно этим ограничением. Я могу предложить запись, по которой легче проверить смысл и источник правила. Но не могу обещать, что несколько дополнительных полей сами предотвратят следующий повтор.

Как проверить перенос смысла в следующую задачу

Начать я предлагаю с одного правила, ошибка в котором дорого обходится. Перед изменением попросить агента назвать затронутое ограничение, открыть его основание и отделить сохраняемый смысл от решения, которое задача меняет. Копия инструкции в ответе ещё не показывает, что агент понял эту границу.

Для P&L следующим шагом стали бы проверки для соседних случаев. Ниже – предлагаемые проверки смысла, не дополнительные выполненные тесты исторической версии:

Состояние строкиЧто должна вернуть проверка себестоимости
Себестоимость неизвестна или помечена как отсутствующаяОтказ
Стоимость явно указана как нольСам по себе ноль не вызывает отказ

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

Такие соседние примеры нужны, чтобы исправление не превратилось в новое чрезмерное правило. Проверить только отказ при null мало: можно случайно запретить допустимый ноль и получить ещё одну аккуратную, но неверную реализацию.

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

Кто сможет восстановить основание решения

Мне хотелось бы передавать следующему исполнителю больше, чем запрет на одну строку кода. Чтобы он понимал, почему неизвестная стоимость не равна нулю, какой отчёт требует полного расчёта и что действительно можно поменять в новой задаче. Тогда прежнее исправление помогает рассуждать, а не только запрещает знакомый синтаксис.

За это приходится платить: записи нужно сверять, устаревшие выводы убирать из активного использования, а закрытые материалы не выносить за разрешённые границы. Текущий статус задачи, который проще проверить в Git, я бы не превращал в постоянное воспоминание. И не сохранял бы весь разговор ради одного вывода, если достаточно короткого основания и ссылки.

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

Что почитать

  • Как GitHub проектировал память Copilot – практический разбор записей со ссылками на код и проверки перед применением.
  • Как Claude Code хранит инструкции и заметки – официальная документация о двух источниках памяти, их области и ограничениях.
  • LongMemEval – бенчмарк долгосрочной памяти диалоговых помощников: извлечение, рассуждение между сессиями, обновление знаний и отказ от ответа без основания.
  • Evaluating AGENTS.md – исследование влияния контекстных файлов репозитория на результаты coding agents; в изученных настройках общего улучшения не обнаружено.
Поделиться: Telegram X