Устав и летопись кодинг-агентов

При ИИ-ассистированной разработке (или, если угодно, вайб-кодинге) довольно быстро проявляются одни и те же проблемы:

  • деградация контекста (модель “забывает” детали);

  • саботаж команд или неаккуратное следование;

  • трудности переноса контекста (в новую сессию или агента);

  • ограничение контекстного окна и суммаризации (контекст ограничен возможностями модели, а контроль за суммаризацией, как один из способов решения проблемы, затруднителен);

  • малая наглядность и проверяемость процесса разработки.

Хочу рассказать об одном из подходов к решению этих проблем через создание постоянной памяти в локальной файловой системе с добавлением текстового управляющего слоя. Такой подход, надо сказать, не нов (об этом в конце), но определённо заслуживает заметки на Хабре.


Сейчас фактическим стандартом ИИ-ассистированной разработки становится использование файла AGENTS.md. Наличие AGENTS.md сигнализирует кодинг-агенту прочитать его содержимое и следовать имеющимся там инструкциям, с чем подавляющее большинство (Codex, Cursor, Gemini, Kilo) успешно справляется. Инструкции могут быть любыми — делать коммиты или обновлять README по завершении цикла, можно прописать архитектуру и спеки, правила и алгоритмы. Если агент игнорирует, то достаточно указать в промпте “следуй инструкциям в AGENTS.md” и периодически напоминать об этом во избежание деградации (Claude Code пока не сдаётся и продолжает уважать только собственный CLAUDE.md, но это лечится симлинком в Linux ln -s AGENTS.md CLAUDE.md или mklink CLAUDE.md AGENTS.md в Windows).

Фактически AGENTS.md представляет собой элементарный управляющий слой кодинг-агента и для небольшого проекта вполне достаточен. Мы же пойдём дальше и разовьём идею до нескольких управляющих файлов и создадим постоянную память под проект.

Сперва инициализируем git-репозиторий. Система контроля версий проекта уже давно является золотым стандартом ИИ-ассистированной разработки, позволяя быстро вернуться к предыдущему состоянию, если вдруг что-то пойдёт не так. Нам же это послужит ещё и журналом изменений для контроля агентом (об этом ниже).

Для наглядности дальше буду разбирать механизм предлагаемого подхода на примере демо-репозитория с простым скриптом постинга объявлений в Телеграм-группы (сам скрипт академического интереса не представляет и хорош только как наглядный пример): https://github.com/gulolog/standing-orders/tree/main/demo

Там нас будут интересовать уже известный AGENTS.md и docs_ru/ (или docs_en/). Весь управляющий слой задублирован на русском и английском: AGENTS.md в репе связан с docs_ru/, но для переключения на docs_en/ достаточно просто его удалить и переименовать AGENTS_en.md в AGENTS.md (после чего и директорию невостребованного языка тоже можно удалить).

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


Итак, часто можно ограничиться единственным AGENTS.md, но при сложном проекте он очень быстро превратится в гигантский трудно читаемый одностраничный справочник. Чтобы этого избежать, вынесем архитектуру нашей будущей системы в директорию demo/docs_ru/architecture/ и пропишем спеки отдельных частей системы в demo/docs_ru/architecture/specs/

Соберём все архитектурные части в одном файле demo/docs_ru/architecture/architecture.md. Теперь в architecture.md у нас получился главный архитектурный источник истины, а отдельные контракты ушли в спецификации. Иными словами: в architecture.md описывается вся система, а в specs/ отдельные компоненты, требующие детального пояснения (таким образом пришли к SDD — Spec-Driven Development).

Поскольку важным преимуществом описываемого подхода является кросс-агентность, то важно сохранять историю архитектурных решений, чтобы следующий агент не притащил какое-то поверхностное решение, отклонённое ранее по более глубоким соображениям. Для этого будем фиксировать принятые решения в файле demo/docs_ru/architecture/decisions.md


Также полезно иметь информацию об актуальном состоянии дел и текущем этапе реализации проекта. Будем фиксировать это в demo/docs_ru/progress.md

И помните инициализированный git? Он становится подробнейшим журналом сделанных изменений и образует пару с progress.md (состояние) + git (журнал). Если, например, случится расхождение кода с текстом, агент сможет самостоятельно выполнить git log для дальнейшего анализа.


А теперь займёмся управляющими правилами агента. Поместим их в demo/docs_ru/control/agent_rules.md

В AGENTS.md уже есть часть управляющих истин, но только верхнеуровневых, выполняющих роль входной точки и отвечающих для восставшего из небытия агента на вопрос “шо тут ваще происходит?”, а вот уже agent_rules.md расскажет ему как жить дальше)


Обобщая структуру проекта:

