如果你正在给团队接一个会调外部工具的智能体,我建议你先花十分钟想清楚一件事:它拿起工具之前那一秒,有没有任何东西拦过它。我核对了 2026 年 10 月 16 日这一批 arXiv 提交,发现有五篇论文在同一个工作日把这个位置摆上了台面,而它们给出的数字,几乎全部指向同一个结论——这道门此前长期没人系统测过,测出来之后数据很难看。这篇稿子不谈注入手法分类,也不做防御方案横评,只做一件事:把「调用之前那个决定」这一层,用可核对的数字与工程实现讲清楚。
过去两年关于智能体安全的讨论,我按自己的归类习惯分成几层:最外层是沙箱与运行时隔离,中间是执行时的预算与范围上限,再往里是产出验收与轨迹留痕。这几层在过去几个月里各自都有人做了量化工作。但工具准入这一层——Agent 决定要不要调用某个工具、要不要读取某段检索结果——一直是个说得多、测得少的位置。
2026 年 10 月 16 日这天,arXiv 上出现了五篇预印本,全部落在这个位置。它们互不引用、作者也不重叠,但做的事情高度一致:把工具准入这个决定单独拎出来当被研究对象。这不是巧合能解释的密度。
需要先把口径说清楚:下面所有数字都来自预印本摘要,未经同行评议,我按作者自述转述,未做独立复现。你在自己的系统里能不能复现,取决于模型、工具集与提示词,我不能保证一致,工具准入的观测结果尤其依赖这三者。
把一次带外部工具的调用拆开看,实际有三段判定:
绝大多数工具安全研究,包括我此前整理的评测口径与产出验收材料,都集中在后两段。第一段,也就是工具准入这一层,长期处于一个尴尬状态:工程上有人做,论文里没人测。
原因不复杂。后两段有现成的判分对象——工具返回是文本,产出是文件或者可运行的服务,都能自动打分。而「要不要调」是一个二元决定,且强烈依赖上下文:同一个请求在中文和英文下的结果可能完全不同,同一批工具换一个候选集顺序结果也可能不同。没有稳定的判分对象,就很难对工具准入做出可比较的评测。
五篇论文做的事情,本质上都是给工具准入这个二元决定造出了可比较的判分口径。
下面这张表把三层判定位置与本文覆盖的内容对齐,先建立坐标系:
| 判定层 | 检查对象 | 常见工程手段 | 本文是否覆盖 |
|---|---|---|---|
| 调用前(工具准入) | 请求是否该被接纳、工具是否该进候选集 | 策略护栏、schema 校验、确定性准入 | 是,全文主体 |
| 调用中(内容判定) | 返回内容里的指令是否可信 | 逐条指令分类、注入过滤 | 部分覆盖(EDIC) |
| 调用后(产出验收) | 产出是否达标、轨迹是否完整 | 判分器、预算与签名 | 不覆盖 |
五篇里我优先讲三篇给出硬数字的,另外两篇提供机制层面的补充。它们分别是:Before Retrieval Begins: Safety and Tool-Use Decisions in LLM Agents、Disentangling Language Effects on Tool Admission and Post-Retrieval Safety、Ignored, Not Refused、Robust Function Calling for LLM Agents Under Interface Drift,以及 Identify, Classify, Act。
2610.23071 的做法是先测「接纳率」,再测「接纳之后的伤害率」。作者在七个开源权重模型上跑了一遍:有害请求一旦被接纳,之后产生的无加权模型平均伤害率是 74.5%。我把这个数字理解为工具准入失效之后的下游代价,而不是准入本身的失败率。
这个数字本身不算最刺眼,真正有意思的是后面两步。作者做了一次因子干预——对输入请求的各个成分分别做中和处理,看哪个成分对工具准入这个决定影响最大。结果是:把请求里的「操作成分」中和掉,产生了 19 个百分点的准入对比,是所有干预里最大的一个。
我对照了一下自己手上几个项目的调用日志,这个结果解释得通。模型判断「该不该调」时,依赖的往往不是这个请求整体是否安全,而是请求里描述动作的那一部分。作者进一步从这次对比里估出一个线性方向,发现这个方向能影响七个模型中四个的留出准入;同时他们自己指出,在部分模型上,这个方向对「操作成分已被中和」的请求仍然有效,说明它不是单纯的操作特定机制,而是更贴近工具准入的一道通用开关。
还有一条我建议所有做智能体的人记住:书面拒绝只伴随了 33.6% 的被接纳有害调用。换句话说,模型嘴上说不做,和实际不做,是两件事。你如果只看最终文本里有没有拒绝措辞,会漏掉三分之二。
2610.23093 把语言这个变量单独拆了出来,命名了 SLIP(Safety Loss through Interlingual Processing)。受控设置是 URL 检索,作者独立变化三个维度:请求语言、被检索页面的语言、要求回答的语言,跑了十个模型、七种语言。
结果分三条,我按作者口径逐条列:
| 干预 | 观测结果 | 覆盖模型 | 口径提示 |
|---|---|---|---|
| 请求改为非英语 | 平均工具准入从 46.9% 升至 67.4% | 十个模型全部为正向变化 | 论文自述为平均值 |
| 回答要求用页面语言 | 配对工具调用上的合并有害率从 50.3% 升至 57.4% | 四个闭源模型,主要集中在中文与印地语 | 仅在配对调用上统计 |
| 仅翻译页面本身 | 未观察到一致的有害上升 | 八个推理设置可比的模型 | 结论为「无一致上升」,非「无上升」 |
第三条是我特别想强调的,因为它直接否掉了一个常见假设:很多人以为非英语场景的风险来自「页面是非英语的」。作者测出来的结果是,页面语言本身不是主因,请求语言才是。这意味着给智能体加一层「把页面翻成英文再读」的处理,大概率不会解决工具准入这一层的问题。
作者在自主检索设置下也复现了工具准入的上升,但只测了四个模型。样本量摆在这里,我按作者口径标注为「该设置下测试的四个模型」,不外推。
2610.23254 是五篇里我最想推荐的一篇,标题本身就把结论说完了:Ignored, Not Refused。
作者审了 33,000 条轨迹,覆盖 11 个开源权重智能体(参数量 2B 到 14B),跑在 InjecAgent 与 AgentDojo 两个基准上。他们用一个「盲标签」的 LLM 判定器去分辨每一条失败攻击到底是怎么失败的——是模型识别出来并拒绝了,还是根本没注意。
结果是:76% 到 80% 的失败攻击属于被忽略,只有 17% 到 18% 属于被拒绝。
这两者的区别在安全工程上是根本性的。被拒绝意味着有一道判定在起作用,你强化它就能提高成功率;被忽略意味着这道判定压根没被调用,换个提示词、换个任务、换个模型版本,结果可能整个翻面。论文给了一个极端的例子:模型权重完全不变,只改系统提示,InjecAgent 上的攻击成功率从 0.2% 跳到了 43%。
作者据此提出了一个叫 ATOM 的矩阵,把攻击结果与任务结果交叉。里面有一格数据我认为比 ASR 更值得盯:在 AgentDojo 中,21.7% 的回合里智能体保持了安全,但没有完成用户的任务。安全与有用在这里不是此消彼长的关系,而是可以同时丢掉。
论文的结论我原样转述:低 ASR 主要反映的是智能体静默忽略了注入,而不是拒绝了注入;评测应当同时报告攻击是如何失败的,以及用户的任务是否完成,而不只是报告 ASR。作者也公开了标注语料。
前面三篇都在讲工具准入的判定本身,2610.23342 讲的是另一个入口:工具没变,但工具之间的摆放方式变了。
作者把这类故障命名为 interface drift,具体指三样东西在部署之后发生变化:工具描述、参数 schema、以及推理时展示给模型的候选工具列表。底层工具的语义没动,但模型看到的那层文本动了,于是调用开始出错。
他们给的方案叫 SchemaShield-Lite,三件套:schema 校验、面向缺失工具名的候选集重试、一次性约束修复。关键约束是 training-free,不重训基座模型。在 Qwen3.5-9B 上,DICE-300 准确率从 0.717 提到 0.813,BFCL-200 从 0.580 提到 0.805;在漂移引发的失败里分别恢复了 19/22 与 33/45 处,且未观察到修复损害。
消融结论是这篇的价值所在:候选集漂移,尤其是干扰工具(distractor tools)的加入,造成了大部分损害;而单独改描述、单独改 schema 的影响都小。作者还排除了一个竞争解释——朴素重试并不能解释这些收益。
我按这个结论反推了一条工程动作:把「部署前后的候选工具集比对」做成发布门。工具集变了,即便每个工具的实现都没变,也应该重新跑一遍工具准入校验。
学术侧之外,开源侧在同一个窗口里也出现了六个方向一致的工具准入项目。下面这张表里的星数是我在 2026 年 10 月 19 日的快照,功能描述均来自仓库自述,我没有逐个实测。
| 项目 | 建库日 | 星数(10-19 快照) | 自述定位 |
|---|---|---|---|
| tiangang0245/MCP-checkup | 10-15 | 21 | 离线只读的 MCP 配置体检,自述 28 条规则 |
| vasikmansuri/agentconfig-doctor | 10-14 | 3 | MCP 安全扫描与配置 linter,自述查泄露密钥、危险命令、过宽权限 |
| LISparrowC1/dsh-loop-guard | 10-18 | 2 | 停滞智能体的停止器,含近似重复工具调用护栏 |
| Noooblien/mcpgate | 10-16 | 1 | 零信任 MCP 网关,自述含注入防火墙与动态 RBAC |
| PHM-Audit/dsh-action-guard | 10-13 | 0 | 执行前的确定性工具调用护栏插件 |
| Krendelb/zlp-guarded-agent | 10-08 | 0 | 面向不可信本地智能体的确定性准入,自述 64 字节 |
我会把视线放在三个 0 星与 1 星项目上,而不是那个 21 星项目。原因很直接:星数高的是配置体检,属于事后扫描;星数低的那几个在做执行前的确定性判定。其中 argszero/dsh-phantom-tool-call-guard 与 ConnorDev1337/tool-call-guard 点出的缺陷最具体——一个空身份的工具调用会被接纳为 tool/call,而它的 tool/result 永远无法被校验。这类「调用被接纳但结果不可验证」的情形,正是准入层最该拦却最少被拦的部分。
需要声明的是:这些仓库的星数与功能描述全部来自仓库自述,属于存在性证据,不构成性能背书;它们的不足同样明显:建库时间短、缺少第三方验证,我也没有跑过其中任何一个。
我按上面几篇论文的共同口径,写了一段最小骨架,作用是把「调用前那个决定」单独落成一条结构化记录。这段代码是示意,不是可直接安装的库。
def admit(call, policy): verdict = policy.check(call.tool, call.args, call.origin) log.write({ "stage": "pre_call", "tool": call.tool, "args_schema_ok": verdict.schema_ok, "candidate_set_hash": call.candidates_hash, "request_language": call.lang, "instruction_class": verdict.instruction_class, "decision": verdict.action, "reason_codes": verdict.reasons, }) return verdict.action
这里四个字段是刻意加的:candidate_set_hash 对应接口漂移,request_language 对应 SLIP,instruction_class 对应逐条指令归属,decision 与 reason_codes 对应「忽略还是拒绝」的区分。缺了这四个字段,你事后无法回答「当时为什么放行了」。
def drift_report(deployed, current): added = sorted(current - deployed) removed = sorted(deployed - current) same = sorted(deployed & current) return { "distractor_added": added, "missing": removed, "unchanged": same, "needs_revalidation": bool(added or removed), }
我按 2610.23342 的消融结论,把「有增有减」直接升级为「需要重新校验」的信号,而不是告警。配套的只读审计器可以参考 heindall92/mcp-shield 的做法,它把 mcp.json 的审计结果输出为 SARIF 与 Markdown 两种格式,便于接进现有的发布流程。干扰工具的加入是主要的损害来源,因此新增工具应当与缺失工具同等对待。
我把上面的材料收敛成六步,按代价从低到高排,你可以直接照着做:
这个顺序的代价是递增的,好处是每一步都能独立产出结论,不必等全部做完才有收益。
我必须把这部分的限制写清楚,否则前面的数字很容易被误用:
不是。提示注入防御处理的是「外部内容里藏了指令」这个输入污染问题,而工具准入关注的是「要不要调用、要不要读」这个决定本身。两者相邻但不重合:即便注入被完全过滤,请求语言变化、候选工具集漂移、操作成分被中和这些情形依然会让准入决定出错。本文通篇锚定具体论文的具体数字,不做注入手法分类,也不做防御方案横评。
不可以。它来自七个开源权重模型在特定构造环境下的测量结果,论文自述为无加权模型平均。不同模型、不同工具集、不同提示词下的结果可能差别很大。我建议的用法是把它当作「这个位置值得测」的动机,而不是「你的系统有多危险」的估计。
只改系统提示就能让注入成功率从 0.2% 变 43%,这是不是说明基准不可信?
更准确的说法是:它说明低成功率里包含大量「静默忽略」而非「成功拒绝」,因此成功率这个指标本身对提示词高度敏感、不稳定。作者的建议是同时报告攻击如何失败与任务是否完成,我认同这个方向。至于基准是否可信,那是另一个问题,不在本文范围内。
按 SLIP 的结果,大概率不能。作者测出的是「仅翻译页面本身在八个模型中未产生一致的有害上升」,而风险的主因在请求语言这一侧。换句话说,翻译页面没有触及主要变量。这个结论囿于论文自身的设置,我标注为供参考。
我的判断是不建议直接上生产关键路径。它们的共同特征是建库时间短、星数低、文档薄,功能描述全部来自仓库自述,我没有实测其中任何一个。合理的做法是列入观察名单,先在自己环境里做只读验证,再考虑是否在非关键路径上灰度。
区别在于变的是什么。版本升级通常伴随工具语义变化,而接口漂移指的是工具语义未变、但描述文本、参数 schema 或候选工具列表发生了变化。论文特别指出,候选集漂移尤其是干扰工具的加入造成了大部分损害,而单独改描述与 schema 的影响较小——这一点与很多人的直觉相反。
补日志字段。它成本最低,且是后面五步的前提:没有 candidate_set_hash、request_language、instruction_class、decision 这四个字段,你事后无法回溯「当时为什么放行」,其余分析都无从谈起。
有,但都需要自己验证。SchemaShield-Lite 走的是免训练路线,思路容易移植;EDIC 需要训练判分器,成本更高。两者的效果都只在论文报告的基准与模型上验证过。我在正文里给的排查顺序与两段示意代码,是我按论文口径自己整理的,不是论文原作者的方案;这是一种取舍,代价是缺少大规模验证,我未在生产环境跑过。