治理约 20 分钟
终关:三道门守住交期承诺
把状态门、校验门、授权门装到同一个智能体上,用一套配置同时守住过期库存、写错数量、超出授权三种情况,而且不误伤、不滥用人工。
依据 agent-core 0.1.20 · develop@fbe6a0d9(2026-11-13)核实
回到这张询单
客户问:"500 件,最快什么时候能交?"智能体查库存、算交期、写订单、答复客户。一路走来,你已经见过它可能出错的三种方式:
| 风险 | 后果 |
|---|---|
| 库存快照过期,期间被别的订单占了 | 承诺了交不出的货 |
| 模型写订单时把数字写错 | 订单和计算结果对不上 |
| 承诺超出智能体的权限 | 没人批准的承诺进了系统 |
这一关把三道门装到同一个智能体上。
三道门
| 门 | 问的问题 | 判断依据从哪来 | 建议的位置 | 没通过时 |
|---|---|---|---|---|
| 状态门 | 智能体看到的现实还可信吗? | 数据的时间戳、来源、有效期 | 取数之后,尽早 | 引导它重新取数 |
| 校验门 | 要做的动作和确定性计算一致吗? | 计算服务的结果、业务规则 | 写入之前 | 拒绝本次调用,让它按结果重来 |
| 授权门 | 这件事它有权决定吗? | 岗位权限、金额或时间阈值 | 写入之前 | 挂起转人工,批准后继续 |
三道门的共同点:判断依据来自确定的业务数据,不让模型自己判断自己。 Rail 只提供拦截的位置,规则要由你的业务系统给。
说明
三道门是本站对企业治理做法的通用概括,不是 openJiuwen 里的现成组件;实现它们用的是 agent-core 的 Rail。阈值、数量、日期都是示例。
时机:三条从源码里来的规则
配置三道门时最容易错的是时机。ReAct 主循环每一轮是这样走的:
吸收排队的引导 → 调模型(含 AFTER_MODEL_CALL)→ 有 force_finish 就结束
→ 执行本轮工具(含 BEFORE / AFTER_TOOL_CALL)→ 下一轮
源码ReAct 主循环agent-core/openjiuwen/core/single_agent/agents/react_agent.py:3185@fbe6a0d9
由此得出三条规则:
- 引导晚一拍。
push_steering要到下一次模型调用前才生效。在AFTER_MODEL_CALL里引导,拦不住模型这一轮刚提出的工具调用。 - 模型调用后的 force_finish 能挡住本轮工具。 模型调用结束后循环先检查结束请求,有就直接退出,本轮工具不执行。代价是这次调用没有结果。
- reject 和 interrupt 只在工具执行前。 它们由中断类 Rail(
BaseInterruptRail)提供,挂在别的钩子上不起作用。
还有一条关于顺序:同一个钩子上的多个 Rail 按 priority 从高到低执行。把状态门、校验门的优先级设得比授权门高,经理审批时看到的就是已经纠正过的数字。
动手:一套配置,四个场景
下面的模拟器要求你只配一次,然后同时通过四个场景。评判标准有三条:
- 错单零写入:过期库存算出的交期、写错的数量、没批准的承诺,一单都不能进订单系统
- 不滥用人工:智能体能自己纠正的问题,不要叫经理
- 不丢生意:拦下之后,这张询单最终还要能正确下单
模拟器加载中…
装上之后还要防绕过
Rail 运行在智能体进程里。门装得再好,如果智能体能绕开它直接写数据库,就形同虚设。前面几关里已经出现过相应的做法:
- 护栏 fail-closed 安装:配置不对就拒绝启动,而不是"看起来装了"(agent-solution·护栏、意图与 react-rails)
- 写操作只走受控工具:业务系统的本地工具默认不暴露,有副作用的默认要审批(agent-solution·agent-client)
- 入口幂等:同一张询单重发不会写出两笔订单(agent-solution·agent-gateway)
- 审计:每次拦截都记下是谁、为什么,供事后追查(agent-runtime·多租户与请求上下文)
小结
- 状态门问"现实可信吗",校验门问"动作对吗",授权门问"有权吗",依据都来自确定的业务数据。
- 时机决定成败:引导晚一拍,reject 和 interrupt 只在工具执行前。
- 好的配置既不放过错单,也不滥用人工、不丢生意。
完整的治理全景,见导航栏里的"治理"页。
源码出处
- agent-core/openjiuwen/core/single_agent/agents/react_agent.py:3185
steering = await self._drain_steering_batch(ctx)每轮开头吸收引导,再调模型 - agent-core/openjiuwen/core/single_agent/agents/react_agent.py:3199
ai_message = await self._call_model(模型调用后先看 force_finish,有就不执行本轮工具 - agent-core/openjiuwen/core/single_agent/agents/react_agent.py:3276
results = await self._execute_tool_call(ctx, ai_message.tool_calls执行本轮工具 - agent-core/openjiuwen/core/single_agent/rail/base.py:895
class AgentRailpriority 越大越先执行 - agent-core/openjiuwen/harness/rails/interrupt/interrupt_base.py:46
class BaseInterruptRailreject / interrupt / approve 只在工具执行前
链接指向固定提交,仓库后续更新不影响这里的行号。
过关测验
1. 在 AFTER_MODEL_CALL 里发现模型要写的数量不对,调用 push_steering 提醒它。这次写订单会被拦住吗?
2. 超出授权的承诺,授权门应该怎么处理?
3. 三道门挂在同一个钩子上时,为什么状态门要排在授权门前面?