第 24 章:Harness Engineering——把决策织进智能体骨架
第 24 章:Harness Engineering——把决策织进智能体骨架
一个 agent 之所以能自己跑下去,靠的是一圈循环里反复做出的判断:下一步做什么、调哪个工具、这个结果可不可信、该不该停、要不要叫人。把这些判断从模型的自由生成里取出来,交给决策层,agent 的骨架就变得可控、可测、可审计。本章讲决策点该插在哪、工具权限怎么用 Noul 管,以及把决策层做成 first-class 集成到底意味着什么。
Agent 循环里的决策点
先看一个通用的 agent 循环,标出该插判定的位置:
flowchart TD
O["观察:当前状态 / 上一步结果"] --> Q1{"下一步做什么?
Choice"}
Q1 --> Q2{"调哪个工具 / 哪个目标?
Choice"}
Q2 --> G{"这个操作允许吗?
Noul(权限闸门)"}
G -- 允许 --> ACT["执行工具"]
G -- 需确认 --> HUMAN["转人工确认"]
ACT --> V{"结果可信吗?
Noul"}
V -- 可信 --> M["写入上下文"]
V -- 不可信 --> RETRY["重试 / 丢弃"]
M --> S{"该停了吗?
Noul"}
S -- 继续 --> Q1
S -- 停 --> DONE["产出结果 / 结束"]五个决策点对应五种问题:下一步动作、工具选择、操作权限、结果可信度、停止判断。它们的共同特征是——答案封闭、能被代码接住、需要被大量重复。这正是第 3 章那三个判据的落地:规则写不动、输出封闭、会被反复调用。
工具选择与权限:把"是否允许"变成 Noul
Agent 最危险的一步是"决定去执行某个操作"。把这一步的授权交给生成模型,等于让它一边生成一边自我批准。更稳的做法是把它拆成一个 Noul 权限闸门,并且硬规则优先于模型。
jev-auto-approve 给出的正是这个模式:它作为 Claude Code 的 PreToolUse 钩子,问 Jev 一个 Noul——"这条 shell 命令是不是严格只读",在 0.95 以上自动放行,否则回落到正常权限确认,从不直接拒绝;同时用一个本地 hard-no 列表和注入过滤器,把危险命令挡在进入 Jev 之前。它公布的校准里,8 条会改变状态的命令一条都没被放行。
READ_ONLY_SAFE = 0.95
def guard_command(cmd: str) -> str:
# 硬规则先跑:明显危险的根本不进模型
if any(pat in cmd for pat in HARD_NO):
return "deny"
d = client.systemone(
state=cmd,
questions={"read_only": {"type": "noul",
"question": "这条 shell 命令是不是严格只读、不会改变任何文件或系统状态?"}},
instructions="只有确定只读才判真;任何写入、删除、网络副作用都判假。",
)
if d["read_only"].value and d["read_only"].probability >= READ_ONLY_SAFE:
return "allow"
return "ask" # 回落到正常权限确认,绝不光凭概率直接 deny三个设计要点值得抄:硬规则前置(危险命令连模型都不进,节省调用也避免被绕过);阈值由代码定(0.95 写在代码里,不写在提示词里);失败不越权(不确定时回落到保守的人工确认,而不是把决定权让给模型)。工具选择本身也是一次 Choice——从当前可用工具的候选集里选一个,类似于 pi-typesafe-router、decide-mcp 那样把工具路由做成类型化判定。
置信度不是权限
权限闸门有一个容易踩的误解:以为置信度够高就等于获得授权。tenuo.ai 那篇 "typed decisions, scoped authority" 把它讲清楚了——一个置信值只能授权它作用域本来就允许的动作。换句话说,概率回答的是"这个判断有多确定",不是"这个操作该不该被允许归你管"。两者必须分开:作用域(哪些动作在原理上允许、由谁授权)是代码和策略层的事,跑在模型之外;置信度只在作用域已经圈定的候选里做取舍。JevLoop 的规则正是这个原则的极端版本——高风险分数强制触发人工授权,任何概率都无法覆盖它。所以闸门的正确顺序是:先用代码划出作用域,再让模型在作用域内按概率排序;一个 0.999 的"只读"判断,也救不回一条本就落在禁用作用域里的命令。
停止条件与预算控制
Agent 最常见的两个失败模式是"停不下来"和"停太早"。前者烧钱,后者交半成品。把停止判断做成显式的决策点,并且让代码持有最终否决权。
MAX_ROUNDS, BUDGET = 12, 0.50
def should_stop(evidence: str, rounds: int, cost: float) -> bool:
if rounds >= MAX_ROUNDS or cost >= BUDGET:
return True # 硬预算由代码持有,模型无权推翻
d = client.systemone(
state=evidence,
questions={"enough": {"type": "noul",
"question": "基于已积累的证据,是否已经足以回答最初的问题?"}},
)
return d["enough"].value and d["enough"].probability >= 0.8DeepSearcher 的停止策略实验就是这个思路的公开验证:用 Jev 的 Noul 判断所累积的证据是否足以支撑停下,还是要在检索轮次预算内继续。更接近"反方向停止"的是 wakegate:在休眠中的 agent 被定时器或事件唤醒之前,Jev 对着 agent 自己的睡眠笔记回答一个 Choice(唤醒 / 还不到 / 无关),代码只在"唤醒"概率低于 0.2 时才跳过这次唤醒,同时用户消息、裸定时器、跳过上限、错误和超时一律强制唤醒;它的一次运行通过了21 个手写场景(README 自己说这是冒烟测试而非基准)。这两个例子的共同点是:模型给建议,代码下最终判断,而且这个最终判断有明确的、可审计的兜底条件。
多智能体协作中的路由
多 agent 系统里,"这条任务交给谁"天然是一个 Choice。Eliza 就是这样一个多智能体框架,集成 TypeSafe System One 决策服务做亚 100 毫秒的意图分类、动作派发和置信度门控的工具执行。更轻量的还有 hono-jev-router:它是一个 Hono 中间件,按语义而不是按方法和路径路由 HTTP 请求,判定由 Jev 完成。pi-typesafe-router 则把 Pi 的工作按类型化决策路由。它们的价值在于把"分发"这件事从 if-else 里解放出来——路由规则不再是一堆字符串匹配,而是一次带概率的判定,答案可记、可测。
详解一:AutoGPT 的一等决策块
AutoGPT 是自主智能体平台的代表项目,它集成了一等的 TypeSafe Jev 决策块,用于类型化路由、过滤、打分,以及置信度门控的下一步动作派发。
这个"一等(first-class)"是关键。当决策只是提示词里的一句话时,它不被工程看见、无法测试、不可审计;当它是节点图里的一个决策块时,它就有了输入、输出、概率和可断言的行为。AutoGPT 的选择说明:决策被当成积木,而不是被当成指令。
详解二:Pydantic AI 的 TypeSafeModel
Pydantic AI 是官方 agent 框架,它提供一等的 TypeSafeModel 集成,把 Pydantic schema 的字段映射成类型化的 Jev System One 问题,并返回带置信度的答案。
这个映射很优雅:你在 Python 里本来就用 Pydantic 类型描述数据,现在同一套类型可以直接变成"问题集"。字段名成为问题、字段的枚举成为 Choice 的选项、布尔字段成为 Noul。你不需要为决策层另学一套建模语言——你已有的类型系统就是问题定义。
详解三:mu 的 38 个决策点
如果只读一个项目来理解"Harness Engineering",建议读 mu。它是一个基于 pi 的编码 agent 和桌面应用,在循环里设置了38 个决策点,问 Jev 的 Noul、Choice、Score。这些决策点具体到令人信服:
- 长工具输出的哪些片段该进入上下文;
- 一条被规则标记的命令,是不是用户真的要求执行的;
- 抓取到的网页或 MCP 结果里,是否夹带着瞄准模型的指令(prompt injection);
- 一个"完成了"到底有没有被验证过。
而且每个点都可以设为 active / shadow / off,每条裁决都写进本地 ledger。在它仓库的重放基准里,用 Jev 做的目标感知式测试日志选择,砍掉了 40–46% 的日志而没有丢掉任何必需的行。
这个项目的意义是示范了 harness 工程的全部要素:决策点被逐点列出、逐点开关、逐点记账。 决策不是散在代码各处的临时调用,而是被显式枚举、可开关、可回放的一套基础设施。
把决策层做成 first-class 集成
"决策"能不能成为框架里语言级别的东西,决定了它好不好用。2026 年这一批集成给出了肯定答案:
eve(Vercel)的引擎把 Jev 作为默认评测模型(typesafe-ai/jev)内置在它的 evaluate 路径里;Sim 是一个构建与监控 agent 的协作工作台,原生集成 TypeSafe System One 评测与决策 provider;RubyLLM 提供了官方的 Ruby gem,用原生 System One 协议承载类型化问题、概率答案和错误归一化;jev-mcp 把十一个 Jev 判断工具(verify、screen、noul、find、rerank、classify、decide、compare、extract、review、gate)做成 MCP 服务器,配 fail-closed 处理;官方还开源了 system-one-adapter-python,作为跑在 OpenAI / Anthropic 兼容 API 之上的 drop-in 适配器;Rust 侧有类型化的 typesafe-ai 客户端。
JevLoop(zjunlp)则把"一流集成"和"代码掌握控制权"合在一起:Choice 从每一步重建的候选工具里挑下一个工具,Score 给这次调用评级风险,Noul 决定它是否需要授权,而由普通代码来执行答案——一个高风险分数会强制触发人工授权,任何概率都无法覆盖它。它的判定在离线 judge 下只占 7.7% 的墙钟时间(走托管 API 时是 79%)。
这里的信号很清楚:当决策成为一个和 generate、stream 并列的推理类型(如 neurolink 里的 decide),而不是一条特殊提示词时,agent 骨架才算真正把决策织了进去。
落到生产的清单
把上面所有东西收成一份可勾选的清单:
- 决策点显式枚举。 像 mu 那样逐点列出,而不是散落在代码里;先支持 shadow 模式跑一段,再决定开关。
- 阈值写进代码。 0.8、0.95 这些数字属于代码,不属于提示词。
- 硬规则前置。 危险的、确定能判的先在模型之外处理,别让模型做它做不好的事。
- 失败策略逐点决定。 权限闸门 fail-closed(不确定就不放行),上下文压缩、日志剪裁这类 fail-open(出错就保留全部)。别用一个默认策略套所有点。
- 每个判定留 ledger。 记录 state、问题、概率、模型版本,方便审计、回放和漂移监控(第 23 章的回归测试就建在这上面)。
- 预算硬上限由代码持有。 轮次、成本、超时,模型无权推翻。
- 低置信有明确降级路径。 不是"随便猜一个",而是转人工或升级给更大的模型。
这份清单的本质,是让 agent 的每一处判断都可解释、可回滚、可监控——也就是"可推理"。
回到那句话
全书从一段几乎所有 AI 应用都写过的代码出发:让生成模型判断"这条评论是垃圾吗",再字符串匹配它的回复。我们花了二十三章,把那个判断从自由文本里剥出来,装进一个类型化的槽位,最终讲成了这本书的论点——
代码拥有控制流,模型只做语义判定。
这句话不是一句口号,它在这三章里有了具体的形状。第 22 章里,它是 Map Reduce 的分块、映射、聚合:并行和聚合的逻辑在代码里,模型只管填每一块的标签,于是 2,300 篇论文能在 83 秒、14 美分里判完。第 23 章里,它是回归门和护栏:锁版本、对概率做区间断言、监控漂移、按阈值分流,模型给概率,代码下判断,于是 97.5% 的核实准确率背后还有可复现的阈值曲线。第 24 章里,它是 agent 骨架的 38 个决策点:模型对"这条命令只读吗"给出 0.95 的把握,代码决定放行还是转人工;模型对"够了吗"给出答案,代码还持有预算和一票否决。
统一的道理始终只有一条:语义判定是模型最擅长也最该做的事,控制流是软件最擅长也最该保有的东西。 把这两者各自归位,判断就变得便宜、快速、可验证——软件没有变简单,而是变得可推理了。
这就是"一个特别聪明的 switch 语句"真正值钱的地方。
小结
- Agent 循环里至少有五个该插判定的位置:下一步动作、工具选择、操作权限、结果可信度、停止判断。
- 工具权限用 Noul 闸门管理,并坚持三条纪律:硬规则前置、阈值写在代码里、不确定时回落到保守而非把决定权交给模型。
- 停止与预算由代码持有最终否决权(DeepSearcher 的停止策略、wakegate 的 21/21 场景都体现了这一点)。
- mu 的 38 个决策点示范了 harness 工程的全部要素:逐点枚举、active/shadow/off 开关、逐点记 ledger。
- first-class 集成意味着决策成为 generate/stream 并列的推理类型:AutoGPT 的决策块、Pydantic AI 的 TypeSafeModel、eve 的默认评测模型、jev-mcp 与官方 system-one-adapter-python。
- 从生产清单回到全书论点:代码拥有控制流,模型只做语义判定——判断变便宜、变快、可验证,软件因此变得可推理。