reins — оставь домен от Quest CLI, а ratchet вынеси во фреймворк Image: AI generated

how-make-quest была про то, как построить Quest CLI голыми руками. Что такое ratchet, как повесить gate, как заблокировать cheese. Дай агенту одну статью — и на выходе Go CLI на основе cobra.

Но что произойдёт, когда возьмёшься за второй Quest CLI. Снова напишешь тот же однонаправленный конечный автомат. Снова напишешь те же scan/next/submit/status/export. Снова напишешь ту же блокировку PASS, то же монотонное убывание remaining, тот же JSONL export. Меняется лишь один gate, а ты каждый раз заново пишешь всё остальное. Это налог на boilerplate, который ты платишь с каждого нового квеста.

Паттерн был переиспользуем. Код — нет. reins закрывает этот зазор.

Что инвариантно, а что — домен

Положи два Quest CLI рядом и посмотри на разность (差分) — граница станет резкой.

Инвариант (общее для всех квестов)      Домен (своё у каждого квеста)
─────────────────────────────────       ─────────────────────
ratchet: TODO→PASS необратим             что является одним квестом
скелет команд: scan/next/submit…         что есть «факт»
свод уровней: Fail/Review→verdict         какой cheese нужно блокировать
персистентность·resumable прогресс
export: однократная эмиссия

Левая часть — ровно то, что доказала how-make-quest: будь домен названием компании, эндпоинтом или функцией, зубцы ratchet цепляются одинаково. Правую знает лишь человек. reins поставляет левую часть как фреймворк и оставляет тебе только правую.

Это не новое утверждение, а старый принцип, который reins принуждает соблюдать кодом — разделение решения и реализации. Gate — это решение (что в этом домене истинно), а ratchet·CLI·свод — это реализация. Переписывать реализацию каждый раз — это провал, привязывающий решение к реализации.

Реализуешь лишь один gate

Создать квест через reins — значит заполнить четыре метода одного интерфейса.

type Definition interface {
    Seed(args []string) ([]*quest.Item, error)            // вход → начальный TODO-seed
    Render(it *quest.Item) (string, error)                // промпт на запись + контекст проверки, что покажет next
    Prepare(it *quest.Item, raw []byte) (gate.Context, *quest.Verdict, error) // декод заявки
    Rules() []gate.Rule                                   // каталог правил-нарушений gate
}

func main() { cli.NewQuestCmd("myquest", myDef{}, cli.Options{}).Execute() }

Одна строка main поставляет ratchet, шесть команд, свод, export и resumable-сессию целиком. Ты написал лишь четыре доменных куска. Агенту по-прежнему достаточно знать две команды — получает через next, сдаёт через submit. Остальное решает машина.

Gate — это каталог правил защиты от cheese

Суть how-make-quest была: «проектируй gate, который невозможно зачизить». reins превращает это проектирование в структуру данных — gate = каталог правил. Одно правило — один детектор cheese. Обнаружив нарушение, оно срабатывает (true) и грузит факт (Fact).

// Одно правило защиты от cheese для квеста извлечения новостных событий.
// «Существует ли who-якорь в оригинале» — если агент выдумает персону, он попадётся.
var whoAnchorPresent = gate.Rule{
    Meta: gate.RuleMeta{ID: "who-anchor-present", Level: gate.LevelFail, Desc: "обязательный who-якорь существует в оригинале"},
    Check: func(ctx gate.Context) (bool, quest.Fact) {
        sub := ctx.Submission.(*Event)
        if miss := textmatch.MissingTokens(ctx.Source, sub.Who.Anchors); len(miss) > 0 {
            return true, quest.Fact{Where: "who.anchors", Expected: "substring оригинала", Actual: miss[0]}
        }
        return false, quest.Fact{}
    },
}

Достоинство этой структуры в том, что она растёт. Каждый раз, обнаружив новый cheese, добавляешь одно правило — и gate становится настолько же твёрже. И каталог документирует сам себя: когда команда rules выводит список правил, это и есть «аудит-список cheese, который я блокирую». Нет gate, не знающего, что он блокирует.

Серьёзность — это не вес, а уровень. Один Fail — значит сразу FAIL. Решающее нарушение не торгуется: девятью нарушениями на 99 баллов не покрыть один Fail. Evaluate сводит сработавшие правила по уровням: есть хоть один Fail — FAIL, нет, но есть Review — REVIEW, всё прошло — PASS.

Асимметрию полномочий принуждаем типом

Самая важная строка в how-make-quest была: «блокировать PASS может только машина». reins вбивает это не как соглашение, а как тип.

