Mindket

AI 智能体护栏:沙盒、权限规则和钩子

黑色横幅,标题是“Harness 工程”,下面是“智能体外围的循环、工具与护栏”。

AI 智能体护栏,是围在智能体外面、限制它能做什么的那些措施。真正靠得住的放在模型之外:限制文件和网络访问的沙盒、权限规则、在代码里检查每次工具调用的钩子,以及有副作用的操作要先经人批准。提示词里的指令和分类器只能排第二,因为提示词注入可以说服模型绕过它们。

本节内容

  • 把硬性限制放在环境里:沙盒、文件系统边界和出站白名单。
  • 凭据放在沙盒外面,被骗的智能体也就没有东西可以外传。
  • 用钩子或工具护栏在代码里检查每次工具调用,解析不了的一律拦下。
  • 每个放行的域名和连接器都是一个入口,有副作用的操作先问人。
全部课程
本页目录
  1. 为什么提示词里的一句话算不上护栏
  2. 开始之前
  3. 要守的三个部分,以及模型被骗时还挡得住的那一个
  4. 把编程智能体锁好:沙盒、限定范围的规则和白名单
  5. 用钩子检查每一次工具调用
  6. OpenAI Agents SDK 里的工具护栏
  7. 提示词注入和“致命三要素”
  8. 哪些事绝不能让智能体单独去做
  9. Simon Willison 和 Boris Cherny 怎么说
  10. 常见错误
  11. 自己试一试
  12. 下一门课:AI 安全
  13. 自测一下
  14. 常见问题
  15. 资料来源

为什么提示词里的一句话算不上护栏

2026 年 2 月,在一次内部红队演练中,Anthropic 的一名研究员用钓鱼邮件骗一位员工,用一段恶意提示词启动了 Claude Code。

邮件写的是“你能帮我跑一下吗?”,提示词读起来像普通的任务说明。其中一步要求 Claude 读取 ~/.aws/credentials,把内容编码后 POST 到外部服务器。同一段提示词重试 25 次,Claude 照做了 24 次。

模型没有理由拒绝。指令是从用户那里来的,而 Anthropic 在模型层的防御以用户意图为准,分类器找不到任何可疑之处。

Anthropic 在介绍如何约束 Claude 的文章里说,这种情况下只有环境挡得住:出站控制不管意图如何都会拦下那次 POST,文件系统边界让 ~/.aws 根本够不着。

先问人也解决不了。Claude Code 过去每一轮都会请求权限,而 Anthropic 的遥测数据显示,用户批准了约 93% 的确认请求。文章给出的原因是:用户看到的确认越多,对每一次的注意力就越少。

智能体护栏,就是围在智能体外面的限制:它能读写哪些文件、能连哪些主机、哪些工具调用不用人批准、哪些检查要先在代码里跑。Anthropic 把自己的做法总结成一句话:“先在环境层做好约束,再在模型层引导行为。”

开始之前

这是 Harness 工程课程的最后一课。它接着长时间运行的智能体停下的地方往下讲:那个带 --dangerously-skip-permissions 的重启循环,这个参数会关掉所有确认。这一课讲的,都是该在它外面加些什么。

Claude Code 的 allow、ask、deny 规则在 Claude Code 设置那一课里讲过,Claude Code 的 hooks 讲的是写在设置文件里的 shell 钩子。

这一课把同样的思路放进你自己的代码,用的是 Python 版的 Claude Agent SDK 和 OpenAI Agents SDK。

要守的三个部分,以及模型被骗时还挡得住的那一个

Anthropic 防守的是智能体的三个部分。环境是智能体外面的沙盒、虚拟机、文件系统边界和出站控制。模型这一层包括系统提示词、分类器、探针和训练。

第三个部分,是智能体通过 MCP 服务器、插件和网页搜索能接触到的外部内容。这里靠收窄工具权限来限制损失:只有数据库只读权限的智能体,可以比能写生产库的智能体放心地部署到更多地方。

