我让一个 AI 编程代理帮我管理一台个人服务器:它有 shell,我的账号能读写的它都能读写,也能访问任意 URL。在处理一件与 SSH 无关的任务时,它打开了 ~/.ssh/id_rsa。没有报错,也没有越权:按我给它的权限,这次读取完全合法,它也没有用密钥做任何事。问题恰恰在这里:一切都在权限之内。它为什么能读这个文件?在我碰巧看到之前,它还读过什么?这两个问题我都答不上来。

这不是一个新问题。给一个进程超出任务所需的权限,又无法审计它用多出来的权限做了什么——sudo、SELinux 和安全工程里的每一套最小权限机制,针对的都是这个问题。新的只是进程的形态:一个自己决定下一步做什么的代理。我没有找到能把这套办法用在这种进程上的现成工具,于是开始写 AgentGuard。

三种失效方式

带工具的代理,即能读文件、执行命令、抓取网页的代理,值得关注的失效方式有三种。它们常被统称为「AI 安全」问题,但成因不同,对策也不同。

三种失效对应三套彼此独立的机制。这是 AgentGuard 最早定下的设计决定:它不是一个过滤器,而是三层。让一个机制同时做三件事,三件都做不好。

边界放在哪一层

机制定了,还要定执行的位置。候选位置有四个,前三个各有一个具体的不足。

剩下的位置是协议层,在 AI 代理与 MCP 服务器之间。每条 tools/call 在转发前检查,每条结果在返回前检查。MCP 的 stdio 传输适合这么做:按行分隔的 JSON-RPC,两端都不用改。把代理的客户端配置指向 AgentGuard 而不是服务器,策略就在中间执行;两侧照常使用 MCP,不需要知道中间有它。

每条消息经过什么

(2026 年 9 月补记:以上说的是 0.1。从 0.2.0 起,参数属于哪一类,由服务器在 tools/list 里声明的 schema 和参数名里的词决定,例如 file_location 里的 file。两种途径都识别不出的参数默认仍然放行,在审计日志里记为 unclassified;把 unclassified_arguments 设为 deny 后,这样的调用会被拒绝。)

三层的每个决定都写入审计日志。日志是哈希链:每条记录包含前一条的哈希,过去的某一行被改动,重新校验一遍链就会暴露。链存在的原因是:一份改写后无法被发现的日志起不到审计的作用。

它不承诺什么

完整论证在仓库的威胁模型文档里,这里只写结论。我宁可发布一个范围窄、边界写清楚的工具,也不发布一个范围大、承诺兑现不了的工具。AgentGuard 不给 MCP 服务器进程加沙箱:服务器在操作系统层面做了什么,协议层的代理看不到。它不保证捕获所有注入措辞:规则公开在策略 YAML 里,读过的人可以绕开;计划中用于补这个缺口的 LLM 分类层尚未实现,这个缺口写在文档里。审计日志能发现篡改,不能阻止篡改:它能证明某一行被改过,但阻止不了有完整文件系统权限的人删掉整个文件、重新伪造一条自洽的链。

安全工具只有在使用者能确切知道它提供什么的时候才有用。夸大承诺的工具比没有更糟:它让人放弃了本来会自己做的核对。

这些结论写在 README 里,写在单独的威胁模型文档里,也写在这里。

下一篇只讲注入检测这一层:第一版为什么用规则而不是分类器,误报和漏报各自的代价,以及默认规则里一个真实发布出去、后来被测试套件发现的漏洞。