L1 машина(детерминизм)   единственное право блокировать PASS
L2 ИИ(скептик)           только REVIEW — поднимает сомнение, но не присуждает завершение
L3 человек               остаток, упущенный обоими

Машинный gate выносит PASS. Даже если поставить ИИ-верификатор в gate, максимум, что он может, — вытолкнуть в REVIEW. Неверное дело делаем невозможным изначально: если фреймворк не даёт ИИ API для выдачи PASS, ты даже по ошибке не доверишь вердикт пьяному другу.

Второй backend — defeat graph

Многим gate достаточно свода уровней из независимых правил. Но когда правила начинают конкурировать — «это нарушение значимо лишь при наличии того», «корневая причина этого провала на самом деле вон та» — написанные руками if-else-гарды разъедают gate. Не там, где ломается слабый gate, а там, где гниёт сложный gate.

Второй backend gate у reins переносит эту конкуренцию в декларативный граф — toulmin h-Categoriser. Модель аргументации Тулмина прямо становится структурой данных:

  • Warrant — tautology PASS. Основание «нет возражений — значит прошло».
  • Counter — нарушение атакует warrant.
  • Supersedes — приоритет между правилами. Какое возражение бьёт какое возражение.

Написанные руками клаузы-гарды испаряются в рёбра Attacks·Supersedes. И когда рёбер 0, этот граф в точности эквивалентен своду уровней — сложность это цена, включаемая (opt-in) лишь при необходимости (включается, когда Definition реализует gate.Evaluator).

Настоящий подарок графа — не вердикт, а обратная связь. Оценка графа возвращает агенту прямое прохождение — Verdict.Feedback: «почему проиграл и что поменять, чтобы выиграть». Это не простое «FAIL», а корневая причина, вычисленная из структуры аргументации.

Здесь снова срабатывает парадокс how-make-quest. Модель угодлива — она покорно следует указаниям. Для мнений угодливость яд, но для фактов угодливость актив. Прохождение — это не мнение («как-то странно»), а факт («who.anchors нет в оригинале, поменяй это»). Чем угодливее модель, тем покорнее она принимает этот факт и сходится. Детерминированный граф + угодливый LLM = цикл с гарантированной сходимостью.

Побочные эффекты изолируем — ground и staged-оценка

Чтобы gate был детерминированным, сети не должно быть внутри gate. Правило, напрямую вызывающее net/http, невозможно покрыть юнит-тестом, а вердикт колеблется от состояния канала.

reins сгоняет побочные эффекты в pkg/ground: примитивы вроде HTTPBody·MXResolves владеют внешними запросами через инъецируемый Resolver и снимок на каждый запрос. Правило остаётся чистым, а внешний мир берёт на себя ground.

И staged-оценка: сначала идут дешёвые проверки, и если они провалились, сетевой fetch вообще не происходит. Нет смысла резолвить DNS для заявки в неверном формате. Дорогое и шаткое ставим за дешёвым и надёжным.

Запрет на абстракцию при N=1

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

Это не придирчивость, а первопринцип. Абстракция, вытащенная из одного случая, принимает случайность этого случая за сущность. Лишь когда второй домен требует ту же абстракцию, доказывается, что она инвариантна. Фреймворк применяет «не утверждение, а проверку» даже к собственной эволюции. Как gate не верит утверждениям агента, так абстракция не верит утверждению одного случая.

Та же фраза, ставшая библиотекой

reins стоит на семи пакетах в pkg/textmatch (примитив отсечения галлюцинаций), temporal (нормализация времени), quest (ядро ratchet), gate (контракт gate), graph (defeat graph), ground (изоляция сети), cli (cobra-скаффолд). Проходят go build·go test, покрыты все функции. И toulmin сцеплен однонаправленно только с graph-backend, так что потребитель, не использующий граф, даже не линкует toulmin.

Код: github.com/park-jun-woo/reins

Если how-make-quest была одной фразой — генерация может быть вероятностной, верификация обязана быть детерминированной — то reins застыл этой фразой в компилируемой форме. Gate перепроверяет факты домена, ratchet блокирует прошедшее, граф возвращает причину проигрыша как факт, а угодливая модель подчиняется этому факту.

В следующий раз, когда понадобится Quest CLI, не пиши ratchet заново. Используй лишь gate своего домена, а поводья — одолжи.


Что почитать вместе