只有环境能划出硬边界。Anthropic 写道,因为模型是概率性的,模型层的措施只能影响智能体倾向于做什么。它最好的数字也不是零。会根据结果调整的攻击者,成功率也远高于只试一次的攻击者。

权限规则和钩子夹在中间,放在智能体 harness 里,也就是模型外围的代码。写在代码里的检查,不管模型信了什么都照样生效。一个第一百次点“同意”的人,就是弱得多的一道检查。

Anthropic 测到的数字

93%

的权限确认被 Claude Code 用户批准了

来源:Anthropic: How we contain Claude across products (2026年11月6日 查阅)

84%

的权限确认在 Bash 进入 Claude Code 沙盒后消失了,这是 Anthropic 内部使用的数据

来源:Anthropic: Making Claude Code safer with sandboxing (2026年11月6日 查阅)

83%

的越界操作在执行前被自动模式拦下

来源:Anthropic: How we contain Claude across products (2026年11月6日 查阅)

5 到 6%

是在 Gray Swan 基准上,100 次自适应尝试后提示词注入对 Claude Opus 4.7 的成功率,单次尝试只有 0.1%

来源:Anthropic: How we contain Claude across products (2026年11月6日 查阅)

三个部分出自 Anthropic 介绍如何约束 Claude 的文章,2026 年 11 月 6 日阅读。最后一列是我们的概括。
Anthropic 防守什么它的防御模型被骗时还挡得住吗?
环境进程沙盒、虚拟机、文件系统边界、出站控制能,在它覆盖的范围内
模型系统提示词、分类器、探针、训练靠不住
它能接触到的外部内容收窄工具权限,比如数据库只读能限制损失

把编程智能体锁好:沙盒、限定范围的规则和白名单

上一课的示例,Anthropic 的 autonomous-coding 快速入门,在第一个会话开始之前,就把护栏写进了一个设置文件。

下面这段原样摘自它的 client.py,注释也保留了。PUPPETEER_TOOLS 在该文件里定义在它上方,是七个 Puppeteer MCP 工具组成的列表。里面有三道防线,第四道在一个钩子里。

security_settings = {
    "sandbox": {"enabled": True, "autoAllowBashIfSandboxed": True},
    "permissions": {
        "defaultMode": "acceptEdits",  # Auto-approve edits within allowed directories
        "allow": [
            # Allow all file operations within the project directory
            "Read(./**)",
            "Write(./**)",
            "Edit(./**)",
            "Glob(./**)",
            "Grep(./**)",
            # Bash permission granted here, but actual commands are validated
            # by the bash_security_hook (see security.py for allowed commands)
            "Bash(*)",
            # Allow Puppeteer MCP tools for browser automation
            *PUPPETEER_TOOLS,
        ],
    },
}
  1. 打开沙盒。Bash 命令会在操作系统级的沙盒里运行,Linux 上是 bubblewrap,macOS 上是 Seatbelt,autoAllowBashIfSandboxed 让这些命令不用确认就能跑。Anthropic 把这个运行时开源成了 sandbox-runtime。
  2. 把文件工具的范围限定在 ./ 下面的路径,让 Read、Write、Edit、Glob 和 Grep 都留在项目目录里。
  3. 把 defaultMode 设成 acceptEdits。允许目录里的修改就不用再问了。
  4. 之所以敢放行 Bash(*),是因为有一个钩子拿每条命令去对一份 18 项的清单,从 ls、git 到 npm 和 init.sh。
  5. 给有风险的几项加额外检查。在 security.py 里,pkill 只能结束 node、npm、npx、vite 或 next,chmod 只能加 +x,init.sh 必须以 ./init.sh 这样的路径运行,不能只写名字。

沙盒管的是 Bash 和它启动的进程。SDK 的类型定义写明,文件工具和 WebFetch 要靠权限规则来限制。它还列出了两个要留意的默认值:沙盒默认关闭,只支持 macOS 和 Linux;allowUnsandboxedCommands 默认为 true,除非你把它设成 false,否则命令可以申请在沙盒外运行。

用钩子检查每一次工具调用

