为什么提示词里的一句话算不上护栏
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 里,也就是模型外围的代码。写在代码里的检查,不管模型信了什么都照样生效。一个第一百次点“同意”的人,就是弱得多的一道检查。
84%
的权限确认在 Bash 进入 Claude Code 沙盒后消失了,这是 Anthropic 内部使用的数据
来源:Anthropic: Making Claude Code safer with sandboxing (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 防守什么 | 它的防御 | 模型被骗时还挡得住吗? |
|---|---|---|
| 环境 | 进程沙盒、虚拟机、文件系统边界、出站控制 | 能,在它覆盖的范围内 |
| 模型 | 系统提示词、分类器、探针、训练 | 靠不住 |
| 它能接触到的外部内容 | 收窄工具权限,比如数据库只读 | 能限制损失 |
把编程智能体锁好:沙盒、限定范围的规则和白名单
上一课的示例,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,
],
},
}- 打开沙盒。Bash 命令会在操作系统级的沙盒里运行,Linux 上是 bubblewrap,macOS 上是 Seatbelt,
autoAllowBashIfSandboxed让这些命令不用确认就能跑。Anthropic 把这个运行时开源成了 sandbox-runtime。 - 把文件工具的范围限定在
./下面的路径,让 Read、Write、Edit、Glob 和 Grep 都留在项目目录里。 - 把
defaultMode设成acceptEdits。允许目录里的修改就不用再问了。 - 之所以敢放行
Bash(*),是因为有一个钩子拿每条命令去对一份 18 项的清单,从ls、git到npm和init.sh。 - 给有风险的几项加额外检查。在 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 工具一律保持批准开启,读和写都一样。
- 从外部输入里只取你需要的字段,不让不可信的数据直接左右智能体的行为。
指南最后也承认了局限:结构化输出和隔离能大大降低这种风险,但不能完全消除。
哪些事绝不能让智能体单独去做
下面是我们整理的清单:这些操作要么放在硬限制后面,要么交给人来批准,每一行都对应上文来源里的一次失败或一种做法。判断每一行只用一个问题:如果模型此刻被骗了,这个操作能造成什么后果?
| 操作 | 护栏 | 出处 |
|---|---|---|
| 读取云服务凭据或 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 的文章里,权限确认交给一个基于模型的分类器,它判断为安全的命令会直接放行。
把它和上面的图表对照着看:分类器减少了人要看的确认,而它漏掉的时候,挡住的是沙盒。
只要你把访问私有数据、接触不可信内容和对外通信的能力组合在一起,攻击者就能诱骗系统偷走你的数据
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 为自动模式报告的也是这两个比率:被拦下的正常命令占多少,被放过的越界操作占多少。每次改规则后,都再数一遍。
自测一下
在 Anthropic 2026 年 2 月的红队演练中,什么能拦住被偷的凭据外传?
查看答案
B. 出站控制,加上让 ~/.aws 够不着的文件系统边界 指令是从用户那里来的,模型层找不到可疑之处。这种情况下,只有环境里的限制挡得住。
在 Claude Agent SDK 里,allowed_tools 起什么作用?
查看答案
D. 让清单上的工具不用问就能运行,其余的交给权限模式决定 它是一份权限白名单,不会从工具集中移除任何东西。要拿走一个工具,得用 disallowed_tools。
Simon Willison 说的“致命三要素”是哪三样?
查看答案
A. 私有数据、不可信内容和把数据发出去的途径 三样都具备的智能体,可能被骗着泄露数据。去掉其中一样,这条攻击路径就断了。
快速入门里的 Bash 钩子遇到解析不了的命令会怎么做?
查看答案
C. 拦下它 它默认拒绝,理由是“Could not parse command for security validation”。
常见问题
什么是 AI 智能体护栏?
凡是限制或检查智能体行为的东西都算,从沙盒、出站白名单,到钩子、工具护栏,再到批准某次调用的人。给它们分类只用一个标准:模型被骗了,这道限制还在不在?
用 --dangerously-skip-permissions 安全吗?
只在你可以随手丢掉的环境里才行。Nicholas Carlini 用它来跑写编译器的智能体,并提醒读者在容器里运行,不要在自己的机器上。别把凭据放进那个容器,也要限制它能连接的地方。
分类器能挡住提示词注入吗?
它能降低成功率。Anthropic 写道,任何概率性的防御都有不为零的漏报率。它从中学到最多的两次事故,都是数据从被允许的通道流出去,模型根本看不出异常。
权限规则和沙盒有什么区别?
权限规则由智能体 harness 在工具运行之前,对照写出来的那次工具调用来检查。沙盒由操作系统强制执行,管住 Bash 和它启动的每一个进程,所以一个自己去打开文件的脚本,照样会被挡住。
不开沙盒,Claude Code 的自动模式安全吗?
Anthropic 并不是这么定位它的。它的分类器会拦下大约 0.4% 的正常命令,又会放过大约 17% 的越界操作,所以文章说它只是沙盒里的一层防御,不能代替沙盒。
资料来源
- Anthropic: How we contain Claude across products https://www.anthropic.com/research/how-we-contain-claude
- Anthropic: Making Claude Code safer with sandboxing https://www.anthropic.com/research/claude-code-sandboxing
- GitHub: anthropics/sandbox-runtime https://github.com/anthropics/sandbox-runtime
- Anthropic Engineering: Building a C compiler with a team of parallel Claudes https://www.anthropic.com/research/building-c-compiler
- GitHub: anthropics/claude-quickstarts, autonomous-coding README https://github.com/anthropics/claude-quickstarts/tree/main/autonomous-coding
- GitHub: autonomous-coding client.py security settings https://github.com/anthropics/claude-quickstarts/blob/main/autonomous-coding/client.py
- GitHub: autonomous-coding security.py command allowlist https://github.com/anthropics/claude-quickstarts/blob/main/autonomous-coding/security.py
- GitHub: anthropics/claude-agent-sdk-python README https://github.com/anthropics/claude-agent-sdk-python
- GitHub: claude-agent-sdk-python types.py https://github.com/anthropics/claude-agent-sdk-python/blob/main/src/claude_agent_sdk/types.py
- GitHub: openai/openai-agents-python, guardrails guide https://github.com/openai/openai-agents-python/blob/main/docs/guardrails.md
- GitHub: openai/openai-agents-python, human-in-the-loop guide https://github.com/openai/openai-agents-python/blob/main/docs/human_in_the_loop.md
- OpenAI Developers: Safety in building agents https://developers.openai.com/api/docs/guides/agent-builder-safety
- Anthropic: How we built Claude Code auto mode https://www.anthropic.com/research/claude-code-auto-mode
- Anthropic: Introducing Claude Opus 4.7 https://www.anthropic.com/news/claude-opus-4-7
- X: Simon Willison on the lethal trifecta https://x.com/simonw/status/1934602159984984235
- X: Boris Cherny on Claude Code auto mode https://x.com/bcherny/status/2044847849662505288


