OpenRouter Armor评测:Agent工具调用拦截的三种模式与Claude Code的漏洞

OpenRouter Armor 用模型为工具调用把关,但 Claude Code 模式下只标记不拦截。
OpenRouter 推出 beta 版 Armor,在模型与应用之间插入一个安全分类器,用自然语言规则检查每次工具调用。它基于 TypeSafe 的 Jev 结构化决策模型打分,规则分 hard_deny、soft_deny、allow 三层,deny 默认阈值 0.6,并提供 audit 模式供先观察后拦截。在 Chat Completions 和 Responses 上,被拦截的调用不会到达代码;但在 Messages 的 claude_code_compatibility 模式下,Armor 只把调用标记为 flagged,拦截责任转移给客户端——忽略 safeguard_results 的自研客户端会直接执行被标记的 tool_use。此外 on_error 的 block/log 设置管不到该模式,unavailable 判定一律放行,零数据保留等场景也不在 Armor 覆盖范围内。加载 Claude Code 规则集后延迟中位增加 279ms,且每次工具调用都要付出这笔成本。
Agent 自作主张删掉一个你从未提及的文件夹,问题出在模型和 shell 之间缺少一道「说不」的闸门。OpenRouter 在 10 月 15 日推出 beta 版 Armor,想做这道闸门:一个位于模型和应用之间的安全分类器,用自然语言规则检查每一次工具调用。被标记的调用不会执行,模型会被告知自己撞了哪条规则。
但文档里藏着一个例外,而且恰好出现在最贴近 Claude Code 的场景里。