Claude Agent SDK 的 README 对钩子的定义是:一个 Python 函数,由 Claude Code 应用程序在智能体循环的固定节点调用,而不是由 Claude 调用。所以模型没法靠说服绕过它。

PreToolUse 钩子在调用执行之前就能看到工具名和输入,可以拒绝这次调用,并给出一个模型能读到的理由。

下面的示例原样摘自那份 README,去掉了后面的两个测试查询。它会拒绝任何包含 foo.sh 的 Bash 命令。

from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher

async def check_bash_command(input_data, tool_use_id, context):
    tool_name = input_data["tool_name"]
    tool_input = input_data["tool_input"]
    if tool_name != "Bash":
        return {}
    command = tool_input.get("command", "")
    block_patterns = ["foo.sh"]
    for pattern in block_patterns:
        if pattern in command:
            return {
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": f"Command contains invalid pattern: {pattern}",
                }
            }
    return {}

options = ClaudeAgentOptions(
    allowed_tools=["Bash"],
    hooks={
        "PreToolUse": [
            HookMatcher(matcher="Bash", hooks=[check_bash_command]),
        ],
    }
)
  • 在我们看来,列出要拦什么是较弱的设计,因为同一条命令可以有很多种写法。快速入门的钩子反过来做:它把命令行里的每个程序都拆出来,只要有一个不在清单上,就拦下整次调用。
  • 解析不了的命令也会被拦下,理由是“Could not parse command for security validation”。检查拿不准时,答案就是不行。
  • allowed_tools 容易让人误会。README 说它是一份权限白名单:列出的工具不用问就能跑,其余的交给 permission_mode 和 can_use_tool 决定。它不会从 Claude 的工具集中移除任何东西,要拿走一个工具,得把它写进 disallowed_tools。

OpenAI Agents SDK 里的工具护栏

OpenAI Agents SDK 那一课讲过输入护栏、输出护栏和 needs_approval。对一个会动手做事的智能体来说,最要紧的是工具护栏。

它包住一个函数工具,每次调用都会运行。输入检查可以跳过这次调用或触发 tripwire,输出检查可以替换结果。

下面的示例原样摘自这个 SDK 在 GitHub 上的护栏指南。一个检查拒绝含有 sk- 的参数,另一个拒绝含有它的输出。

import json
from agents import (
    Agent,
    Runner,
    ToolGuardrailFunctionOutput,
)
from agents.decorators import tool, tool_input_guardrail, tool_output_guardrail


@tool_input_guardrail
def block_secrets(data):
    args = json.loads(data.context.tool_arguments or "{}")
    if "sk-" in json.dumps(args):
        return ToolGuardrailFunctionOutput.reject_content(
            "Remove secrets before calling this tool."
        )
    return ToolGuardrailFunctionOutput.allow()


@tool_output_guardrail
def redact_output(data):
    text = str(data.output or "")
    if "sk-" in text:
        return ToolGuardrailFunctionOutput.reject_content("Output contained sensitive data.")
    return ToolGuardrailFunctionOutput.allow()


@tool(
    tool_input_guardrails=[block_secrets],
    tool_output_guardrails=[redact_output],
)
def classify_text(text: str) -> str:
    """Classify text for internal routing."""
    return f"length:{len(text)}"


agent = Agent(name="Classifier", tools=[classify_text])
result = Runner.run_sync(agent, "hello world")
print(result.final_output)

要注意作用范围。智能体级的输入护栏只对链条里的第一个智能体生效,所以指南建议,交接之后仍需生效的检查要用工具护栏。WebSearchTool、HostedMCPTool 这类托管工具不走这条管线,ShellTool、ApplyPatchTool、ComputerTool 和 LocalShellTool 也不走。这些工具要靠人工批准或运行环境来把关。按照人工介入指南,本地 shell 和文件补丁的批准要自己打开,needs_approval 默认是 false。

提示词注入和“致命三要素”

OpenAI 的智能体安全指南给提示词注入下的定义是:不可信的文本或数据进入 AI 系统,其中的恶意内容试图推翻给 AI 的指令。不是你自己写的东西都不可信:网页、邮件、issue 评论、工具返回的结果都算。

