Два отказа, а не один
Их часто сводят к одному «агент испортил файл», но чинятся они по-разному и происходят по разным причинам.
Правка невиданного. Агент выдаёт запись по имени файла, содержимого которого не запрашивал: восстанавливает его по памяти о похожих проектах, по описанию задачи, по своему прошлому запуску. Результат правдоподобный и не тот. Заметно это не сразу, потому что файл после записи выглядит нормально написанным.
Затирание чужой правки. Агент прочитал, подумал — а за это время файл поменял человек в редакторе, линтер, форматтер или соседний процесс. Агент пишет свою версию поверх, и чужое изменение исчезает бесследно: ни ошибки, ни конфликта, ни следа в истории до коммита.
Второй отказ интереснее первого, потому что от него не спасает ни аккуратность агента, ни качество модели. Он про время, а не про знание.
Почему идемпотентность здесь не помогает
Соседний паттерн (Idempotency For Agents) отвечает на вопрос «что будет, если повторить этот вызов». Ответ «повтор не меняет результат» устаревшую запись не лечит вовсе: записать одно и то же испорченное содержимое дважды ровно так же разрушительно, как один раз.
Различие в том, кто вклинился. Идемпотентность разбирается с повтором своего вызова, предохранитель устаревшей записи — с чужим изменением между чтением и записью. Механизмы не заменяют друг друга и нужны оба.
Устройство
Состояние — одна таблица на сессию: путь после разрешения ссылок → время изменения файла на момент чтения.
- Чтения записываются централизованно, наблюдателем, на каждый успешный вызов читающего инструмента. Инструменты об этом ничего не знают.
- Проверка стоит на воротах подтверждения, а не внутри пишущего инструмента. Поэтому ни один файловый инструмент не пришлось трогать, и новый получает предохранитель бесплатно.
- Новый файл пишется свободно: если его ещё нет, читать было нечего.
- Сброс — при очистке сессии и смене рабочего каталога.
Разрешение символических ссылок до сравнения обязательно: без него один и тот же файл под двумя путями считается двумя, и предохранитель молчит там, где должен сработать.
Сообщение об отказе несёт исправление
Оба отказа возвращают не «запрещено», а что делать дальше: «файл не читался в этой сессии — прочитай его, чтобы не переписать невиданное» и «файл изменился на диске с момента чтения, его мог поправить человек или инструмент — прочитай заново». Читатель здесь агент, у которого между запусками нет памяти, и текст отказа — единственное место, куда можно положить порядок действий (Agent First Repository).
Развилка: предохранитель падает открытым
Реализация, из которой снят этот разбор, при любой неожиданной ошибке — не прочиталось время изменения, отказали права — разрешает запись, а не блокирует её.
Решение осознанное, и его стоит принимать явно, а не наследовать. Довод за: это предохранитель от собственной оплошности агента, а не рубеж безопасности; сломавшись, он не должен останавливать всю работу с файлами. Довод против: тот же самый отказ — недоступный stat — бывает признаком того, что с файловой системой что-то не так, и продолжать запись там сомнительно.
Ориентир: предохранитель от своей ошибки падает открытым, предохранитель от опасного действия — закрытым. В том же проекте разбор команды для правил разрешений устроен наоборот: неразобранная команда не получает автоматического одобрения (Human in the Loop).
Чего он не заменяет
Это защита от несогласованности, а не от вредоносного действия и не от потери данных. Файл, который агенту вообще нельзя трогать, закрывается областью файловой системы (Capability Containment); возможность вернуть испорченное — границей отката (Rollback Boundary); подтверждение необратимого — воротами (Approval Path). Предохранитель устаревшей записи стоит рядом с ними и решает свою узкую задачу, зато почти бесплатно.
Связано с
- Tool Calling — где стоит гейт и почему он внутри исполнения, а не в промпте
- Idempotency For Agents — повтор своего вызова; здесь чужое изменение между чтением и записью
- Agent Failure Modes — каталог тихих отказов, к которым относятся оба описанных
- Approval Path — ворота подтверждения, на которых удобно разместить проверку
- Capability Containment — что агенту недоступно вовсе; предохранитель работает внутри доступного
- Human in the Loop — постоянные правила разрешений и противоположная посадка отказа
- Agent First Repository — текст ошибки как носитель инструкции для агента