Принцип, который reins застыл в коде — генерация вероятностна, верификация детерминирована — это не открытие одного лишь reins. Незнакомые друг с другом люди упёрлись в одну стену и пришли к одному выводу. Проекты независимой конвергенции, собранные how-make-quest, тому свидетельство.

  • episteme — принудительная Reasoning Surface перед необратимой операцией. Та же интуиция, что и ratchet у reins: PASS проверяют, прежде чем заблокировать.
  • MagLab — «LLM лишь рассуждает, числа — детерминированному инструменту». То же разделение, что и изоляция побочных эффектов reins в pkg/ground.
  • Manifesto — «Agent proposes, World verifies». Одной фразой суммирует асимметрию полномочий reins (только L1 блокирует PASS).
  • oh-my-kamisama — «diffs beat claims». Тот же принцип, что gate перепроверяет не утверждение агента, а факт.

А корни defeat-graph-backend — в теории аргументации, в линии Toulmin·Dung·Amgoud из источников ниже. pkg/graph у reins переносит этот более чем 60-летний формальный логический аппарат в структуры данных Go.


Источники

  • Toulmin, S. (1958). The Uses of Argument. Cambridge University Press. — модель аргументации, чьи Warrant·Ground·Backing прямо взяты в defeat graph.
  • Dung, P.M. (1995). “On the Acceptability of Arguments and its Fundamental Role in Nonmonotonic Reasoning, Logic Programming and n-Person Games.” Artificial Intelligence, 77(2), 321–357. — первоисточник абстрактного фреймворка аргументации и attack(defeat)-графа.
  • Amgoud, L. & Ben-Naim, J. (2013). “Ranking-based semantics for argumentation frameworks.” SUM 2013, LNCS 8078, 134–147. — weighted h-Categoriser, принятый в pkg/graph. Свойство Compensation, при котором приемлемость атакованного узла восстанавливается, если он снова защищён, и гарантия сходимости.
  • Nute, D. (1994). “Defeasible Logic.” In Handbook of Logic in Artificial Intelligence and Logic Programming, Vol. 3. Oxford University Press. — классификация strict/defeasible/defeater. Формальный корень уровней правил reins (Fail/Review) и приоритета Supersedes.
  • Modgil, S. & Prakken, H. (2014). “The ASPIC+ Framework for Structured Argumentation: A Tutorial.” Argument & Computation, 5(1), 31–62. — система аргументации, структурировавшая классификацию Nute внутри фреймворка Dung. Родословная defeat graph.
  • Gabriel, V.O. et al. (2020). “Reasoning in BDI agents using Toulmin’s argumentation model.” Theoretical Computer Science, 805, 76–91. — предшествующий пример программной реализации модели Тулмина (BDI-агенты). pkg/graph у reins переносит это в вердикт gate.
  • Von Neumann, J. (1956). “Probabilistic Logics and the Synthesis of Reliable Organisms from Unreliable Components.” Automata Studies, Princeton University Press. — принцип построения надёжного протокола поверх нестабильных компонентов (предпосылка reins).
  • Stechly, K., Valmeekam, K., & Kambhampati, S. (2024). “On the Self-Verification Limitations of Large Language Models.” arXiv:2402.08115 — самопроверка почти не повышает качество → причина отдать право PASS машине L1.
  • McKee-Reid, L. et al. (2024). “Honesty to Subterfuge: In-Context RL Can Make Honest Models Reward Hack.” arXiv:2410.06491 — даже честная модель манипулирует, когда судит собственную награду → основание асимметрии полномочий.
  • Bondarenko, A. et al. (2025). “Demonstrating Specification Gaming in Reasoning Models.” arXiv:2502.13295 — чем выше способности, тем лучше находит лазейки gate → причина, почему gate=каталог правил должен расти.
  • Thaman, K. (2026). “Reward Hacking Benchmark: Measuring Exploits in LLM Agents with Tool Use.” arXiv:2605.02964 — преднамеренное упрочнение gate сократило эксплойты на 87,7%.
  • Fanous, A. et al. (2025). “SycEval: Evaluating LLM Sycophancy.” AAAI/ACM AIES 2025. arXiv:2502.08177 — замер уровня капитуляции под угодливостью. Две стороны «для фактов угодливость актив».
  • Shapira, I. et al. (2026). “How RLHF Amplifies Sycophancy.” arXiv:2602.01002 — теорема о том, что RLHF усиливает угодливость. Предпосылка цикла сходимости фактическая обратная связь + угодливость.
  • Deque Systems (2021). “Automated Testing Study Identifies 57 Percent of Digital Accessibility Issues.” — граница машинно-судимой области (57%) и человеческого остатка (20%).

Связанные статьи

История изменений

  • 2026-06-05: Первая версия