Simon Willison 在 2025 年 6 月给这种危险组合起了名字,叫“致命三要素”(lethal trifecta)。一个智能体如果能访问私有数据、会接触不可信内容、又有办法把数据发出去,就可能被骗着把数据泄露出去。

三者去掉任何一个,这条攻击路径就断了。换作我们,会先从“发出去”这一环下手,因为很少有智能体真的需要连上每一台主机。

Claude Cowork 的出站白名单放行了发往 api.anthropic.com 的流量,因为产品本身要调用 Anthropic 的 API。用户工作区里被放进了一个文件,里面藏着指令和攻击者的 API 密钥。

Claude 读了工作区里的其他文件,再通过 Anthropic 的 Files API 把它们上传到了攻击者的账号。Anthropic 的结论是:白名单上任何一个域名能调用的每一项功能,都成了攻击面。

内容也会从你信任的工具进来。GitHub 连接器即使通过了恶意软件检查,也可能把一份投了毒的 README 直接带进模型的上下文。

OpenAI 这份指南是为 Agent Builder 写的,OpenAI 将在 2026 年 11 月 30 日关停它,但里面的建议适用于任何智能体:

  • 不可信的输入放进用户消息,绝不放进优先级更高的开发者消息。
  • 步骤之间用结构化输出,比如枚举和固定的 schema,不留可以夹带指令的自由文本。
  • MCP 工具一律保持批准开启,读和写都一样。
  • 从外部输入里只取你需要的字段,不让不可信的数据直接左右智能体的行为。

指南最后也承认了局限:结构化输出和隔离能大大降低这种风险,但不能完全消除。

哪些事绝不能让智能体单独去做

下面是我们整理的清单:这些操作要么放在硬限制后面,要么交给人来批准,每一行都对应上文来源里的一次失败或一种做法。判断每一行只用一个问题:如果模型此刻被骗了,这个操作能造成什么后果?

我们的清单。来源:Anthropic 谈如何约束 Claude、Claude Code 沙盒、快速入门、OpenAI 的人工介入指南和 C 编译器那篇文章。
操作护栏出处
读取云服务凭据或 SSH 密钥把它们放在沙盒外,让智能体无从读取Anthropic 的红队演练
向没用过的主机发送数据出站白名单,加上一个遇到新域名就询问的代理Claude Code 沙盒
推送到 git 远程仓库一个代理先核对限定范围的凭据和目标分支,再附上真正的令牌网页版 Claude Code
运行任意 shell 命令一个白名单钩子,解析不了的命令一律拦下autonomous-coding 快速入门
取消订单或发送邮件needs_approval,由人批准或拒绝这次调用OpenAI Agents SDK
关掉所有确认来运行一个里面没有任何值得偷的东西的容器C 编译器那篇文章

Simon Willison 和 Boris Cherny 怎么说

Simon Willison 是 Django 的联合创建者、Datasette 的创建者。2025 年 6 月 16 日,他在 X 上发帖提出了“致命三要素”,引文见下。帖子是写给所有用 AI 智能体的人的,它把智能体定义为会调用工具的 LLM,所以这条提醒既管你自己做的智能体,也管你接上了工具的聊天。

Claude Code 的作者 Boris Cherny 在 2026 年 4 月 16 日,也就是 Opus 4.7 发布当天,发帖谈了自动模式。Anthropic 同一天把自动模式扩展到了 Max 用户。他的帖子把自动模式和 Opus 4.7 联系在一起。

Anthropic 介绍自动模式如何做出来的文章标注的日期是 2026 年 3 月 25 日。在 Anthropic 介绍如何约束 Claude 的文章里,权限确认交给一个基于模型的分类器,它判断为安全的命令会直接放行。

把它和上面的图表对照着看:分类器减少了人要看的确认,而它漏掉的时候,挡住的是沙盒。

只要你把访问私有数据、接触不可信内容和对外通信的能力组合在一起,攻击者就能诱骗系统偷走你的数据

