验证门与观察预算 本节摘要:没有验证层的外壳就是套着风衣的许愿。本节建确定性门链,决定:工具调用是否允许触发、Agent 能看到多少它的输出、循环何时因「读太多」而停。门链是若干小而命名的门加一本「观察账本」(跟踪给模型看过的每个 token)的函数。真实使用头一小时会出现三类 bug:无界观察(grep 一个 20 万行仓库倒 50 万 token)、陈旧近因(第 47 轮编辑从不出现在第 3 轮读之后)、权限蔓延(研究任务从 websearch 蔓延到 shell)。验证门就是说「不」的外壳组件——它不是模型、不是评委,是 的确定性函数,返回 ALLOW 或 DENY 加理由。 对应原课程:Phase 19 · Lesson 25 · (原英文 )。
本节摘要:没有验证层的外壳就是套着风衣的许愿。本节建确定性门链,决定:工具调用是否允许触发、Agent 能看到多少它的输出、循环何时因「读太多」而停。门链是若干小而命名的门加一本「观察账本」(跟踪给模型看过的每个 token)的函数。真实使用头一小时会出现三类 bug:无界观察(grep 一个 20 万行仓库倒 50 万 token)、陈旧近因(第 47 轮编辑从不出现在第 3 轮读之后)、权限蔓延(研究任务从 web_search 蔓延到 shell)。验证门就是说「不」的外壳组件——它不是模型、不是评委,是
(call, history, ledger)的确定性函数,返回 ALLOW 或 DENY 加理由。
对应原课程:Phase 19 · Lesson 25 ·
verification-gates-observation-budget(原英文phases/19-capstone-projects/25-verification-gates-observation-budget/docs/en.md)。本节属「Agent Harness 深度构建赛道」第六节。
阅读完本节,你应当能够:
evaluate(call) 方法的 VerificationGate 协议。ObservationLedger 跟踪每次观察。GateDecision 记录。外壳让模型自由调工具时,真实使用头一小时出现三类 bug。
第一,无界观察。一个跨 20 万行仓库的 grep 把 50 万 token 输出倒进下一轮。模型每 KB 看一条匹配,上下文其余浪费了。token 账单大,Agent 在任务上反而更差。第二,陈旧近因。长跑任务攒 50 个工具调用,模型把第 3 轮的 read_file 当活状态重读,第 47 轮的编辑从不出场。第三,权限蔓延。研究任务从 web_search 开始,不知怎么跑起了 shell,因为模型发明了工具名而外壳默认宽松。等读 trace 时,/tmp 里躺了个垃圾文件,一个 curl 跑去了私有 API。
验证门就是说「不」的组件。它不是模型、不是评委,是 (call, history, ledger) 的确定性函数,返回 ALLOW 或 DENY 加理由。理由被记、被告诉模型、循环继续或中止。
门是任何有 evaluate(call, ctx) -> GateDecision 方法的东西。链是有序列表,首个 deny 短路。顺序要紧:便宜的结构门先跑,贵的数 token 门后跑。
四个门:
rm -rf 的 shell 调用、拦去内网 IP 的 HTTP 调用。纯在调用 payload 上。观察账本(把账):
class ObservationLedger: def record(self, turn, tool, tokens): ... # 每成功调用写一行 def cumulative(self) -> int: ... # 模型总共看了多少 def per_tool(self, name) -> int: ... # 模型对工具 X 看了多少
账本回答两个问题:模型总共看了多少、对工具 X 看了多少。预算门读第一个,按工具预算门(练习)读第二个。
deny 比 allow 便宜。白名单门 O(1) 哈希查;正则门 O(pattern × argv);近因门读消息存储小切片;预算门读整本账本。按成本升序排,被拒调用在做贵活前短路。也按影响半径排:白名单是最强主张(这工具不在契约里);正则其次(这参数不在契约里);近因再后(外壳还在乎但调用结构合法);预算最后(按定义只在前全过后才触发)。
实现是单个 main.py 加测试。Observation 与 ToolCall 数据类定义线形状;ObservationLedger 记 (turn, tool, tokens) 行并答 cumulative() 与 per_tool(name);GateDecision 带 (allow, reason, gate_name);VerificationGate 是协议,每门实现 evaluate(call, ctx);GateChain 包有序列表,逐门调,首个 deny 返回,全过返回 allow。
demo 跑一个微型合成 Agent 循环:三轮,第三轮触发预算门,循环报告一个干净的拒绝(非零 refusal_count)。token 计数器故意用 len(text) // 4 的蠢启发——本节重点是门管道不是分词器,生产里换真分词器。
前几节给了循环、工具注册表、消息存储、提示构造器、模型路由。本节加模型与工具之间的层。第 26 节发门链说 ALLOW 后分发器把工具调用交去的沙箱;第 27 节发把拒绝计数当质量信号记的评估套件;第 28 节把门决策接到 OpenTelemetry span;第 29 节把全部缝成一个能跑的编码 Agent。
ledger.per_tool(name) 的门,对单工具设上限,超限拒绝。rm -rf 的 shell 调用,确认 DENY 带理由。(call, history, ledger) → ALLOW/DENY + 理由,不是模型不是评委。下一节,我们建「沙箱与拒绝清单」——门链说 ALLOW 后,工具调用实际跑在的隔离环境。