Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Правила поверх генерации

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

Правила — данные, а не код: таблица stream_rules, мастерский CRUD (stream_rule_list / _create / _edit / _delete) и первичный посев один раз за жизнь базы, как у наборов.

Что это НЕ заменяет

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

Две половины

половинаscopemodeчто делает
мягкаяtool:<имя>remindнапоминание в результат вызова, тур идёт дальше
жёсткаяtextinterruptготовый ответ выбрасывается, ход переписывается

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

Мягкая

Смотрит на имя и аргументы вызова и, если совпало, клеит спереди к результату этого вызова блок:

<system-reminder rule="local_shell_for_his_machine">…текст правила…</system-reminder>

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

Порядок в точке вклейки важен: сначала снимается служебная метка «пусто», потом выхлоп режется капом context.tool_result_max_chars, и только потом сверху ложится напоминание — кап считается по выхлопу инструмента, а не по правилу.

Жёсткая

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

<system-interrupt reason="rule_violation" rule="telegram_reply_tail">…текст правила…
That reply was discarded and NOBODY saw it. …</system-interrupt>

Дальше ход играется заново с той же историей. В базу и клиенту уезжает только исправленный ответ; сам <system-interrupt> живёт лишь в промпте этого тура и никуда не сохраняется.

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

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

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

Форма правила

полечто значит
nameключ дедупа и повтора; он же едет модели в атрибуте тега
scopetool:<имя> — вызов инструмента; text — готовый ответ. Мыслей у нас в истории нет, thinking не поддерживается
conditionregex по цели: аргументы вызова компактным JSON либо текст ответа. Пусто — совпадает любая цель этого scope
negateсработать, когда regex не нашёлся: «не хватает хвоста» иначе не выразить — в rust-regex нет lookaround
whenусловия окружения: telegram, client_ops, master, trusted, picture. Все обязаны выполниться; пусто — любой тур
textчто подставится модели (до 400 символов)
moderemind | interrupt; следует из scope, называть не обязательно
repeatonce — раз на чат навсегда; after:<N> — не чаще раза в N ходов чата
enabledвыключенное правило не поднимается, но не теряется

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

Без repeat правило говорит одно и то же каждым вызовом, и напоминание перестают замечать. Ход считается ответом модели; правило, сработавшее в туре, в этом же туре не повторится.

Невалидная regex в базе — правило не регистрируется, warning в лог, тур идёт дальше: ядро не падает из-за кривых данных. На входе (создание, правка) такая regex просто не принимается.

Что посеяно

Шесть хронических промахов: два мягких, четыре жёстких.

правилополовиналовит
write_diff_over_write_fileмягкаяwrite_file — напомнить, что правка существующего файла идёт через write_diff
local_shell_for_his_machineмягкаяshell в туре с клиентом — это машина ядра, а не машина Господина
telegram_markdown_imageжёсткая![](url) в телеграмном ответе — там markdown не рендерится
telegram_reply_tailжёсткаяответ в телеграм без хвоста ↩ #id (правило с negate)
look_before_refusingжёсткая«не могу посмотреть картинку» там, где картинка в контексте и видна
telegram_react_not_emojiжёсткаяголый эмодзи вместо вызова telegram_react

Посев — один раз за жизнь базы. Правку правила Господином ядро отличает по updated_at: волна калибровки (когда меняются сами тексты посева) переписывает только то, к чему он не прикасался, и переписанное им не трогает.

Телеметрия

Сработавшее правило никогда не молчит: строка stream rule fired (у текстового — stream rule fired on the answer, с полем retry: переписан ли ход) в лог и запись в stream_rule_stat. Иначе через месяц никто не поймёт, откуда в контексте посторонний текст и куда делся ответ. Свод — в GET /v1/stats, поле rules:

"rules": [{ "rule": "local_shell_for_his_machine", "fired": 3 }]

Ноль срабатываний у правила — тоже ответ: либо промах вылечен, либо условие мимо.

Записи переживают своё правило: удалить правило — не то же самое, что переписать историю.