用决策模型看住 agent
编码 agent 是这类模型用得最集中的地方,而且集中在三个位置。 它们有一个共同的结构:把「判断」从生成模型里拿出来,交给一个更便宜、 更快的模型去做,代码拿到类型化的答案之后自己决定动作。
一、工具调用之前:要不要放行
最直接的一处。一个 Claude Code 的 PreToolUse 钩子做的事是:
问一个
Noul:这条 shell 命令是不是严格只读的? 到 0.95 才自动放行,否则退回常规的权限提示,而且从不主动拒绝。
三个设计决定值得注意:
- 0.95 很高。 自动放行的门槛比「判断它危险」的门槛要高得多 —— 误放的代价是一台机器,误拦的代价是用户多点一次确认。不对称的代价要反映在不 对称的阈值上。
- 从不 deny。 它只做「自动批准」这一件事,其余全部交还给原系统。 一个只增加能力的门比一个会夺走能力的门好测 —— 前者最坏情况是没帮上忙。
- 危险命令根本不进模型。 本地有一份 hard-no 清单和一个注入过滤器, 在调用之前就把它们挡掉。这一条比阈值重要,理由见什么必须写在代码里。
实测数据:8 条会改变状态的命令,0 条被自动放行。
二、agent 想收尾的时候:做完了吗
另一个高频位置。一个 Stop 钩子用决策模型判断 agent 是否过早收工, 办法是拿自然语言写的完成规则去问它。
更克制的版本是这样的:
只在「有文件被改动过、且此后没有任何通过的检查」时,才花一次四个问题的调用; 任何错误都放行(fail-open)。
「只在值得问的时候才问」是这一类的关键 —— 这些钩子在每一轮都会跑, 无条件调用会把成本乘以轮数。上面的条件是「有未验证的改动」, 那正是最容易出现「agent 说自己做完了但其实没验证」的时刻。
同类做法还有一个防止过早结束的钩子,它把「完成规则」写成自然语言, 让模型判断是否满足,而不是靠模型自己声明。
三、上下文快满的时候:哪些还留着
第三处是上下文压缩 —— 这里的做法分歧最大,也最有意思。
传统的压缩是摘要:把一段对话交给模型,让它压成一段文字。代价是 原文没了,而摘要是不可逆的。
用决策模型的做法不同,它只打分,不改写:
| 做法 | 保留什么 |
|---|---|
| 逐条给工具调用与结果打「还需要吗」的分 | 判定要留的行逐字保留 |
低分的段移到 store,原位挂一个 expand() 指针 |
没有删除,只是折叠 |
| append-only 的冻结前缀 | prompt cache 不被打断 |
| 在模型看到之前就裁掉冗长的 Bash 输出 | 终端噪音不进窗口 |
共同点是同一句:决策模型在这里的作用是「保留 / 不保留」的二值判断, 而不是「改写」。这恰好是它能做好而生成模型不擅长的事 —— 而「不删除、只折叠」这个选择,把判断错误的代价从「信息永久丢失」 降成了「多一次展开」。
反面:安全门和效率门要用相反的兜底
这一片生态里最值得对照的一组数据:同样是「出错怎么办」, 不同项目选了相反的答案,而两个都对。
- 一个权限判定器:出错或超时就拒绝。
- 一个 Stop 钩子:任何错误都放行。
判据不是「哪个更安全」,而是这个门挡的是风险还是效率。 挡风险的(权限、密钥、危险命令)错杀好过漏放;挡效率的(收尾检查、 格式检查)漏放好过卡死流程。
一句话
agent 是这类模型最自然的落点,因为它需要大量的廉价判断, 而且这些判断的结果马上要被代码消费。但每一处都要先问清楚: 这个门出错时应该往哪边倒。