Armor 的判定机制:用模型而不是正则
对每一次工具调用,OpenRouter 会把调用内容、你写的规则和对话上下文一起发给一个决策模型,由它返回该调用匹配每条规则的概率,再汇总成一个 flagged 或 not_flagged 的结论。
OpenRouter 在发布帖中点名了背后的模型:TypeSafe 的 Jev。这是一个结构化决策模型,返回带概率的类型化答案,而不是自由生成的文本,当前版本为 typesafe/jev-1.14。
为什么用模型而不是规则列表?OpenRouter 的说法是「基于正则的过滤往往不足以识别不想要的工具调用」。发布帖还声称 Armor 在性能和成本上都优于 Claude Code 自带的 auto-mode 分类器。不过,explainx.ai 的一篇评测指出,OpenRouter 并未公开这套结论背后的测试集和数字。这是一个合理的质疑——安全类的性能宣称,没有可复现的评测就只是营销话术。
结构化决策模型是一类专门为分类、路由或打分任务训练的模型,输出的是带类型的结构化结果(如枚举标签加置信度概率),而不是自由生成的文本。与通用 LLM 相比,它通常更小、更快、更便宜,且输出格式稳定,便于程序直接解析。OpenRouter 选用 TypeSafe 的 Jev 正是看中这一点:每次工具调用都要在毫秒级完成判定,且结果需要可比较的概率值来与阈值对照。这类模型的能力边界也由此确定——它擅长按给定规则做判断,但不具备通用推理能力,规则写得含糊时它的表现会明显下降。
三层规则:hard_deny、soft_deny 与 allow
规则体系分为三层:
hard_deny用于「无论用户说什么都绝不能发生」的动作;soft_deny用于「通常不该做、但有时确实是合理请求」的动作,命中后除非有allow规则同时匹配,或用户在对话中明确要求了对该目标执行该操作,否则阻止;allow本身从不阻止任何东西,只用于定义 soft-deny 的例外。
Armor 还内置了一条 soft-deny,针对用户在对话中设定且尚未解除的限制,比如「我审阅之前不要 push」。每条规则包含 id、可选的 name、instructions 和可选的 threshold,deny 规则默认阈值 0.6,allow 规则默认 0.7。
一个实用的细节是 "mode": "audit":此时 Armor 对每次调用打分但不阻止任何东西,适合先用真实流量调规则,再切换为强制执行。注意 audit 模式不能与 claude_code_compatibility 组合使用。
被拦截的调用去哪里:三种 output_mode
拦截结果的呈现方式取决于 output_mode:
| output_mode | 支持的 API | 被拦截调用的形态 |
|---|---|---|
text(Chat Completions 与 Responses 默认) | Chat Completions、Responses | 助手文本中出现 <openrouter_armor_blocked> 标记 |
custom_item | Responses | 一个 openrouter:armor_blocked 输出项 |
claude_code_compatibility | Messages | 保留 tool_use,在 safeguard_results 中标记为 flagged |
前两种模式下,被拦截的调用根本不会到达你的代码,模型会被告知该调用「被 OpenRouter Armor 按某条规则阻止且未执行」,并被要求不要用相同参数重试。
第三种模式的行为完全不同,也是本文标题所指的核心问题。
Chat Completions、Responses 和 Messages 是当前 LLM API 的三种主要请求格式。Chat Completions 是 OpenAI 最早普及的对话补全接口,生态最广;Responses 是 OpenAI 较新的接口,用统一的输入/输出项(item)模型表达文本、工具调用和多轮状态;Messages 则是 Anthropic 的接口格式,Claude Code 等工具基于它构建。三者的差异直接决定了 Armor 能把拦截结果塞进哪里:Chat Completions 只有文本通道,所以只能用 <openrouter_armor_blocked> 标记;Responses 有结构化的输出项,可以新增 openrouter:armor_blocked 类型;Messages 的 tool_use 块是客户端自己解析执行的,因此 Armor 只能附加 safeguard_results 供客户端参考,而无法代替客户端拒绝执行。
Claude Code 模式:只标记,不拦截
一个携带 dangerous_tool_use 类型 safeguards 条目的 Messages 请求会自动选中这种模式,无需 plugins 配置。文档的措辞很直白:「在 claude_code_compatibility 下,阻止调用的是你的应用。」在文档描述的流程中,Claude Code 就是这个应用:它读取 flagged 结论,拒绝执行,并写回一个 is_error 的工具结果。
漏洞在于:如果你自己写了一个 Messages 客户端,而它从不读取判定结果,被标记的 tool_use 就会被直接执行。文档明确警告:「忽略 safeguard_results 的 Messages 客户端会运行被标记的 tool_use。」如果无法确认客户端会检查,官方建议改用 "output_mode": "text"。
如果你确实要自研 Messages 客户端,执行前必须检查。safeguard_results 是顶层数组,每个条目的 status.tool_uses 以工具调用 id 为键,包含 type、outcome 和可选的 explanation。一个稳妥的做法是:只有当 Armor 确实评估过且结论为 not_flagged 时才执行,缺失判定或判定为 unavailable 一律拒绝。需要说明,拒绝缺失判定是笔者的选择,并非文档强制要求,但它与 Armor 插件请求中 on_error 的默认值方向一致。
故障时闭还是开:on_error 的边界
决策模型可能超时、报错,或收到大到无法判定的请求,此时结论为 unavailable,由 on_error 决定行为:block(默认)或 log。但这个设置管不到 Claude Code 模式——在那里,unavailable 的调用总是被放行并附带可见判定,由客户端自己决定。
其他几个值得注意的边界条件:
- Armor 尚不支持 HIPAA、零数据保留(Zero Data Retention)或数据区域限制下的请求。这些场景的插件请求会返回 403,而 Claude Code 请求则被放行并标记
unavailable,回退到 Claude Code 本地的 auto-mode 检查。换言之,在零数据保留场景下,今天并不是 Armor 在把关。 - 决策模型会产生误报和漏报,per-rule 的
threshold是调节旋钮。 - 加载 Claude Code 兼容规则集时,Armor 的中位延迟增加 279 ms,P90 增加 334 ms。
- beta 期间评估不计费,但 OpenRouter 表示正式发布前会定价,且配置字段、
safeguard_results对象和openrouter:armor_blocked项类型都可能变动。
第三点是每次工具调用都要付的成本。一个 agent 回合里连续触发多次工具调用,这笔延迟会叠加。
on_error 的 block 与 log 对应分布式系统中的 fail-closed 与 fail-open 两种故障策略。fail-closed 指依赖组件不可用时默认拒绝,安全性高但可用性受损;fail-open 则默认放行,保证业务连续但把风险暴露给下游。对 agent 安全检查而言,选择取决于误伤成本与漏放成本的对比:block 会在决策模型抖动时中断正常任务,log 则可能在检查器故障的窗口期内让危险调用通过。值得注意的是,这两种策略都只是转移风险而非消除风险,实际部署时通常还需要配合审计日志,以便在 fail-open 期间事后追溯。
通过 Ori 运行 Claude Code
Armor 的 Claude Code 路径经由 Ori,这是 OpenRouter 的启动器,能把它支持的 agent CLI 运行在 OpenRouter 上。它用 OAuth 登录而不是粘贴密钥,支持 Claude Code、Codex、OpenCode、Cline 等。安装脚本是 curl -fsSL https://openrouter.ai/labs/ori/install.sh | bash——在把它管道进 shell 之前,建议先读一遍脚本内容。
之后用非 Anthropic 模型加 auto 模式启动:
ori claude --model moonshotai/kimi-k2-0905 -- --permission-mode auto
Claude Code 的 auto 模式会用第二个模型来审查动作。据 GIGAZINE 报道,经由 Ori,OpenRouter 上的模型可以使用这套审批机制,由 Armor 承担审查角色。
上锁之前的三件事
想试 Armor,先想清楚这三点:
- 从
"mode": "audit"开始,在真实流量上读判定结果,确认规则合理后再允许拦截。 - 在 Chat Completions 和 Responses 上主动选择
on_error。block让检查器故障时不放行,log让检查器故障时不阻断 agent,取决于你的风险偏好。 - 如果你拥有 Messages 客户端,在每次
tool_use前检查safeguard_results,或干脆改用"output_mode": "text"。
Armor 本质上是在 agent 的每个动作前插入一个网络跳转,外加一个故障时闭还是开的决策。这会带来延迟预算、超时处理和依赖不可用时的系统行为问题——这些正是分布式系统设计的经典议题,只不过现在落在了 agent 的安全检查上。
一个值得权衡的问题:你愿意为 agent 的每次工具调用多付出约 300 毫秒,换取一层由第二个模型把守的检查吗?还是更信任权限提示和你自己的眼睛?
相关推荐

Workbuddy零基础入门:桌面AI智能体如何落地工作流
Workbuddy桌面AI智能体零基础教程解读:从安装、积分领取到文件管理与自动化工作流搭建,对比CodeX的适用场景与成本差异,帮助职场人完成从AI提问者到AI管理者的思维转变。

OpenAI智能体新栈:计算机操作、决策API与超速工作流
OpenAI Dev Day发布智能体新栈:Computer Use操作速度超越普通人,Decisions API基于Luna实现极低延迟分类,Agents API开放同源计算机操作能力,GPT-6.1 Sol成本仅为Astra七分之一。本文梳理核心更新、实测数据与开发者落地要点。

梁文峰开源推理框架DeepInfo,硬刚美国禁令
DeepSeek在开源模型后再开源推理框架DeepInfo,包含GPU/CPU交叉流水线、小模型预判大模型复核、华为昇腾与英伟达芯片全面适配等技术,直面美国联邦禁令。本文解析其对AI产业落地的意义与开源策略。