reins — Quest CLIからドメインだけを残し、ratchetはフレームワークへ Image: AI generated

how-make-questはQuest CLIを素手で建てる方法だった。ratchetとは何か、ゲートをどう掛けるか、cheeseをどう塞ぐか。記事一本をエージェントに渡せばcobraベースのGo製CLIが出てくる。

だが二つ目のQuest CLIを建てると何が起きるか。同じ一方向ステートマシンをまた書く。同じscan/next/submit/status/exportをまた書く。同じPASSロック、同じremainingの単調減少、同じJSONL exportをまた書く。変わるのはゲート一つだけなのに、毎回残り全部を書き直す。 これがクエストを一つ建てるたびに課されるボイラープレート税だ。

パターンは再利用できた。コードはできなかった。reinsはその間隙を閉じる。

何が不変で何がドメインか

Quest CLIを二つ重ねて差分(差分)を見ると、境界が鮮明だ。

不変 (すべてのクエストが共有)        ドメイン (クエストごとに異なる)
─────────────────────────       ─────────────────────
ratchet: TODO→PASS 不可逆          何が一つのクエストか
コマンド骨格: scan/next/submit…    何が「事実」か
レベル集計: Fail/Review→verdict   どのcheeseを塞ぐか
進行の永続・resumable
export: 一度きりの放出

左側はhow-make-questが証明したそのままだ — ドメインが会社名でもエンドポイントでも関数でも、ratchetの歯車は同じように噛む。右側だけを人が知っている。reinsは左側をフレームワークとして供給し、あなたには右側だけを残す。

これは新しい主張ではなく、reinsがコードで強制する古い原則だ — 決定と実装の分離。 ゲートは決定(このドメインで何が真か)であり、ratchet・CLI・集計は実装だ。実装を毎回書き直すのは、決定を実装に縛りつける失敗である。

ゲート一つだけを実装する

reinsでクエストを作るとは、インターフェース一つの四つのメソッドを埋める作業だ。

type Definition interface {
    Seed(args []string) ([]*quest.Item, error)            // 入力 → 初期 TODO シード
    Render(it *quest.Item) (string, error)                // next が見せる作成プロンプト + 検証コンテキスト
    Prepare(it *quest.Item, raw []byte) (gate.Context, *quest.Verdict, error) // 提出デコード
    Rules() []gate.Rule                                   // ゲート違反ルールのカタログ
}

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

main一行がratchet・六つのコマンド・集計・export・resumableセッションを全部供給する。あなたが書いたのはドメインの四つのピースだけだ。エージェントは相変わらずコマンド二つだけ知っていればよい — nextで受け取りsubmitで出す。残りは機械が決める。

ゲートはcheese防御ルールのカタログだ

how-make-questの核心は「cheese不可能なゲートを設計せよ」だった。reinsはその設計をデータ構造にする — ゲート = ルールカタログ。 ルール一つが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を見つけるたびにルールを一つ追加すれば、ゲートはその分だけ堅くなる。そしてカタログは自分自身を文書化する — rulesコマンドがルール一覧を出力すれば、それがそのまま「私が塞いでいるcheeseの監査リスト」だ。何を塞いでいるか分からないゲートは存在しない。

深刻度は重みではなくレベルだ。Fail一つあれば即FAIL。決定的な違反は交渉されない — 99点の違反九つで一つのFailを覆い隠せない。Evaluateは発動したルールをレベルで集計する: 一つでもFailならFAIL、そうでなくReviewがあればREVIEW、全部通過ならPASS。

権限の非対称性を型で強制する

how-make-questで最も重要な一行は「PASSロックは機械だけ」だった。reinsはこれを規約ではなくで打ち込む。

L1 機械(決定論)   PASSをロックする唯一の権限
L2 AI(懐疑者)     REVIEWだけ — 疑いを提起するが完了を授与できない
L3 人間           両方が見逃した残余

機械ゲートはPASSを出す。AI検証器をゲートに入れても、それができる最大はREVIEWに回すことだ。間違ったことを最初から不可能にする — フレームワークがAIにPASS権限を与えるAPIを提供しなければ、誤って酔った友人に判定を任せることもできない。

二つ目のバックエンド — defeatグラフ

独立したルールたちのレベル集計で十分なゲートは多い。だがルールたちが互いに競合し始めると — 「この違反はあの違反があるときだけ意味を持つ」「この失敗の根本原因は実はあれだ」 — 手書きのif-elseガードがゲートを侵食する。弱いゲートが壊れる場所ではなく、複雑なゲートが腐る場所だ。

reinsの二つ目のゲートバックエンドはこの競合を宣言的グラフに移す — toulmin h-Categoriser. トゥールミン論証モデルがそのままデータ構造になる:

  • Warrant — tautology PASS。「反論がなければ通過」という根拠。
  • Counter — 違反がwarrantを攻撃する。
  • Supersedes — ルール間の優先順位。どの反論がどの反論に勝つか。

手書きのガード節がAttacksSupersedesエッジへ蒸発する。そしてエッジが0なら、このグラフはレベル集計と正確に同値だ — 複雑性は必要なときだけ点く(opt-in)コストである(Definitiongate.Evaluatorを実装すれば点く)。