AGENTS.md — точка входа

docs_ru/progress.md — текущее состояние работы над проектом

control/ — управление поведением агента (совместно с AGENTS.md формируют управляющий слой)

architecture/ — архитектурный слой: содержит архитектуру системы, принятые архитектурные решения с пояснением и спеки.

Особо подчеркну несколько важных компонентов, которые легко пропустить при беглом знакомстве, но они критически важны.

  • Иерархия истины находится в AGENTS.md и одной таблицей указывает как разрешать возможные конфликты, а не оставляет решение на взбалмошный вкус самого агента. Именно эта иерархия запретит ему переписать архитектуру под код, а заставит согласовать код с архитектурой.

  • Протокол сессии там же. Он указывает, что делать в начале каждой сессии (прочитать файлы) и в конце (зафиксировать изменения), без этого весь наш управляющий слой просто кучка текстовых файлов, которые никто не прочитает. Также это несёт подспудную функцию контроля: если после отработки промпта изменения не были отражены в необходимых файлах, значит началась деградация дисциплины и нужно вернуть в контекст “следуй инструкциям в AGENTS.md”.

  • Счётчик состояния state_version в progress.md как самоконтроль состояния, этакий глобальный инвариант (только увеличивается, должен совпадать на старте и перед завершением сессии, увеличивается на единицу строго перед закрытием). Если с этим счётчиком что-то пошло не так (например, он изменился в ходе сессии до закрытия) — это повод всё перепроверить.

Полное дерево проекта с комментариями лежит в AGENTS.md, но это чуть менее однозначное решение, поскольку не несёт особенного практического смысла, ведь и пользователь, и агент могут самостоятельно восстановить файловую структуру, выполнив tree /F под Windows или find . | sort | sed 's|[^/]*/|│ |g; s|│ ([^│])|├── 1|' под Linux. Само же описание файлов уже содержится в соответствующих управляющих или архитектурных истинах, так что в некотором смысле дерево дублирует сущности, чего следует избегать, но здесь в демо-проекте оставлено для наглядности (а тащить ли в рабочий проект лишнюю сущность для наглядности, пусть каждый решит самостоятельно).

.
├── AGENTS.md              # Точка входа, протокол сессии и Иерархия истины
└── docs_ru/
  ├── progress.md          # Летопись: текущий статус и инвариант состояния
  ├── control/
  │  └── agent_rules.md    # Устав: правила поведения и работы агента
  └── architecture/
    ├── architecture.md    # Главный архитектурный источник истины
    ├── decisions.md       # ADR: история принятых и отклонённых решений
    └── specs/             # Спецификации отдельных компонентов (SDD)

В первую очередь предложенный подход — это концепт, который можно и нужно дорабатывать под конкретные задачи.

Шаблоны для быстрого старта здесь: https://github.com/gulolog/standing-orders/tree/main/template

В template/ нужно выбрать язык и поместить содержимое сборки в корень проекта, далее инициализировать git и шаблон, заполнив пустые блоки самостоятельно (что даст больше контроля), или с помощью планировочной сессии, где в промпте описать структуру будущего проекта и попросить LLM дозаполнить архитектурные файлы по шаблону.

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

Сразу надо сказать и об ограничениях такого подхода.

Вся дисциплина тут держится на желании LLM следовать инструкциям в текстовом файле. Деградация происходит молча. В текущем виде деградация может быть замечена через progress.md, но этот механизм можно довести и до полностью автоматического контроля, в текущей же реализации такой риск просто надо иметь в виду.

Отсюда два практических правила:

Первое — минимум сущностей: каждый лишний файл это ещё одна поверхность для расхождения. Если можно обойтись без спеков — надо обходиться без спеков, если можно ограничиться одним AGENTS.md — надо ограничиться. В общем, перед добавлением очередного файла имеет смысл задаться вопросом » а не лишний ли он здесь?», и если для решения задачи сущностей уже достаточно, то не надо их множить без необходимости (привет Оккаму).

Второе — единственный владелец у каждого факта: как только что-то описано в двух местах, две копии разъедутся, вопрос только когда.


И в завершение закрою гештальт, обозначенный в начале. Предложенный подход не нов. Сама идея лежит на поверхности и является логичным развитием концепта AGENTS.md. А более продвинутой реализацией является, конечно же, замечательный OpenSpec, в котором управление на основе текстовых файлов дополняется инструментальной дисциплиной. Однако же OpenSpec может быть избыточным для небольших проектов и обладает некоторыми сознательными ограничениями:

  • накопление опыта специально урезается для сохранения токенов;

  • нет глобального состояния на уровне проекта;

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

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

Автор: RDF

Источник

Оставить комментарий