Agent 越强,越让人后怕:一个能「自动退款、自动转账、自动改价」的智能体,一旦幻觉发作,造成的损失是实打实的。2026 年 7 月,ActionRail 进入 public beta,它做的是一件很朴素但极关键的事——在工具真正执行前,把动作参数拿去和实时业务数据核对,返回 allow(放行)/ hold(暂扣待人工)/ block(拦截)三种结论。它用确定性规则把「AI 想做」和「业务允许做」隔开。本教程手把手教你把它接进 Python 与 LangGraph 两种 Agent。
先搞懂:为什么需要执行前护栏?
用一句话理解:普通 Agent 是「想做就做」,加了 ActionRail 是「想做先过审」。典型翻车场景:
| 风险动作 | 幻觉可能犯的错 | 护栏该做的事 |
|---|---|---|
| 退款 | 把金额算错、退给错的人 | 核对订单号、余额、退款上限 |
| 转账 | 收款方 / 金额填错 | 比对白名单账户与单笔限额 |
| 改价 | 把价格改成 0 或负数 | 校验价格区间与审批层级 |
永远不要相信 Agent 的「自我约束」。提示词里写「请勿退错」没有任何工程保障。只有把校验做成确定性的、跑在工具调用之前的一层,才算真正的安全护栏。
Step 1:理解 allow / hold / block 三态
allow → 参数合法且在业务阈值内,直接放行执行
hold → 拿不准(金额偏大 / 账户陌生),暂扣,等人工确认
block → 明显违规(超限 / 黑名单 / 字段缺失),直接拦下并报警
设计原则:宁可 hold 一千,不可 block 一个该放的,更不可放过一个错的。
Step 2:安装 ActionRail 并定义第一个 Action Schema
ActionRail 的核心是给每个风险动作写一张 Schema,声明它有哪些参数、各自范围是什么:
pip install actionrail
# refund.schema.yaml
action: refund
params:
order_id: { type: string, required: true }
amount: { type: number, required: true, max: 5000 }
payee: { type: string, required: true }
rules:
- name: amount_within_balance
check: amount <= lookup_balance(order_id)
Schema 写不全 = 护栏有洞。凡是业务上「绝不允许」的情况,都要落成一条 rule。漏掉一条,就少一道闸。
Step 3:写校验规则,和实时业务数据核对
def lookup_balance(order_id):
# 调你的订单系统,拿实时余额 / 状态
return orders.get(order_id).available_refund
def rule_amount_ok(ctx):
if ctx.amount > ctx.max_daily:
return "hold" # 超日限额,暂扣人工
if ctx.payee not in WHITELIST:
return "hold" # 陌生账户,暂扣人工
if ctx.amount > lookup_balance(ctx.order_id):
return "block" # 退超了,直接拦
return "allow"
Step 4:接进 Python Agent(确定性 demo)
from actionrail import Rail
rail = Rail.load("refund.schema.yaml")
def agent_refund(order_id, amount, payee):
verdict = rail.check({"order_id":order_id,
"amount":amount, "payee":payee})
if verdict == "block":
return "❌ 已拦截:参数违规"
if verdict == "hold":
return "⏸ 已暂扣,等待人工审批"
# 只有 allow 才真正执行
return do_real_refund(order_id, amount, payee)
执行语句必须放在 Rail 之后、且只在 allow 分支。这是整条安全链的关键:任何「真正产生副作用」的调用,都不能出现在校验之前。
Step 5:接进 LangGraph(在工具节点前插护栏)
from langgraph.graph import StateGraph
def guard_node(state):
v = rail.check(state["action_args"])
state["verdict"] = v
return state
def tool_node(state):
if state["verdict"] != "allow":
return {"result": f"未执行({state['verdict']})"}
return {"result": run_tool(state["action_args"])}
g = StateGraph()
g.add_node("guard", guard_node)
g.add_node("tool", tool_node)
g.add_edge("guard", "tool") # guard 永远在 tool 之前
app = g.compile()
Step 6:hold 队列与人工确认闭环
1. hold 的动作写入审批队列(数据库 / 工单系统)
2. 运维 / 业务在后台看到「待确认」列表
3. 人工点「批准」→ 重新跑 rail 仍 allow → 执行
4. 人工点「拒绝」→ 记录拒绝原因,闭环归档
hold 不等于安全,闭环才是。如果暂扣的动作永远没人看,那它既没执行也没处理,同样是事故。务必配通知和超时升级。
Step 7:上线前清单
□ 该动作的 Schema 是否覆盖所有必填参数?
□ 是否每条「绝不允许」都落成 rule?
□ 校验是否读实时数据(而非 Agent 自报)?
□ 执行语句是否只在 allow 分支?
□ hold 是否有审批闭环与超时升级?
□ block 是否接了告警(钉钉 / 邮件)?
常见问题速查
| 你遇到的现象 | 大概率原因 & 解决 |
|---|---|
| 所有动作都被 block | Schema 规则过严或实时查询返回异常,先放宽 rule 排查 |
| hold 越积越多 | 阈值设太低,调高日常额度并配审批人力 |
| 护栏没生效 | 执行语句写在 rail.check 之前,调整顺序 |
| AI 绕过参数填假值 | 校验必须查系统真实数据,不要信任入参自证 |