グラフがくれる本当の贈り物は判定ではなくフィードバックだ。グラフ評価はエージェントに直通の攻略集を返す — Verdict.Feedback: 「なぜ負けたか、そして何を変えれば勝つか。」 単なる「FAIL」ではなく、論証の構造から計算された根本原因だ。

ここでhow-make-questの逆説が再び作動する。モデルはおべっかを言う — 指示に素直に従う。意見にはおべっかが毒だが、事実にはおべっかが資産だ。 攻略集は意見(「ちょっと変だけど」)ではなく事実(「who.anchorsが原文にない、これを変えろ」)だ。おべっかするモデルほどその事実を素直に受け入れて収束する。決定論的グラフ + おべっかするLLM = 収束が保証されるループ。

副作用は隔離する — groundとstaged評価

ゲートが決定論的であるためには、ネットワークがゲートの中にあってはならない。net/httpを直接呼ぶルールは単体テストが不可能で、判定が回線事情によって揺れる。

reinsは副作用をpkg/groundに追い込む — HTTPBodyMXResolvesのような原始演算が注入型Resolverとリクエストごとのスナップショットで外部照会を所有する。ルールは純粋なまま残り、外部世界はgroundが責任を負う。

そしてstaged評価: 安い検査が先に回り、それが失敗すればネットワークfetchはそもそも起きない。形式が間違った提出にDNSを照会する理由はない。高くて揺れるものを、安くて確実なものの後ろに立てる。

N=1抽象化禁止

reinsの規約の一つが、このフレームワークの性格を最も正確に表す — 一つの消費者から抽象を抜くな。 新しい抽象は二つ目の消費者で検証した後にだけ凍結する。

これは気難しさではなく第一原理だ。一つの事例から抜いた抽象は、その事例の偶然を本質と取り違える。二つ目のドメインが同じ抽象を要求したときにはじめて、それが不変であると証明される。フレームワークが自分自身の進化にまで「主張ではなく検証」を適用するのだ。ゲートがエージェントの主張を信じないように、抽象は一つの事例の主張を信じない。

同じ一文が、ライブラリになる

reinsはpkg/の七つのパッケージで立つ — textmatch(幻覚遮断の原始演算)、temporal(時間正規化)、quest(ratchetコア)、gate(ゲート契約)、graph(defeatグラフ)、ground(ネットワーク隔離)、cli(cobraスキャフォールド)。go buildgo test通過、全関数カバー。そしてtoulminはグラフバックエンドにだけ一方向に結合されており、グラフを使わない消費者はtoulminをリンクすらしない。

コード: github.com/park-jun-woo/reins

how-make-questが一つの文だったとすれば — 生成は確率的でいい、検証は決定論的でなければならない — reinsはその文をコンパイル可能な形に固めたものだ。ゲートはドメインの事実を再検証し、ratchetは通過したものをロックし、グラフは負けた理由を事実で返し、おべっかするモデルはその事実に順応する。

次にQuest CLIが必要になったら、ratchetを書き直すな。ドメインのゲートだけ書いて、手綱(reins)は借りろ。


一緒に読みたいもの

reinsがコードで固めた原則 — 生成は確率的、検証は決定論的 — は、reinsだけの発見ではない。互いに知らない人々が同じ壁にぶつかり、同じ結論に到達した。how-make-questが集めた独立収束プロジェクトたちがその証拠だ。

  • episteme — 取り返しのつかない作業の前にReasoning Surfaceを強制。reinsのratchetと同じ直観 — PASSはロックする前に検証する。
  • MagLab — 「LLMは推論だけ、数字は決定論的ツールが」。reinsが副作用をpkg/groundに隔離するのと同じ分離。
  • Manifesto — 「Agent proposes, World verifies.」 reinsの権限の非対称性(L1だけがPASSをロックする)を一文に要約する。
  • oh-my-kamisama — 「diffs beat claims.」 ゲートがエージェントの主張ではなく事実を再検証するのと同じ原則。

そしてdefeatグラフバックエンドの根は論証理論だ — 下記の出典のToulmin・Dung・Amgoud系列。reinsのpkg/graphは、その60年を超える形式論理をGoのデータ構造に移したものだ。


出典

  • Toulmin, S. (1958). The Uses of Argument. Cambridge University Press. — defeatグラフのWarrant・Ground・Backingがそのまま取られた論証モデル。
  • 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. — pkg/graphが採用したweighted h-Categoriser。攻撃を受けたノードが再び防御されると受容度が回復する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グラフの系譜。
  • Gabriel, V.O. et al. (2020). “Reasoning in BDI agents using Toulmin’s argumentation model.” Theoretical Computer Science, 805, 76–91. — トゥールミンモデルをソフトウェアで実装した先行事例(BDIエージェント)。reinsのpkg/graphはこれをゲート判定に移す。
  • 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 — 能力が高いほどゲートの隙間をよりうまく見つける → ゲート=ルールカタログが育たねばならない理由。
  • Thaman, K. (2026). “Reward Hacking Benchmark: Measuring Exploits in LLM Agents with Tool Use.” arXiv:2605.02964 — ゲートを意図的に堅くすればエクスプロイトが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: 初版