Simon Willison, Django 联合创建者,Datasette 创建者,引文为译文 · X (2026年11月6日 查阅)
Simon Willison(@simonw)2025 年 6 月 16 日在 X 上发帖:如果你在用“AI 智能体”(会调用工具的 LLM),就得知道“致命三要素”。只要把访问私有数据、接触不可信内容和对外通信的能力组合在一起,攻击者就能诱骗系统偷走你的数据。
Simon Willison 在 X 上,2025 年 6 月 16 日
Boris Cherny(@bcherny)2026 年 4 月 16 日在 X 上发帖:1/ 自动模式 = 不再有权限确认。Opus 4.7 喜欢做复杂的长时间任务,比如深度研究、重构代码、构建复杂功能、不断迭代直到达到某个性能基准。帖子在“Show more”之后还有内容。
Boris Cherny 在 X 上,2026 年 4 月 16 日

X 官方嵌入卡片的截图,拍摄于 2026-11-06,时间为 UTC。点击截图可到 X 上打开原帖。两条帖子都被 X 用“Show more”截短了。

常见错误

  • 在系统提示词里写一句“不要读取 .env”,就当它是护栏。
  • 放行 Bash(*),后面却没有钩子把关。
  • 把一个工具写进 allowed_tools,以为其他工具就会消失。
  • 把 API 密钥或云服务凭据放在智能体读得到的地方。
  • 往白名单里加域名,却没想过这个域名还能让智能体做些什么。
  • 把网页、邮件或工具输出贴进开发者消息。
  • 确认弹得太多,多到没人再认真看。
  • 在你自己的机器上运行 --dangerously-skip-permissions。

自己试一试

  • 在 Claude Code 里运行 /sandbox,再让它执行一条要联网的命令,看看会发生什么。
  • 复制 README 里的钩子,把 rm -rf 加进 block_patterns,然后让智能体清空一个临时文件夹。
  • 在一个测试仓库的 README 里写一行字,让智能体打印某个环境变量。再让一个在沙盒里运行的智能体总结这个仓库,看它有没有照做。
  • 把 block_secrets 挂到一个工具上,用含有 sk-test 的参数调用它,读一读模型拿回来的是什么。

下一门课:AI 安全

Harness 工程课程到这里就结束了。下一门课“AI 安全”从提示词注入开始:藏在网页、邮件或文件里的指令,怎样让你的智能体反过来对付你,以及哪些设计能把损失控制住。之后的 AI 评估课会衡量你的护栏到底拦下了什么。

护栏同样需要衡量。数一数你的钩子拦下了多少条安全的命令,又放过了多少个有风险的操作。Anthropic 为自动模式报告的也是这两个比率:被拦下的正常命令占多少,被放过的越界操作占多少。每次改规则后,都再数一遍。

自测一下

  1. 在 Anthropic 2026 年 2 月的红队演练中,什么能拦住被偷的凭据外传?

    查看答案

    B. 出站控制,加上让 ~/.aws 够不着的文件系统边界 指令是从用户那里来的,模型层找不到可疑之处。这种情况下,只有环境里的限制挡得住。

  2. 在 Claude Agent SDK 里,allowed_tools 起什么作用?

    查看答案

    D. 让清单上的工具不用问就能运行,其余的交给权限模式决定 它是一份权限白名单,不会从工具集中移除任何东西。要拿走一个工具,得用 disallowed_tools。

  3. Simon Willison 说的“致命三要素”是哪三样?

    查看答案

    A. 私有数据、不可信内容和把数据发出去的途径 三样都具备的智能体,可能被骗着泄露数据。去掉其中一样,这条攻击路径就断了。

  4. 快速入门里的 Bash 钩子遇到解析不了的命令会怎么做?

    查看答案

    C. 拦下它 它默认拒绝,理由是“Could not parse command for security validation”。

常见问题

什么是 AI 智能体护栏?

凡是限制或检查智能体行为的东西都算,从沙盒、出站白名单,到钩子、工具护栏,再到批准某次调用的人。给它们分类只用一个标准:模型被骗了,这道限制还在不在?

