AI Agent 不该拥有无限工具权限:Planlock 计划审批与 MCP 调用实战
当 AI Agent 能调用退款、发邮件、修改脚本等工具时,常见方案只有两种:每次调用都让人点确认,或者让 Agent 在无人值守状态下拥有完整权限。前者很快变成“看到提示就点同意”,后者则把一次提示注入或错误推理变成真实的业务操作。
Planlock 提供了第三种路径:先让 Agent 写出执行计划,由人一次性批准;之后它作为 MCP 代理站在 Agent 与上游工具之间,只放行计划明确允许的调用。它是一个 MIT 许可证的 TypeScript 项目,仓库在 2026 年 10 月 7 日创建,当前仍是非常早期的工具,适合先做实验性验证,不应直接当成成熟的生产审计系统。
核心思路:批准约束,而不是批准一段话
Planlock 的关键不是让模型“保证自己会遵守计划”,而是把约束写进代理代码。一个计划可以规定:某个工具最多调用几次、参数必须等于什么值、数值上下限是多少、当前步骤允许哪些工具,以及上游脚本是否仍然是审批时的版本。
例如,一个客服 Agent 处理退款时,可以把计划拆成“查询订单→最多退款 30 欧元→发送确认邮件→关闭工单”。如果 Agent 尝试退款 300 欧元、换一个订单,或者在退款前先发邮件,代理会在请求到达 Windmill 等上游 MCP 服务之前拒绝调用。
这种设计还把“工具版本变化”纳入边界。README 展示的 pin 约束会在写操作前重新读取上游脚本的哈希;如果同事在审批之后修改了退款脚本,当前计划会进入 Hold,而不是继续执行未知版本的代码。
一次调用是怎样被拦截的
计划不是只保存自然语言描述,而是保存每一步的工具和参数限制。下面是官方 README 展示的约束结构的简化版本:
{
"s-f_support_refund__customer": {
"max_calls": 1,
"args": {
"order_id": { "eq": "4711" },
"amount": { "min": 1, "max": 30 }
},
"pin": {
"tool": "getScriptByPath",
"args": { "path": "f/support/refund_customer" },
"field": "hash",
"eq": "APPROVED_SCRIPT_HASH"
}
}
}
实现层支持 eq、enum、min、max、max_length 和 any 等约束;未声明的参数也会被拒绝。调用失败、被 pin 拦截或跳过步骤,都可能让计划暂停。处于 Hold 时,Agent 本身不能自我批准,必须通过独立的审批通道由人处理。
这比在系统提示词中写“不要退款超过 30 欧元”更可靠,因为规则位于工具调用链路上。即便 Agent 读到了恶意文本,最多也只能尝试调用;越过参数、步骤或版本边界的请求不会抵达业务系统。
用 Docker 跑通官方演示
仓库提供了基于 Windmill 的退款示例。官方要求 Docker 和 Node.js 20+,在项目目录中可以按下面的流程启动:
npm ci cp .env.example .env # 在 .env 中设置 APPROVER_TOKEN docker compose --env-file .env -p wm \ -f examples/windmill/docker-compose.yml up -d node examples/windmill/setup.mjs docker compose --env-file .env -p wm \ -f examples/windmill/docker-compose.yml \ --profile guard up -d --build planlock node examples/windmill/demo.mjs
演示会先尝试一个超出上限的退款,再执行批准范围内的退款,然后模拟同事修改上游脚本。后一个调用会因为哈希不一致而被阻断,计划进入 Hold。测试结束后可以清理环境:
docker compose --env-file .env -p wm \ -f examples/windmill/docker-compose.yml \ --profile guard down -v
如果只想测试代理本身,也可以使用仓库自带的 mock upstream:docker compose up --build,然后运行 npm run smoke。MCP 服务和审批通道在 README 给出的示例中分别监听本机的 3001 和 4001 端口;不要把这些开发端口直接暴露到公网。
它适合什么场景
Planlock 最适合“动作风险高,但计划边界相对清晰”的任务:客服退款、工单状态流转、受限的运维脚本、带金额或对象范围的业务操作。它也适合作为现有 MCP 服务的旁路保护层,不必重写每个上游工具。
但它不是万能沙箱。官方威胁模型明确指出:如果 Agent 同时能在宿主机执行命令、读取审批令牌或修改相关文件,这些保证就不成立;pin 检查与真正调用之间也存在竞态窗口。此外,示例状态默认保存在内存中,并且一个服务实例只处理一个 Agent。生产化前还需要补充持久化、身份认证、审计留存、密钥隔离和故障恢复设计。
因此更稳妥的落地顺序是:先接入只读工具,再启用参数严格的低风险写操作;把审批通道与 Agent 能看到的 MCP 工具分开;对每次 Hold 记录原因和上游实际状态;最后再评估是否需要接入真实业务系统。Planlock 的价值不是承诺 Agent 永远不会犯错,而是让错误在进入业务系统前有一个可执行、可测试的边界。