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。**改变的只有那一道门,可每一次都得把其余的全部重写一遍。**这就是每多造一个 quest 就要缴的样板代码税。

模式是可复用的。代码却不是。reins 弥合了这道缝隙。

什么是不变,什么是领域

把两个 Quest CLI 叠在一起看差分,边界就一目了然。

不变 (所有 quest 共享)            领域 (每个 quest 不同)
─────────────────────────       ─────────────────────
ratchet: TODO→PASS 不可逆         什么算一个 quest
命令骨架: scan/next/submit…       什么是"事实"
级别汇总: Fail/Review→verdict     该堵住哪种 cheese
进度持久化·resumable
export: 一次性放出

左边正是 how-make-quest 所证明的——无论领域是公司名、是端点、还是函数,ratchet 的齿都同样卡住。只有右边需要人来知道。reins 把左边作为框架供给,给你留下的只有右边。

这不是什么新主张,而是 reins 用代码强制执行的一条古老原则——**决定与实现的分离。**门是决定(在这个领域里什么为真),ratchet、CLI、汇总是实现。每次都重写实现,是把决定绑死在实现上的失败。

只实现一道门

用 reins 造一个 quest,就是填满一个接口的四个方法。

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)。

// 新闻事件抽取 quest 的一条 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 —— 规则间的优先级。哪个反驳胜过哪个反驳。

手写的守卫子句蒸发成 Attacks·Supersedes 边。而当边为 0 时,这张图与级别汇总完全等价——复杂性是只在需要时才开启(opt-in)的成本(当 Definition 实现 gate.Evaluator 时开启)。

图真正赠予的不是判定,而是反馈。图评估把一份直通的攻略手册返还给智能体——Verdict.Feedback:*“为什么输了,以及改什么就能赢。"*不是简单的"FAIL”,而是从论证结构中计算出来的根本原因。

这里,how-make-quest 的悖论再度运作。模型会谄媚——它会顺从地遵循指令。对意见而言谄媚是毒,但**对事实而言谄媚是资产。**攻略手册不是意见(“好像有点怪”),而是事实(“who.anchors 不在原文里,把这个改掉”)。越是谄媚的模型,越会顺从地接受那个事实并收敛。确定性的图 + 谄媚的 LLM = 收敛得到保证的循环。

隔离副作用 —— ground 与 staged 评估

门要保持确定性,网络就不能待在门里面。直接调用 net/http 的规则无法做单元测试,判定会随线路状况摇摆。

reins 把副作用都赶进 pkg/ground——HTTPBody·MXResolves 这样的原始操作,用注入式的 Resolver 与每次请求的快照来持有外部查询。规则保持纯粹,外部世界由 ground 负责。

而且还有 staged 评估:便宜的检查先跑,它一失败,网络 fetch 根本就不会发生。对格式错误的提交没有理由去查 DNS。把昂贵又摇摆的放在便宜又确定的后面。

禁止 N=1 抽象

reins 的约定之一,最准确地揭示了这个框架的性格——**不要从单一消费者中抽取抽象。**新抽象只有在第二个消费者验证之后才冻结。

这不是吹毛求疵,而是第一性原理。从一个案例中抽取的抽象,会把那个案例的偶然误认为本质。只有当第二个领域要求同一个抽象时,它的不变性才被证明。框架把"不是主张而是验证"连自身的进化也一并适用。正如门不相信智能体的主张,抽象也不相信单一案例的主张。

同一句话,成了库

reins 立于 pkg/ 的七个包之上——textmatch(幻觉拦截原始操作)、temporal(时间归一化)、quest(ratchet 核心)、gate(门契约)、graph(defeat 图)、ground(网络隔离)、cli(cobra 脚手架)。go build·go test 通过,全函数覆盖。而且 toulmin 只单向耦合到图后端,不用图的消费者甚至不会链接 toulmin。

代码: github.com/park-jun-woo/reins

如果说 how-make-quest 是一句话——生成可以是概率性的,验证必须是确定性的——那么 reins 就是把那句话凝固成了可编译的形态。门重新核验领域的事实,ratchet 锁住通过的东西,图把输的理由作为事实返还,谄媚的模型顺从那个事实。

下次再需要 Quest CLI,别重写 ratchet。只写领域的门,缰绳借来就好。


延伸阅读

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 把那套六十多年的形式逻辑搬进了 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 —— 把门刻意做坚固,exploit 减少了 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:初版