用 --dangerously-skip-permissions 安全吗?

只在你可以随手丢掉的环境里才行。Nicholas Carlini 用它来跑写编译器的智能体,并提醒读者在容器里运行,不要在自己的机器上。别把凭据放进那个容器,也要限制它能连接的地方。

分类器能挡住提示词注入吗?

它能降低成功率。Anthropic 写道,任何概率性的防御都有不为零的漏报率。它从中学到最多的两次事故,都是数据从被允许的通道流出去,模型根本看不出异常。

权限规则和沙盒有什么区别?

权限规则由智能体 harness 在工具运行之前,对照写出来的那次工具调用来检查。沙盒由操作系统强制执行,管住 Bash 和它启动的每一个进程,所以一个自己去打开文件的脚本,照样会被挡住。

不开沙盒,Claude Code 的自动模式安全吗?

Anthropic 并不是这么定位它的。它的分类器会拦下大约 0.4% 的正常命令,又会放过大约 17% 的越界操作,所以文章说它只是沙盒里的一层防御,不能代替沙盒。

资料来源

  1. Anthropic: How we contain Claude across products (2026年11月6日 查阅)https://www.anthropic.com/research/how-we-contain-claude
  2. Anthropic: Making Claude Code safer with sandboxing (2026年11月6日 查阅)https://www.anthropic.com/research/claude-code-sandboxing
  3. GitHub: anthropics/sandbox-runtime (2026年11月6日 查阅)https://github.com/anthropics/sandbox-runtime
  4. Anthropic Engineering: Building a C compiler with a team of parallel Claudes (2026年11月6日 查阅)https://www.anthropic.com/research/building-c-compiler
  5. GitHub: anthropics/claude-quickstarts, autonomous-coding README (2026年11月6日 查阅)https://github.com/anthropics/claude-quickstarts/tree/main/autonomous-coding
  6. GitHub: autonomous-coding client.py security settings (2026年11月6日 查阅)https://github.com/anthropics/claude-quickstarts/blob/main/autonomous-coding/client.py
  7. GitHub: autonomous-coding security.py command allowlist (2026年11月6日 查阅)https://github.com/anthropics/claude-quickstarts/blob/main/autonomous-coding/security.py
  8. GitHub: anthropics/claude-agent-sdk-python README (2026年11月6日 查阅)https://github.com/anthropics/claude-agent-sdk-python
  9. GitHub: claude-agent-sdk-python types.py (2026年11月6日 查阅)https://github.com/anthropics/claude-agent-sdk-python/blob/main/src/claude_agent_sdk/types.py
  10. GitHub: openai/openai-agents-python, guardrails guide (2026年11月6日 查阅)https://github.com/openai/openai-agents-python/blob/main/docs/guardrails.md
  11. GitHub: openai/openai-agents-python, human-in-the-loop guide (2026年11月6日 查阅)https://github.com/openai/openai-agents-python/blob/main/docs/human_in_the_loop.md
  12. OpenAI Developers: Safety in building agents (2026年11月6日 查阅)https://developers.openai.com/api/docs/guides/agent-builder-safety
  13. Anthropic: How we built Claude Code auto mode (2026年11月6日 查阅)https://www.anthropic.com/research/claude-code-auto-mode
  14. Anthropic: Introducing Claude Opus 4.7 (2026年11月6日 查阅)https://www.anthropic.com/news/claude-opus-4-7
  15. X: Simon Willison on the lethal trifecta (2026年11月6日 查阅)https://x.com/simonw/status/1934602159984984235
  16. X: Boris Cherny on Claude Code auto mode (2026年11月6日 查阅)https://x.com/bcherny/status/2044847849662505288

分享本节课

让智能体用 MCP 服务器之前,先查一查

看看 Mindket 在一个服务器加入智能体工具之前会查哪些地方,从谁发布了它,到它能访问什么。

MCP 服务器安全吗?

本节课的 Markdown 版本 · Harness 工程:搭建 AI 智能体外围的代码 · 全部课程