Статья
Как научить агентов не повторять свои ошибки
Ревизия 2 — сентябрь 2026 что изменилосьСодержание
Практическая статья для тех, кто строит автономных агентов и упёрся в то, что система не помнит своих провалов: одну и ту же ошибку приходится ловить руками из сессии в сессию. Это разбор рабочей петли само-обучения - как провал становится уроком, как уроки хранятся простым текстом под git, почему судить их обязан отдельный агент, и какие предохранители не дают памяти агента отравиться собственным мусором.
Это самый молодой слой системы, о которой рассказывает соседняя статья про перенос своих принципов в ИИ-агентов. Там он показан сжато - здесь разбираю его целиком и с оговорками.
Представьте ИИ-агента, который делает за вас работу - пишет код, тексты, разбирает задачи. Вы поправляете его: «нет, я имел в виду другое». Он исправляется - в этой сессии. Завтра вы открываете новую сессию, и он не помнит ни этой коррекции, ни того, на чём споткнулся вчера. Вы пишете ту же поправку заново. И послезавтра снова.
Это и есть разомкнутая петля обучения: агент учится внутри разговора и обнуляется на его границе. Статья - про то, как её замкнуть. Не «сделать агента умнее» (это про модель), а перенести урок между сессиями: чтобы провал, случившийся один раз, превратился в запомненное правило и больше не повторялся.
Механика вся на простой технике: папка обычных текстовых файлов в репозитории проекта,
по файлу на урок, плюс отдельный агент, который решает, что достойно записи, и держит эту
папку в порядке. Никакой векторной базы, никаких эмбеддингов - текст под git, который
человек в любой момент читает, правит и откатывает. Где зазвучит мудрёно, держите в голове
эту картинку: заметки о граблях, на которые уже наступали, лежат в папке, и агент читает
их на старте.
Сейчас всё работает на Claude Code, но ядро вендор-нейтрально: формат урока и правила одинаковы на любом движке, а привязка к конкретной среде вынесена в тонкий сменный слой (§11).
За два месяца боевой работы петля выучила про себя главное, и под это отведён отдельный раздел: часть уроков не лечится текстом ни при какой формулировке. Им нужен исполняемый гейт - причём у того, кто заявляет, а не у того, кто проверяет потом (§9).
TL;DR
- Проблема - разомкнутая петля. Агент учится в пределах сессии и забывает всё на её границе. Аудит месяца работы назвал протечки прямо: коррекции владельца не переживают сессию, один и тот же промах повторяется, гейт качества не помнит прошлых обломов.
- Урок - это эвристика, не лог. Короткое правило + мишень-провал, который оно чинит +
адресат (кому из агентов) + триггеры (под какую задачу всплыть) + статус доверия. Всё
plain-text под git: человекочитаемо, диффается, откатывается одним
git revert. - Ступень доверия
candidate → active → retired. Родившись, урок падает в карантин (candidate): наблюдается, но поведение не меняет. Это главный предохранитель против отравления памяти. Повторился, подтвердился, не навредил - судья повышает его вactive. - Recall тремя уровнями. Лёгкий индекс active-уроков грузится всегда на старте; тело урока подтягивается только по совпадению триггера; кандидаты в горячий путь не лезут вовсе - агент лезет в них сам, когда упёрся, и берёт как гипотезу, не как правило.
- Судит и пишет отдельный агент -
lessons-curator. Не исполнитель (он заинтересован, себя не судит) и не менеджер (это раздуло бы его). Профильный судья-библиотекарь: единственный, кто пишет в папку, вызывается точечно и спит между вызовами. - Гейт входа высокий, дефолт - не записать. Урок обязан чинить воспроизводимый механизм (не разовый инцидент), называть конкретную мишень и давать исполнимое лекарство. Правило вида «будь внимательнее» - мёртвое, оно не проходит.
- Отравление лечится не машинерией, а аудируемостью. Вся память - plain-text под git, поэтому любая деградация видима в диффе и обратима откатом. Это дешевле любой governance-системы.
- У лекарства три формы, и текст - только одна из них. Правило, артефакт, гейт-команда. Урок, чей счётчик растёт при «очевидной» формулировке, лучших слов уже не дождётся. Ему нужен исполняемый гейт - и стоять этот гейт должен у того, кто заявляет, а не у того, кто проверит потом.
1. Проблема: система учится в сессии и всё забывает на её границе
Соседняя статья описывает автономную систему из нескольких агентов: дешёвый движок закрывает тактические развилки, менеджер-гейт проверяет работу «как владелец» и держит цикл до готового, мечтатель накидывает идеи. Работает хорошо - но у всей конструкции есть обидная дыра.
Менеджер ловит промах исполнителя и оставляет ноту на будущее: «третий раз приносишь готовое без проверки на самом изменении - прогоняй до готового». Но это будущее длиной в одну сессию. Кончилась сессия - нота умерла вместе с ней. Коррекция, которую владелец написал агенту вчера, назавтра забыта, и он пишет её снова.
Аудит месяца работы (145 сессий) назвал протечки поимённо, и все они про потерю урока между сессиями:
- коррекции владельца не переживают сессию - самое ценное сырьё исчезает;
- один и тот же промах из сессии в сессию - движок выбора недоиспользуется ровно там, где недоиспользовался вчера;
- ложное «чисто» там, где гейт уже обламывался раньше, но не помнит об этом.
Повторяемость одних и тех же граблей - прямое доказательство, что знание не переносится. И задача формулируется не как «научить агента думать» (это работа модели, не обвязки), а узко: замкнуть перенос урока - сделать так, чтобы провал, случившийся однажды, оставил след, переживающий конец сессии.
2. Что такое урок: эвристика, а не лог работы
Первый соблазн - логировать всё: каждую сессию, каждое действие, каждый провал. Это тупик, и он описан в литературе: системы, которые пихают всю историю в память, «плохо масштабируются» и пухнут неограниченно. Урок - не запись того, что случилось, а выжимка того, что из этого следует впредь. Эвристика: короткое правило-механизм плюс метаданные, по которым его находят и которым доверяют (или пока нет).
Физически каждый урок - отдельный markdown-файл в папке LESSONS/ в корне репозитория. Вот как
эта папка выглядит у меня в мастерской агентов (на сентябрь 2026 - 80 файлов: 59 в статусе
active и 19 кандидатов в карантине, суммарно 670 зафиксированных срабатываний):
LESSONS/
├── INDEX.md # лёгкий индекс - грузится на старте
├── L-0001-overengineering-vs-point-fix.md
├── L-0002-claim-without-live-verification.md
├── L-0005-structural-not-neighbor-name.md
├── L-0014-corpus-sizing-from-data.md
├── L-0023-voice-pass-not-prose-quality-pass.md
└── … # файл на урок, имя самодокументирует
А вот формат одного урока - frontmatter с полями плюс тело из правила и «почему». Это
реальный урок из моей базы (поле evidence.situations и хвост обоснования я сократил для
примера, остальное - как в файле):
---
id: L-0002
addressee: [manager, executor]
trigger: [dod, promote, готово, evals, verification]
target: "claim готовности («сделано»/«ЧИСТО») выдан по описанию/памяти, без живого
прогона/скриншота/реального N/бенча или без проверки, что прогнан ИМЕННО тот
объект/версия/бэкенд и покрыта нужная территория"
status: active
evidence: { hits: 14, situations: [...], helped: true, refuted: 0 }
born: 2026-07-04
last-touched: 2026-07-04
---
Правило: перед claim «готово»/«ЧИСТО»/«промоут» - предъяви живой артефакт (свежий
прогон, скриншот, реальное число, бенч на актуальном объекте), а не пересказ по
памяти/описанию. Явно проверь, что прогнан ИМЕННО тот объект/версия/бэкенд/язык,
который заявлен, и что покрыта заявленная территория (не подмножество под другим именем).
Почему: механизм промаха - карта (описание сделанного) подменяет территорию
(реально прогнанное). […]
Каждое поле несёт ровно одно свойство системы, и это не украшение:
target- мишень. Урок обязан называть конкретный воспроизводимый провал, который он чинит. Нет мишени - нет урока (это отсекает процентов девяносто раздувания: без мишени правило превращается в благое пожелание).trigger- ключи для поиска. Отвечают за «не раздувать контекст»: урок всплывает под свою задачу, а не висит в промпте всегда.addressee- кому урок, то есть его скоуп: каким агентам он адресован.executor- тому, кто делает работу и пишет файлы (главная сессия);manager- проверяющему гейту (тот самый менеджер-критик из соседней статьи, что судит работу «как владелец»);subagent:<имя>- конкретному профильному агенту (скажем, ревьюверу кода);role:<роль>- роли вродеwriter, общей для нескольких агентов. Урок для менеджера не течёт в исполнителя и наоборот - это изолирует агентов друг от друга и не даёт ошибке одного размазаться по всем.status+evidence- отвечают за «не навредить»: ступень доверия и счётчик свидетельств (сколько раз сработал, помог ли, опровергался ли).last-touched- чтобы стареть: живая система понижает то, что давно не подтверждалось.
Само правило в теле - механизм, а не частный случай. Не «проверь метод parseConfig», а
«меряй на реальном размере данных». Инцидент - лишь повод; в урок идёт правило, не привязанное к
конкретному случаю, иначе оно сработает ровно один раз и на одном месте.
3. Ступень доверия: карантин как предохранитель от отравления
Самый опасный класс провала у памяти агента - отравление: в память просочилось кривое наблюдение, оно стало «правилом», и теперь агент уверенно делает не то, ссылаясь на выученную чушь. Против этого в петле стоит ступень доверия из трёх состояний, и сырое наблюдение проходит её не сразу.
Рис. 1. Ступень доверия из трёх состояний. Карантин candidate - главный предохранитель от отравления: сырое наблюдение не рулит поведением, пока не заслужило. Всё plain-text под git, поэтому любая правка памяти видима в диффе и обратима.
Ключ - в карантине. Родившись, урок падает в candidate: он наблюдается, копит
свидетельства, но поведение агентов не меняет. Это и есть главный предохранитель: пока
наблюдение сырое, оно не имеет права рулить. Только пройдя гейт доверия - повторился на разных
ситуациях, подтвердился как верный, не начал конфликтовать с другими уроками - кандидат
повышается в active и начинает всплывать в работе.
Важная тонкость: повышение - это суждение, а не арифметика по порогам. Пороги хрупки
(«три хита - в правило» легко обмануть тремя хитами из одного отчёта). Счётчик evidence лишь
помогает судить, но решает не он. И сила подтверждения зависит от источника: явная санкция
владельца («я N раз поправил ровно это») весит больше, чем независимое применение разными
агентами, а то - больше, чем самоотчёт одного применителя (он заинтересован: применял свою же
гипотезу). Один субъективный «помог» кандидата в active не протолкнёт.
По сути candidate → active - это храповик: провал, однажды доказавший себя, застывает в
правило и больше не проходит незамеченным. Но храповик через карантин, и это осознанное
отличие от наивного «баг сбежал к владельцу → сразу навесь постоянный страж». Навесить страж на
один побег - значит закрепить заодно и случайность, и кривое наблюдение (то самое отравление).
Здесь побег сам по себе правило не куёт: он лишь заводит кандидата, а храповик щёлкает только
после того, как наблюдение повторилось и подтвердилось. Медленнее - зато память не твердеет вокруг
шума.
Устаревший или оказавшийся неверным урок уходит в retired - но не стирается: след остаётся в
git. Вся эволюция памяти видима в истории коммитов и обратима.
4. Recall: три уровня, у каждого свой размен надёжности и стоимости
Память бесполезна, если урок не всплыл в нужный момент, и вредна, если всплывает всё подряд и раздувает контекст. Поэтому чтение памяти расслоено на три уровня, и каждый берёт свой размен.
Рис. 2. Три уровня recall: чем ближе к горячему пути, тем осторожнее память грузится. Всегда - только лёгкий индекс; тяжёлое тело - по триггеру; сырые гипотезы - по запросу.
- Уровень 0 - всегда на старте. В начале сессии агент безусловно читает
INDEX.md- лёгкий индекс active-уроков (строчка на урок). Это не «когда вспомню», а обязательный шаг: промах recall дороже лёгкого индекса. «Понять, что урок релевантен» - хрупкое двойное условие; «прочитать индекс всегда» - не промахивается. - Уровень 1 - тело по триггеру. Полный текст урока подтягивается, только когда его
triggerсовпал с текущей задачей. Экономия: индекс лёгкий и в контексте всегда, тяжёлое тело - по требованию. - Уровень 2 - кандидаты по запросу. Кандидаты в горячий путь не грузятся вовсе (иначе сырое влияло бы тихо - это и есть отравление). Агент лезет в них сам, когда упёрся в проблему, находит по мишени подходящую гипотезу и применяет её критически, как зацепку, а не как правило. Применил - шлёт судье наблюдение (что делал и что вышло), и это двигает кандидата к подтверждению или к отбою.
Индекс грузится всегда, поэтому он держится в жёсткой форме: разбивка по статусу, таблица, и полный текст правила в индекс не кладётся (он живёт в теле урока и тянется по триггеру), иначе Уровень 0 рос бы с каждой записью. Вот как выглядят реальные строки active-секции моего индекса:
## active (59) - меняют поведение
| id | адресат | триггеры | мишень (кратко) |
|--------|------------------|----------------------------------|----------------------------------------------|
| L-0001 | forge-wide | overengineering, dod, structure | оверинж-надстройка вместо точечной правки |
| L-0002 | manager,executor | dod, promote, verification | claim готовности без живого прогона |
| L-0005 | forge-wide | naming, dispatch, scope-boundary | имя соседа как костыль вместо признака |
Читая индекс, агент берёт на борт мишени и триггеры, а тела открывает только те, что совпали с работой. Плюс есть Уровень 2а - открытые вопросы в карантине, о них в §7.
5. Судья-библиотекарь: почему уроки судит отдельный агент
Кто решает, что провал достоин урока? Тут легко ошибиться дважды.
Не исполнитель - он заинтересованная сторона. Он сам сделал работу и сам ошибся; просить его судить свои же провалы - это тот же confirmation bias, из-за которого нельзя доверять самопроверке агента (об этом - в статье про петлю проверки). Автор себя не судит: если бы он видел свою ошибку, он бы её не сделал.
Не менеджер - судить урок значит знать всю накопленную библиотеку (чтобы не дублировать), знать глобальные принципы (чтобы не тащить в проект то, что уже есть в ядре), понимать владельца. Навесить это на менеджера - раздуть его роль.
Поэтому суждение об уроке - отдельный профильный класс, и его держит агент lessons-curator:
судья и библиотекарь петли. Устроен он по трём принципам:
- Единственный, кто пишет в
LESSONS/. Одна точка ответственности за целостность. Все остальные уроки только читают - править их руками запрещено (ручная правка сыплет сырьё мимо гейта доверия и ломает петлю). - Standalone. Судья получает на вход сырьё (сигнал провала + контекст + замысел, против которого мерили работу) и судит по своим правилам. Кто и зачем его позвал - ему безразлично; он не знает про менеджера, гейты и оркестрацию вокруг. Это делает его переносимым: методология суждения живёт целиком в его теле, и он едет на другую систему одним файлом.
- Вызывается точечно и спит между вызовами. Не постоянный «надзиратель», крутящийся на каждом шаге (это был бы оверинж), а профильный судья, которого дёргают на гейте пачкой - ровно как ревьювера кода. Постоянного оверхеда ноль.
Судья сам выбирает операцию по существу сигнала - позвавший его не приговаривает «это урок, запиши»:
| Операция | Что делает |
|---|---|
| extract | сигнал провала → урок / разовость / в глобальное ядро; запись как candidate |
| trust-gate | candidate → active по свидетельствам + проверка, что не начал вредить |
| update-evidence | пришло наблюдение применения кандидата → судья сам оценивает «помог/опроверг» |
| replace | новый урок про то же самое → переписать старый, не плодить второй |
| consolidate | «сон»: слить дубли, вычистить устаревшее, разрешить конфликты (§8) |
| deferred | мишень реальна, а лекарства нет → вернуть требование, а не писать пустой урок (§7) |
И над всем - его установка: дефолт - не записать. Планка входа высокая, задача судьи - держать библиотеку короткой и правдивой, а не копить оговорки. Меньше уроков, но каждый рабочий, лучше каталога из ста «будь внимательнее».
6. Как провал попадает в петлю: гейт входа
Урок не заводится от каждого «что-то пошло не так». Триггер (событие в работе) - ещё не урок; чтобы стать кандидатом, сигнал проходит гейт входа целиком:
- Механизм, а не исход. Не «дал неверный ответ», а «почему структурно не мог дать верный». Урок чинит воспроизводимый механизм промаха, а не конкретный симптом.
- Повтор. Один случай - наблюдение в карантине, ждём. Тот же механизм на разных ситуациях - записывается кандидатом. Исключение - явная сильная санкция владельца по одному крупному эпизоду (он сам откатил и проговорил правило): она может засеять кандидата и с одним хитом, чтобы санкционированный сигнал не потерялся. Но в active его всё равно переведёт только повтор.
- Лекарство закрывает пробел, а не велит его не иметь. Вот тонкий и самый ценный фильтр. Правило проходит, только если даёт конкретный исполнимый шаг («меряй на реальном размере данных» - ясно, что делать) либо указывает на уже существующий артефакт с нужным знанием (чек-лист, тест, фикстуру). А «действие» вида «сверяйся с реальностью», «будь полнее», «попади в тон» - это мишень наизнанку, увещевание в повелительной форме. Признак такого мертвяка: провал повторяется много раз, хотя правило «очевидно», - значит, лекарство не в правиле, а в недостающем артефакте.
Откуда берётся сам сигнал - и это далеко не только владелец. Провал ловит чаще не человек,
а сама система: менеджер-гейт, не подписавший работу; code-reviewer, нашедший баг; профильный
судья вкуса, поймавший оверинж; отклонённая проверка готовности. Коррекция владельца - лишь один
источник из нескольких, и часто не главный: большая часть уроков рождается там, где система
поймала свой промах сама, без единого слова от человека. Ретро-сбор из истории (§10) - ещё
один источник, тоже не сводящийся к репликам владельца.
Теперь - как сигнал вообще доходит до судьи. Тут два пути, и это не дубль:
- Push от менеджера, немедленно. Гейт качества посреди работы распознал системную находку (свою или пришедшую от ревьювера), достойную урока, - и указывает исполнителю запустить судью тут же, фоном, тем же механизмом, которым зовёт ревьювера кода. Одна находка - один запуск.
- Выгреб в конце сессии, пачкой. Коррекции владельца, находки ревью и прочие мелкие сигналы копятся в буфере сессии и сливаются судье партией - на финальном ритуале перед отчётом владельцу или по порогу размера буфера. Это лечит главную течь: сессия часто сходит на нет без финального гейта, и без выгреба весь накопленный буфер испарился бы вместе с ней.
Почему выгреб доверен исполнителю, а не менеджеру: у исполнителя горизонт - вся сессия, он единственный слышит живые реплики владельца в потоке. Менеджер между сессиями ничего не помнит и по природе конвергентен; навесить на него ещё и владение выгребом - поставить церемонию против его природы, и она будет проседать.
И ещё тонкость доставки, из недавней ретроспективы петли: сигнал судье кладётся дословно, а не пересказом. Коррекция владельца идёт его реальными словами; системную находку менеджер отдаёт отдельным блоком «сигналы судье» - дословные наблюдения своего аудиторского следа плюс указатели на территорию, - а исполнитель форвардит этот блок судье целиком, не переформулируя. Причина та же, что держит всю петлю: исполнитель - заинтересованная сторона, он сам делал работу, и его пересказ вернул бы судье ровно тот bias, от которого судья и стоит в стороне. Обобщить провал в урок - работа судьи; дело исполнителя - донести первоисточник нетронутым. Первоисточник выше пересказа.
7. Deferred: третий исход, когда провал реален, а лекарства нет
Обычно судья отвечает бинарно: урок или разовость. Но есть третий, важный случай, и он появился не сразу - его вскрыла ретроспектива петли.
Оказалось, петля исправно ловила, что ломается, но не проверяла, что записанное лекарство
лечит. В одной из баз висел урок с восемью хитами и helped: null - провал повторялся снова
и снова, а правило-увещевание («будь аккуратнее») не помогало ни разу, потому что лечить там было
нечем в форме правила. Урок был, а толку не было.
Отсюда - вердикт deferred. Если мишень реальна и повторяема, но выразимого лекарства-
механизма нет (или улик на суждение не хватает), судья не пишет пустой урок и не роняет сигнал
в тишину. Он делает третье: возвращает тому, кто его позвал, явное требование - «чтобы это стало уроком,
нужно вот что: механизм вида вместо X делай Y, либо построенный артефакт, либо независимый
повтор» - и осаждает то же требование в карантин индекса, чтобы оно пережило конец сессии.
Это превращает открытую проблему из мёртвой сноски в активный запрос: на старте и когда
агент упирается в задачу, он пробегает deferred-вопросы карантина - совпал с текущей работой -
пробует закрыть его по ходу (нашёл механизм, сделал артефакт → это и есть сырьё судье). Петля не
просто копит проблемы, а ищет под них лекарства. А если мишень уже повторилась много раз и дорого
обходится, судья помечает требование приоритетным: «петля кровоточит, сконцентрируй силы и найди
лекарство сейчас».
Тут стоит развести две болезни, которые легко спутать, - и это различение тоньше, чем кажется.
Первая - пробел: правила нет вовсе, лечить нечем; это и есть deferred. Вторая - правило
есть, но не срабатывает: оно записано, а провал всё равно повторяется, и поле helped копит
null вместо подтверждений. Лечат их по-разному, и путать нельзя. Пробел ждёт механизма
(deferred-запрос). А неработающее правило ловит гейт доверия: кандидата с helped: null и
растущими хитами он в active не пустит, а active-урок, оказавшийся неверным, уводит в
retired. Без этой развилки петля тихо копит мёртвые увещевания - правила, которые вроде бы есть,
а на деле ни разу не помогли.
8. Сон и custodian: как библиотека сама себя чистит
Запись уроков по одному неизбежно накапливает дубли, устаревшее и мелкие конфликты. Против этого -
отдельный офлайн-проход, «сон» (прямой аналог between-session consolidation у Claude Dreaming).
Никогда в горячем пути работы, всегда отдельным заходом по порогу размера или команде владельца.
Склонность у прохода одна и намеренная - сжимать, а не растить: слить дубли, вычистить
retired, понизить устаревшее, разрешить конфликты между уроками, сжать разросшееся. Каждый шаг -
отдельный git-коммит, и делает их судья сам, не дожидаясь команды «закоммить»: правка
фиксируется автономно, шаг за шагом, поэтому сразу видима в диффе и обратима. Это и есть авто-коммит
ясных фиксов - но фиксов в саму память, не в код продукта: судья автономен ровно в своей папке
LESSONS/ и только там.
Как «сон» вообще запускается - тонкий момент. Фонового демона, который крутил бы уборку по расписанию, в системе нет - да он и не нужен, это ведь просто файлы и промпты. Поэтому глубокая уборка едет прицепом к обычной работе: когда база материально перезрела (индекс распух прозой, накопилось много несведённого, давно не было полного прохода), судья не отделывается лёгким проходом, а помечает в шапке индекса «нужен полный сон». Эту метку безусловный recall уровня 0 (§4) поднимает на старте ближайшей сессии - и та сессия «сон» и запускает. Ритм привязан не к часам, а к активности: пока копится дрейф - копится и метка, а не тишина.
Но у «сна» есть вторая, менее очевидная работа - сверка дрейфа в остальном знании проекта.
Знание в проекте живёт не только в LESSONS/: оно есть и в постоянных инструкциях (AGENTS.md),
и в скиллах. Судья на «сне» активно сверяет уроки с этими соседними источниками и по трём
классам подаёт рекомендацию исполнителю:
- градуация - active-урок про стоячую дисциплину устоялся, но его ещё нет в постоянных
инструкциях → «занеси его туда» (там он грузится всегда и дёшево, урок из
LESSONS/схлопывается); - конфликт - урок противоречит существующему правилу или скиллу → это на эскалацию: какой источник первичен, решает владелец, а не догадка по дате изменения;
- скилл-пробел - лекарство процедурное и объёмное, а покрывающего скилла нет → «оформи скилл».
При этом судья сам соседние источники не правит (пишет только в LESSONS/) и находку не
маршрутизирует - он даёт класс и факт, а кому и как чинить, решает диспетчер. Это тот же принцип
роли, что и во всей системе: у каждого узкая зона ответственности, и границы между ними жёсткие.
Но находку он заявляет уверенно: урок дозрел по его планке - это «созрел, занеси», а не робкое
«вообще-то можно бы, если захочешь». Разница не косметическая: робкая рекомендация зря гоняет
исполнителя переспрашивать владельца о том, что методология уже решила. Уверенность тут в
диагнозе, не в полномочиях - правит всё равно не судья.
Третья работа «сна» - не дать библиотеке разжиреть, и здесь у меня есть числа. Урок пишется
один раз, а читается сотнями прогонов, так что объём тела - прямой расход. Замер по трём базам:
тела крупнее 20 КБ давали 92-93% всего расхода на чтение уроков. Уходит он в журнал улик -
подробный нарратив «где и как урок сработал». 609 записанных ситуаций, 522 КБ повествования.
И 91% из них уже несли ссылку на первоисточник (коммит, сессию, файл со строкой): журнал был
третьей копией события. Лечение простое - журнал сжат с абзацев до строки полей
(что / где / когда / кто поймал / ссылка). Пилот на самом толстом уроке: −55% объёма, все 29
ситуаций сохранены. А 12 наблюдений из журнала при этом поднялись в само правило, то есть
текст стал заодно точнее.
И одна ловушка, на которой я обжёгся, когда настраивал эту уборку, - она общая для всех порогов-триггеров. Первый признак «пора сжимать» звучал разумно: тело крупнее 20 КБ. На деле под него попадали 24 файла из 77 - то есть он был истинен всегда и ничего не ограничивал, а половина его выдачи - тела, где журнал уже свёрнут и объём несёт само правило. Признак, который срабатывает постоянно, - это не признак, а фон. Заменил на два счётных и проверяемых командой: журнал вдвое больше прозы правила, либо тело крупнее 45 КБ. Из той же серии - решение не схлопывать похожие уроки в один: восемь уроков про «проверку» действительно делят одно место в цикле работы, но это восемь разных механизмов, и слитая мишень перестала бы совпадать с задачей вовсе. Вместо слияния каждый урок класса обязан нести блок «чем он не является», а есть ли он, проверяет команда.
Так у устоявшегося урока есть путь наверх, в стабильное ядро: проектная частность, доказав себя
в разных проектах, промоутится в постоянные инструкции или в скилл и перестаёт быть уроком. Быстрый
дешёвый слой (кандидаты в LESSONS/) и медленный обдуманный (промоут в ядро) - две скорости
одной системы.
9. Когда правило не лечит: три формы лекарства
Это главное, что петля выучила про себя за два месяца. Разговор тут уже про природу правил вообще.
Гейт входа (§6) требует от урока исполнимого лекарства. Долгое время я считал, что лекарство
- это всегда формулировка: правильно написанное правило, которое агент прочитает и исполнит. Счётчики сказали иначе. У части уроков срабатывания копились годами при идеально ясной формулировке: правило записано, прочитано на старте хода, признано очевидным - и провал случается снова, в том же самом ходе. Верхушка моей базы сейчас выглядит так: урок про заявление, не сверенное с территорией, - 44 срабатывания; урок про проверку, которая целится в удобный двойник вместо самой цели, - 35; урок про надстройку вместо точечной правки - 32.
Это не «правило плохо написано». У промаха просто нет окна, в котором текст мог бы сработать. Читаешь правило в одном месте, а промахиваешься в другом - в потоке, где внимание занято задачей и решает привычка. Текст держит там, где есть пауза на чтение. Где паузы нет - нужна другая форма.
Поэтому у урока в моей петле теперь три формы лекарства, и судья выбирает форму, а не только слова:
| Форма | Когда работает | Пример |
|---|---|---|
| Правило (текст) | решение принимает человек или агент в момент чтения, есть пауза | «начинай с простого; усложняй, когда измерено, что простое не тянет» |
| Артефакт | знание нужно в готовом виде, а не в памяти | чек-лист поверхностей, фикстура, шаблон сценария |
| Гейт (команда) | промах случается в потоке, мимо внимания | команда сверяет заявленное с территорией и возвращает ненулевой код |
Гейт - самая молодая из трёх и самая полезная. Устроен всегда одинаково: подкоманда сверяет
заявленное с фактическим и возвращает ненулевой код при расхождении. Кодов два. Второй важнее:
1 - расхождение поймано, 2 - сверка не состоялась (территория недоступна, данных нет).
Без него молчание неотличимо от чистоты, и программный потребитель прочитает ноль как «проверено».
Гейты у меня появились под классы одного рода - «заявление против территории»: сверить названное в отчёте число со свежим выводом команды, которая его добывает; сверить статус задачи с доской, а не с памятью; сверить состав фиксации в системе контроля версий с тем набором файлов, который ты объявил своим; сверить, что сохранённый вердикт ревью не старше кода, который он объявляет проверенным; вычесать из текста отчёта идентификаторы коммитов и проверить каждый.
Но вот что важнее самой идеи гейта - и это я узнал дорогой ценой. Инструмент, построенный под урок, сам по себе ничего не лечит. Тот самый урок с 44 срабатываниями получил свой гейт, когда счётчик показывал 11. Дальше он вырос до 44 - при том, что инструмент был построен и работал. Дело оказалось в том, у кого он стоит: звал его проверяющий, постфактум, когда отчёт уже написан и ложное число в нём стоит. Смотреть на этой территории было уже нечего.
Переключение случилось, когда гейт передвинули к заявляющему и привязали к событию. Пара
«до/после» с честным переходом «помогло: неизвестно → да» в базе пока одна. Урок о синхронизации
документации (26 срабатываний) ушёл из карантина в active в тот день, когда сканер стал
обязательным шагом ПЕРЕД сдачей. И тогда же впервые поймал пропуск сам, в потоке автора, а не
руками внешнего ревьювера через раунд.
Второй замер той же природы, ещё жёстче. Есть класс промаха, где короткий флаг одной популярной утилиты означает совсем не то, что ожидает рука, привыкшая к соседней утилите. Текстовое правило про это ловило примерно одну шестую случаев - остальные проходили мимо, потому что рука быстрее памяти. Помог только технический заслон в два слоя: перехват до исполнения плюс подмена команды в путях поиска. Правило-текст здесь исчерпало себя целиком.
Отсюда - формулировка, которую я теперь применяю ко всякому уроку с высоким счётчиком:
Если провал повторяется, а правило «очевидно» - лекарство не в правиле. Спроси, в каком месте потока случается промах, и поставь проверку туда, у того, кто заявляет, а не у того, кто проверит потом. Знание, что инструмент существует, момента его вызова не создаёт.
Обратная сторона: гейт стоит дороже текста - его надо построить, принять (как именно - в соседней статье про петлю проверки, §5) и поддерживать. Поэтому форма выбирается по природе промаха: большинство уроков прекрасно живут абзацем.
10. Откуда взялась живая база: холодный старт из истории
Ждать, пока уроки накопятся в бою сами, - месяцы. Пустая петля бесполезна, и есть приём завести её сразу с реальной базой: ретроактивный сбор из истории работы. Он же, кстати, повторяет ход, которым в соседней статье собирались сами принципы владельца, - не спрашивать себя «на чём я обжигался», а вычитать это из следов реальной работы.
Процедура держится на одном разделении - сбор ≠ суждение, это два разных звена, и мешать их нельзя:
- Сбор (анализатор, не судья). Пройти историю проекта - неважно, в какой она форме: транскрипты сессий, changelog’и, коммиты, ретроспективы, переписка-коррекции. Вычленить провал-сигналы: коррекции (где поправили, отвергли, переделали) плюс повторы одного механизма в разных местах. Отсеять шум - успешную работу, разовые мелочи.
- Группировка по механизму. Похожие провалы в одну группу; счётчик повторов - будущее
evidence. - Суждение (судья, batch). Каждую группу прогнать через тот же гейт входа. Прошло - урок. Ретро-урок обычно рождается кандидатом; в active сразу попадает лишь тот, что подкреплён многократной явной коррекцией владельца (авторитетная санкция правила - это не тот confirmation bias, что у самоотчёта агента).
- Сразу консолидация. Ретро-сбор даёт много разом - тут же «сон», чтобы база родилась чистой, а не свалкой.
Рис. 3. Холодный старт: живая база уроков вычитывается из истории работы. Сбор ведёт анализатор, суждение - отдельный судья; смешать их в одного - вернуть bias заинтересованной стороны.
Так и завели базу в моей мастерской: прогнали историю - полсотни сырых сигналов сжались в
двадцать одну первую запись. Сбор вёл один агент-анализатор, судил - lessons-curator; смешивать
их в одного нельзя, иначе вернётся тот же bias заинтересованной стороны.
11. Портируемость: вендор-нейтральное ядро и тонкий адаптер
Требование к системе с самого начала было - не прибивать её гвоздями к Claude Code. Решается это одной границей: ядро отделено от адаптера.
- Ядро знает только про файлы: папка
LESSONS/, формат урока, правила гейта и доверия, правила консолидации. Оно одинаково везде и ни про какого вендора не знает. - Адаптер знает «кто и когда читает и пишет» - и это всего три крючка: на старте сессии прочитать индекс; поймал коррекцию - в буфер, на выгребе - судье; по порогу - запустить «сон».
В Claude Code эти крючки - расширение сессионного режима плюс сам агент-судья. На другой системе
(скажем, через AGENTS.md конвенцией или кодом обёртки в SDK) переписывается только адаптер -
три крючка. Формат урока, гейты, правила чистки едут как есть. Recall при этом - явное действие
агента (прочитать файл), а не магия автозагрузки конкретной среды; именно поэтому он переносится.
Отдельно решается «не хочу ставить это в каждый проект»: механизм (правила петли) живёт один раз
глобально, а per-project только данные - папка LESSONS/ с уроками этого проекта. Правила проект не
копирует - указывает на глобальный дом тонкой ссылкой. Заводится папка лениво, сама, когда
родился первый урок. Пустые проекты остаются чистыми. Сейчас у меня так живут 12 независимых
баз, 183 урока - у каждого сколько-нибудь длинного проекта своя.
И вот грабля, которую этот дизайн создаёт сам. База висит не только в корне репозитория. Свою папку уроков заводит и подпроект внутри него - она про его код, корневая про постройку агентов. Правило recall написано под одну базу, поэтому исполнитель его честно исполняет и промахивается: читает индекс того репозитория, чьи инструкции загрузились, а работает внутри подпроекта. Условие выбора - папка работы, а не репозиторий: правишь подпроект - читаешь его индекс наравне с корневым. Цену я замерил на живом ходе. Индекс подпроекта прямо предсказывал находку, а нашёл её проверяющий гейт: исполнитель читал базу не той папки.
12. Что стоит забрать себе
Даже если вы не строите именно эту систему, из петли переносится несколько узлов:
- Урок - эвристика, а не лог. Храните вывод («впредь делай так»), а не протокол («вот что случилось»). Логи траекторий не масштабируются; правила-механизмы - да.
- Карантин перед поведением. Свежее наблюдение не должно рулить агентами сразу. Дайте ему статус «гипотеза», пусть копит подтверждения, и только заслужившее меняет поведение. Это ваш главный щит от отравления памяти.
- Судит - не автор. Тот, кто сделал работу и ошибся, не судит свои же уроки. Отдельный агент-судья, в идеале с единоличным правом записи в память, - одна точка ответственности за её чистоту.
- Гейт входа с высокой планкой. Требуйте механизма (не исхода), мишени (не «вообще лучше») и исполнимого лекарства. Ловите увещевания даже в повелительной форме: «будь полнее» - это не правило, а переформулированная жалоба.
- Recall уровнями. Лёгкий индекс - всегда; тяжёлое тело - по триггеру; сырые гипотезы - только по явному запросу. Так память помогает и не раздувает контекст.
deferredкак честный третий исход. Не всякая реальная проблема имеет готовое лекарство. Признавайте это явно и возвращайте требование «что нужно, чтобы стало правилом», а не пишите пустое правило, которое не лечит.- Аудируемость вместо governance-машинерии. Держите память plain-text под версионированием. Тогда любая деградация видна в диффе и откатывается одной командой - это дешевле и надёжнее любой сложной системы контроля.
- Заводите базу холодным стартом. Не ждите месяцами накопления - вычитайте уроки из истории работы. Только разведите сбор (анализатор) и суждение (судья): смешаете - вернёте bias.
- Считайте срабатывания - счётчик скажет то, чего не скажет формулировка. Урок с высоким счётчиком при «очевидном» правиле - сигнал, что текст тут бессилен. Меняйте форму лекарства, а не слова.
- Гейт ставится у того, кто ЗАЯВЛЯЕТ. Проверка постфактум, у проверяющего, приходит на территорию, где смотреть уже нечего. И привязывайте её к событию («перед сдачей», «перед фиксацией»): знание, что инструмент есть, момента вызова не создаёт.
- Порог, истинный всегда, - это фон, а не порог. Прежде чем вводить признак «пора убираться», посчитайте, скольких он касается прямо сейчас. Если больше трети библиотеки - он ничего не ограничивает.
- База выбирается по папке работы, а не по репозиторию. Как только баз становится больше одной, правило «прочитай индекс» начинает промахиваться молча - исполнитель честно читает не ту базу.
Границы и честные оговорки
Слою два месяца боевой работы поверх ретро-старта - он больше не эксперимент, но и не выстоявшая система. Держать пределы явными - часть честности.
- Ступень доверия обкатана, но её честность держится на суждении, а не на замере. Полный цикл
candidate → activeна живом материале пройден многократно: база выросла с 39 файлов до 80 в корне и до 44 в подпроекте, срабатываний зафиксировано под семьсот в каждой. Но повышение по-прежнему выносит судья, и «помог ли урок» он оценивает по свидетельствам, которые ему приносят. Это лучше наивных порогов и хуже настоящего замера. - Признак-триггер может оказаться фоном, и один такой у меня открыт прямо сейчас. Признак «урок давно не пересматривался» считается от даты последней уборки. Значит, урок с уже вынесенным вердиктом попадает под него на каждом следующем проходе - бег на месте. Починка понятна (считать от даты вердикта, а не от даты прохода), но требует правки схемы во всех файлах, и я её пока не сделал. Называю, потому что это ровно тот класс, о котором предупреждает сама петля: признак, истинный всегда, - не признак.
- Промоут в ядро - на бумаге. Путь «устоявшийся урок → постоянные инструкции» описан и проходится руками, но регулярной операцией не стал.
- Адаптер существует не там, где спроектирован. По дизайну привязка к среде - отдельный тонкий слой; на практике чтение индекса вшито императивом прямо в тела агентов и в сессионный режим. Работает. Но переносимость от этого хуже, чем обещает конструкция: при переезде придётся вписывать три крючка в чужие тела.
- Порт на второй движок - не проверен. Ядро вендор-нейтрально по конструкции, но живого переноса на не-Claude систему ещё не было. Это дизайн, а не доказанный факт.
- Механический слой - спроектирован, не построен. Дрейф и раздувание я сейчас ловлю суждением: судья на «сне» сверяет расхождения, аудиторская память гейта ловит регрессии между раундами. Механический слой поверх этого - порог-скрипт на размер индекса, счётчики застрявших кандидатов и частоты переписываний как сигнал дрейфа по окнам запусков - в дизайне есть (следующий приоритет крепости), но кода пока нет. Против более механических self-improving систем это честное слепое пятно, а не решённое: суждение надёжнее наивных порогов, но само по себе тоже не замер.
- Риски памяти агента известны и неприятны - отравление, тихий дрейф, самозакрепление плохого. Против каждого стоит предохранитель (карантин; «помог ли», а не только «повторился»; git-аудит), но выдержат ли они объём - покажет только бой. Пока база небольшая, и настоящий стресс-тест ступени доверия впереди.
Коротко: ядро работает и живая база лежит в репозитории, но это система с первыми живыми прогонами, а не выстоявшая. Потому и помечаю честно.
Родственные идеи и источники
Узлы петли я выводил из своей практики и из аудита протечек, а не из статей - но, выписав их, увидел, что почти каждый ложится на уже названный в литературе паттерн. Это скорее независимое переоткрытие, и совпадение - хороший знак.
- Постоянная память и офлайн-консолидация. Memory tool (CRUD-файлы памяти между сессиями) и Claude Dreaming (between-session консолидация логов в паттерны без человека) у Anthropic - прямые прародители папки под git и «сна».
- Уроки-эвристики, а не логи. ExpeL (Zhao et al., 2024) и ERL - переносимые эвристики из траекторий плюс retrieval релевантного под задачу; отсюда «урок - выжимка, не протокол» и recall по триггеру.
- Провал раздувания памяти. A-MEM - эволюционирующая память-граф и её же проблема неограниченного роста токенов, когда память правят равномерно; отсюда индекс отдельно от тела и консолидация вне горячего пути.
- Карантинная запись. D-MEM (dopamine-gated agentic memory) - не всё пишется в память; гейт входа решает, урок это или разовость.
- Governance эволюционирующей памяти. SSGM (Governing Evolving Memory in LLM Agents) и бенчмарк MemEvoBench - рамка против memory poisoning и дрейфа; мой ответ на это - аудируемость через git, а не отдельная governance-машина.
- Рефлексия и вербальные уроки. Reflexion (Shinn et al., 2023) - вербальная рефлексия в памяти между попытками; Generative Agents (Park et al., 2023) - поток памяти с рефлексией. По духу - тот же слой.
Честная граница: я не реализовывал эти работы по списку, а расставил ссылки постфактум. Соответствия
- на уровне паттерна и духа, не дословной реализации.
Заключение
Разомкнутую петлю обучения замыкает не хитрая технология, а дисциплина: провал оседает эвристикой в текстовом файле под git, отдельный судья с высокой планкой решает, что достойно записи, карантин не пускает сырое рулить поведением, а «сон» не даёт библиотеке зарастать. Всё человекочитаемо и обратимо - поэтому память агента можно растить, не боясь, что она тихо отравится.
Слою два месяца боевой работы. База выросла вчетверо, ступень доверия отработала на живом материале, а сама петля успела назвать собственный предел - есть класс уроков, который текстом не лечится вовсе (§9). Направление то же, что и во всей системе, о которой речь в соседней статье: вынести наружу, в переносимый текст под версионированием, ещё один слой, который обычно живёт только в голове, - на этот раз память о том, на чём уже обжёгся.
И один виток, который я не проектировал нарочно. Петля живёт в той же мастерской, которая её строит, - и потому учится на собственной постройке: урок вроде «коммить сделанное сразу, не жди команды» - это урок о том, как собирается следующий агент кузницы, включая следующую версию самой петли. Не «система переписывает саму себя» - уроки применяют исполнители, промоут арбитрит владелец, - но контур, который учится и на том, как устроен сам.
И самый переносимый вывод, шире этой конкретной петли: память агента - не свалка логов, а курируемая библиотека уроков, и всё держится на том, кто и по каким правилам решает, что в неё попадает. Записать легко; ценность - в дисциплине не записывать лишнего и не давать сырому рулить.
Что изменилось в статье
Петля прожила два месяца боевой работы. Главное, что она выучила про себя: часть уроков не лечится текстом ни при какой формулировке.
- Новый §9 — три формы лекарства: правило, артефакт и гейт-команда. И замер, показывающий, где текст мёртв: под урок построили инструмент, а счётчик вырос с 11 до 44 срабатываний. Инструмент звали постфактум, а не ставили шагом у заявляющего.
- §11 — несколько баз рядом: база выбирается по ПАПКЕ РАБОТЫ, а не по репозиторию. Сейчас на машине 12 независимых баз, 183 урока.
- §8 (сон) — амортизация библиотеки: сжатие журнала улик до строки полей (−55% объёма, все свидетельства на месте), счётные признаки уборки и «болезнь вечно-истинного признака».
- Числа приведены к сентябрю 2026: 59 active / 19 candidate в корневой базе, 670 срабатываний.
- Статус слоя понижен с «экспериментального» до «работающего, с названными пределами».
Первая публикация: формат урока, карантин candidate→active, три уровня recall, судья-библиотекарь, холодный старт из истории.


