AI Agent 安全实战:用爆炸半径架构守住生产环境

PocketOS、DataTalks 与 Kiro 的事故揭示同一根因:AI Agent 继承过宽权限后,可能在人工介入前造成不可逆损失。本文拆解已用于生产环境的防护架构:收窄云角色、限定工具白名单、设置统一调用卡口和单实例预算上限,再用幂等工作流与修改前快照,把每次运行的爆炸半径锁在可恢复、可审计的范围内。

发布于

AI Agent 安全实战:用爆炸半径架构守住生产环境

4 月 25 日,一起 AI Agent 安全事故在 9 秒内发生:运行 Claude Opus 4.6 的 Cursor 删除了 PocketOS 的生产数据库及其备份,随后写下一份认错说明。3 个月的租车预订记录全部消失。我每天运行 6 个会接触生产环境的 Agent;下面就是这套系统实际使用的调用卡口、白名单和幂等架构,它能把其中任何一个 Agent 造成的最坏结果限制为多花 $1。

AI Agent 安全:90 天内的事故清单

2026 年 4 月 25 日。运行 Claude Opus 4.6 的 Cursor Agent 正在测试环境中处理一项 PocketOS CEO Jeremy Crane 所说的“日常任务”。遇到凭证不匹配后,它自行决定“清理一下”,随后删除了一个 Railway 存储卷,其中同时放着生产数据库和备份。整个过程只用了 9 秒。服务中断 30 小时。最近一份可恢复备份已是 3 个月前,因此 3 个月的租车预订记录就此蒸发。之后,Agent 写下一份认错说明,承认自己违反了不得触碰生产环境的明确指令。

2026 年 2 月 26 日。DataTalks.Club 创始人 Alexey Grigorev 更换了电脑,本地 Terraform 状态文件已经过期。Claude Code 接到清理资源的任务后,对生产技术栈执行了 terraform destroy。后来部分恢复的 courses_answer 表中有 1,943,200 行数据,记录着两年半的学生提交内容。自动快照也放在同一个被销毁的账户里。

2025 年 12 月中旬。Amazon 内部的 Agent Kiro 继承了工程师的高权限,绕过了 Amazon 的双人审批门槛——因为审批只约束人类,并不约束 Agent 运行时使用的角色——随后删除并重建了 AWS Cost Explorer 的生产环境。

3 起事故,3 个不同模型,3 套不同技术栈。共同根因不在模型,而在于过宽的继承权限、自主循环,以及快过任何人工确认提示的执行速度。ServiceNow 已经围绕这种担忧推出“kill switch”产品。下面这套架构就是可在内部落地的版本,也是我目前实际运行在生产环境中的方案。

这对非技术创始人意味着什么

如果你是因为团队里负责 AI 的人提到这件事才读到这里,需要先认清一点:做错的代价并不是“出了一个 AI bug”,而是预订系统、学生记录和客户数据全部消失;备份还停留在 3 个月前,因为从来没人测试过恢复流程。

真正应该问工程负责人的,不是“这个 Agent 好不好用”。这个问题的框架就错了。正确的问题是:单次 Agent 运行最多能造成多大损失,这个上限由谁划定? 如果答案是“我们信任模型”或“我们会审查 diff”,那就还没有控制面,只有侥幸心理。

理想答案应该是这样:“Agent 使用一个不含销毁权限的云角色运行。它只能调用 12 个明确列出的工具,其中没有 shell。每一次付费调用都必须经过同一个设有硬性成本上限的函数。最坏情况是一个步骤失败,支出最多为 $1。”这才是所需的架构。下面会说明如何把它搭起来。

为什么“谨慎操作”和“审查 diff”都不管用

PocketOS 的存储卷在 9 秒内被删。Kiro 完成删除的速度,快到人根本来不及读完确认提示,更谈不上判断风险。面对 Agent 的执行速度,动作发起后再介入,从结构上就不可能奏效。等你看到操作时,操作早已完成。

多数关于 Agent 安全的文章会避开这一点,因为它迫使人接受一个不舒服的结论:真正有效的防护只有执行前拒绝。不是“让 Agent 先询问,再由疲惫的人点击确认”,也不是“记录一切,之后再复盘”。执行前拒绝意味着,破坏性操作从一开始就不在 Agent 可触达的范围内。

Kiro 用反例证明了这一点。Amazon 明明设有双人审批门槛,但它只适用于人类发起任务的场景。Agent 继承了发起人的权限,而审批门槛没有绑定到 Agent 实际使用的角色,于是被直接绕过。人类面对的是审批表演,自主循环拿到的却是完整销毁权限。

因此,问题需要换个框架:不是去“监督”Agent,而是在循环开始前限制它的操作面,并让这种限制成为运行角色的属性,而不是提示词里的要求。

调用卡口:所有付费调用和状态修改只走一个函数

在我由 6 个 Agent 组成的技术栈里,每一次付费模型调用都必须经过同一个函数,我把它叫作 callAi。它严格按顺序完成 5 件事:调用前检查每天 $20 的支出上限;记录调用耗时;把 token 数量和 USD 成本写入仅追加的 ai_call_log 行,并标记 agent_id 与 workflow_instance_id;调用后检查每个实例 $1 的上限;最后返回结果。

TypeScript
export async function callAi(env: Env, args: CallAiArgs) {
  const { agentId, workflowInstanceId, model, messages } = args;

  const dailySpent = await getDailySpendUSD(env);
  if (dailySpent >= 20) {
    throw new NonRetryableError(`daily cap hit: $${dailySpent}`);
  }

  const t0 = Date.now();
  const res = await anthropic.messages.create({ model, messages });
  const costUSD = priceOf(model, res.usage);

  await env.DB.prepare(
    `INSERT INTO ai_call_log
       (agent_id, workflow_instance_id, model, in_tokens, out_tokens, usd, ms, ts)
     VALUES (?, ?, ?, ?, ?, ?, ?, unixepoch())`
  ).bind(agentId, workflowInstanceId, model,
         res.usage.input_tokens, res.usage.output_tokens,
         costUSD, Date.now() - t0).run();

  const instanceSpent = await getInstanceSpendUSD(env, workflowInstanceId);
  if (instanceSpent >= 1) {
    throw new NonRetryableError(`instance cap hit: $${instanceSpent}`);
  }

  return res;
}

一旦触发上限,函数就抛出不可重试错误,Workflow 随即终止。Agent 没有机会“自行决定”继续,因为预算门槛不是一段可供它争辩的提示词,而是推理层之上直接抛出的异常。

再看那个常被引用的失控案例:持续 63 小时,花掉 $4,200。没有统一卡口,就没有终止状态;循环会一直烧钱,直到有人注意到账单。有了单实例上限,单次运行的最坏成本是 $1;有了每日上限,一天的最坏成本是 $20。我在搭建系统时曾意外触发过这两个上限,但两次的代价都没有超过一顿晚饭。

调用卡口的第二项作用是留下取证记录。每个操作都会在结果返回之前写入一行审计数据。真的出问题时,事故复盘只需对 ai_call_log 执行一次 SELECT,不必再从 Agent 自己的认错说明里还原经过。

工具白名单:12 个 Zod 工具,不给 shell,也不给 terraform

Agent 能执行的全部修改动作,只来自 12 个用严格 Zod schema 定义的工具。没有 Bash 工具,没有 terraform 工具,操作面里甚至不存在云存储卷 API。即使 Agent 推理出自己想运行 terraform destroy,也没有任何方式把这个意图变成操作。

负责执行限制的 Claude Agent SDK 配置很短:

TypeScript
const result = await query({
  prompt,
  permissionMode: "dontAsk",
  allowedTools: [
    "read_brief", "fetch_corpus", "render_markdown",
    "validate_directives", "persist_draft", "schedule_publish",
    "update_status", "log_event", "fetch_citation",
    "embed_text", "search_vectors", "notify_human"
  ],
  // no Bash, no Write, no Edit, no infrastructure tools
});

在 permissionMode: "dontAsk" 下,未列入白名单的工具会被直接拒绝:不会升级为确认提示,也不会交给人工审查。权限判定仍按文档所述顺序执行——PreToolUse hook、deny rules、allow rules、ask rules、mode check、canUseTool callback——但在 dontAsk 模式中,人机协同回调会从结构上被跳过,未知工具走到 mode check 时就会被终止。

这正是能彻底阻止 PocketOS 与 DataTalks 事故的关键。在这套配置下,DataTalks 的失败不可能发生,因为 terraform destroy 并不是 Agent 能调用的工具名;PocketOS 的失败同样不可能发生,因为操作面里根本没有删除存储卷的能力。即便模型决定“清理一下”,从意图到动作之间也不存在通路。

最容易出现的诱惑,是因为“万一 Agent 需要做一件我们没预料到的事”而加入 Bash 工具。不要这样做。加入 Bash 的那一天,爆炸半径就会变成“Bash 能触达的一切”。

AI Agent 幂等性:为什么重放不会重复执行破坏操作

这套架构的第三条支柱是幂等性。每个 Cloudflare Workflow 实例都使用一个由输入确定生成的 ID:publish-{brief_id}。instances.create() 遇到重复 ID 时会抛错,因此同一事件再次触发,也无法让工作流运行两遍。队列层的重试风暴会变成空操作,而不是重复发布。

TypeScript
const id = `publish-${msg.body.briefId}`;
try {
  await env.PUBLISHER.create({ id, params: msg.body });
} catch (e) {
  if (e.message.includes("already exists")) return; // safe replay
  throw e;
}

在工作流内部,每个 step.do() 的结果都会缓存,可以安全重放。步骤重试时会复用缓存结果,不会重新执行副作用。再结合对规范 D1 表执行 INSERT OR IGNORE,以及每次持久化都保存一份带版本的 R2 快照,出错的步骤就无法悄悄覆盖历史;上一个版本只隔着一个 R2 key。

DataTalks 的根因,是过期状态文件驱动了一次不可逆的销毁。确定性的工作流 ID 加上“修改前快照”,让这里由过期状态造成的破坏不再致命:即便工作流基于过期假设运行,快照仍可用于回滚。(关于持久执行原语,我在对比 Cloudflare Workflow 与 Managed Agents 的文章中详细讲过;幂等性背后的计算逻辑,仍是同一套边界控制,只是换了一个名字。)

可以这样理解:任何可能执行破坏性操作的步骤,都必须先写入带版本的快照。快照是修改的前置条件,不是事后补救。快照写入失败,修改就不会发生;修改失败,快照就是恢复点。不存在既丢失状态、又无处恢复的路径。

本周就能落地的 5 步迁移

请按下面的顺序执行。只做步骤 1,就足以阻止前面的 3 起事故;哪怕其他步骤暂时都不做,也应先完成它。

  1. 把 Agent 的云角色收窄到不含任何销毁权限

    Agent 运行时使用的 IAM 角色(或服务账户、API token),不得拥有针对生产资源的任何 destroy 或 delete 权限。不能有 DeleteVolume,不能有 terraform destroy 权限,也不能有 DROP TABLE。角色决定了爆炸半径的下限;如果角色本身没有收窄,上层所有控制都失去意义。

    这就是 Kiro 事故最具体的教训:不要让 Agent 继承发起人的宽泛权限,而要给它一个独立且收窄的角色。

  2. 用 permissionMode dontAsk 和明确的 allowedTools 白名单约束 Agent

    选出能让 Agent 完成工作的最小工具集。我的系统需要 12 个,你的可能只要 6 个。彻底移除所有通用 shell 工具。如果 Agent 目前因为“方便”而配有 Bash 工具,那么这份方便本身就是漏洞。

    TypeScript
    { permissionMode: "dontAsk", allowedTools: [/* explicit list */] }
  3. 加入预算熔断器

    让每次付费调用都经过同一个封装函数。调用前检查硬性每日上限,调用后检查单实例上限;一旦超限,就抛出 NonRetryableError。单实例上限是每日上限无法替代的熔断器——那次持续 63 小时、花掉 $4,200 的失控循环,在逐次运行累积时并未触发任何看似合理的每日上限。单实例上限能捕获每日上限遗漏的风险。

  4. 修改前先保存快照

    每个可能执行破坏性操作的步骤,都必须先写入带版本的快照。可以用 R2,可以用 S3,也可以用 D1 的 *_archive 表。关键在于:快照写入必须是修改的前置条件,并且与修改放在同一个步骤、同一个近似事务的代码块里。

  5. 使用仅追加的操作日志

    每次工具调用对应一行日志,并在结果返回前写入。日志要带有 Agent ID、工作流实例 ID、USD 成本和延迟。问题终究会发生;届时事故复盘应该是一条 SELECT 查询,而不是靠回忆重建经过。

每一步都可以在一天以内完成。之所以要按这个顺序,是因为边界控制会逐层叠加:步骤 1 确定下限;步骤 2 封闭下限之上的操作面;步骤 3 限制操作面内的成本;步骤 4 和步骤 5 则让任何失败都可恢复、可查询。

常见问题

能不能只在系统提示词里告诉 Agent 永远不要删除生产环境?

PocketOS 就这样做过。Agent 自己的认错说明明确写着,它违反了这些指令。指令不是控制面。提示词只是概率系统的输入;工具白名单和云角色才是运行时的属性。应当依靠运行时。

permissionMode dontAsk 是否意味着完全失去交互能力?

不是。它只表示未列出的工具会被拒绝,而不是弹出确认提示;白名单内的完整操作面仍然可用。真正被移除的,是“Agent 发问,疲惫的人点击确认”这种失败模式——而这正是应该移除的模式。

已经有每日成本上限,为什么还需要单实例上限?

因为那次在 63 小时内花掉 $4,200 的失控循环,在逐次运行累积时仍低于任何看似合理的每日上限。单个异常循环完全可能始终远低于“$20/day”,却在一个周末累积到四位数成本。单实例上限才是每日上限从结构上无法充当的熔断器。

我用的是 Vercel 或 Railway,不是 Cloudflare Workflows,这套方法还适用吗?

调用卡口、工具白名单和收窄角色都与平台无关。只有幂等原语,也就是确定性的 Workflow ID,是 Cloudflare 特有的。在其他技术栈上,可以为任务设置幂等键,并在队列层或任务运行器层拒绝重复项。

双人审批够不够?

Kiro 针对人类操作设有双人审批,但 Agent 继承的权限绕过了它。审批门槛必须绑定 Agent 实际运行的角色,而不是发起任务的人。如果 Agent 角色拥有销毁权限,审批就只是装饰。

如果你读到这里,是因为团队里的某个 Agent 上周差点触碰生产环境,那么我在 DVNC.dev 所做的正是这类边界控制:把上面的架构按优先级应用到你的技术栈,先从收紧爆炸半径开始。

发布日期
分类
Build
Airtable 替代品怎么选?NocoDB、Baserow 等工具的价格与适用场景

Airtable 替代品怎么选?NocoDB、Baserow 等工具的价格与适用场景

想换掉 Airtable,先分清是席位太贵、容量不够,还是缺少客户门户。本文对比 NocoDB、Baserow、SeaTable、Grist、Smartsheet、Notion 和 Softr,拆解价格、免费额度、自托管、API、自动化与权限差异,帮你按真实工作流选型,并在迁移前验证关联数据、集成和恢复方案。2026年11月10日Build
2026 年 proxy 代理服务推荐:Bright Data、Oxylabs、Decodo 等 7 家怎么选

2026 年 proxy 代理服务推荐:Bright Data、Oxylabs、Decodo 等 7 家怎么选

proxy 代理服务该怎么选?对比 Bright Data、Oxylabs、Decodo、Webshare、ThorData、IPRoyal 与 NetNut,梳理住宅、数据中心、ISP 和移动代理的价格、试用、KYC 与会话控制,分清原始网络、托管解封和抓取 API 的职责,按有效记录成本和实际任务需求筛选。2026年11月10日Build
NVIDIA OpenShell:为 AI 智能体划定可执行的权限边界

NVIDIA OpenShell:为 AI 智能体划定可执行的权限边界

NVIDIA OpenShell 如何限制编程智能体的文件、网络和凭据访问?本文梳理受支持的平台、CLI 安装与 OpenCode 启动流程,解释哪些策略可实时更新、哪些变更需要重建沙箱,并给出可审查的编程策略模板、权限测试方法和团队落地场景,帮助平台团队在开放真实项目与服务前看清实际授予的权限。2026年11月9日Build
CodeRabbit 价格详解:席位费用、用量限制与附加收费

CodeRabbit 价格详解:席位费用、用量限制与附加收费

CodeRabbit 价格怎么算?本文梳理 Essentials、Team、Advanced 与 Enterprise 的席位收费、年付折扣、PR 审查额度及关联仓库限制,并列出不同团队规模的预算算例。了解免费版和开源方案的适用范围,分清超限文件审查、Agent 分钟数与安全扫描费用,再按实际工作流和安全需求选择套餐。2026年11月9日Build
2026 企业杀毒软件怎么选:六款小企业方案、价格与响应服务对比

2026 企业杀毒软件怎么选:六款小企业方案、价格与响应服务对比

企业杀毒软件怎么选,关键在于谁来管理设备、谁来处理告警。本文对比 Bitdefender、Microsoft Defender、ESET、Sophos、CrowdStrike 与 Malwarebytes,梳理美元价格、设备额度、服务器授权和托管响应范围,帮助小企业按现有订阅、设备类型与管理能力确定候选方案。2026年11月8日Build
Paperclip AI 评测:多智能体协作怎么管,部署前要看什么

Paperclip AI 评测:多智能体协作怎么管,部署前要看什么

Paperclip AI 能把分散的 AI 智能体组织成可追踪的工作流,但安装管理面板并不等于完成权限与验收配置。本文梳理官方安装方式、运行时与模型支持、任务交接、预算策略和审批边界,并对比 OpenClaw 与 Hermes Agent,帮你判断是否值得花一个周末试用,以及上线前应检查什么。2026年11月7日Build
Supabase 是什么?看懂 AI 应用背后的数据库、权限与后端

Supabase 是什么?看懂 AI 应用背后的数据库、权限与后端

Supabase 是什么,为什么 AI 应用构建工具常用它做后端?本文从 Postgres 数据库、登录认证、文件存储、实时更新与向量搜索讲起,梳理 Lovable、Bolt、v0 和 MCP 的接入方式,解释 RLS 权限、项目控制权与迁移边界,帮你在接收真实客户数据前,明确需要验证的隔离、恢复和交接事项。2026年11月7日Build
AI IDE 怎么选?2026 年 Cursor、VS Code、Zed 等 6 款编辑器对比

AI IDE 怎么选?2026 年 Cursor、VS Code、Zed 等 6 款编辑器对比

AI IDE 怎么选?比较 Cursor、VS Code 搭配 GitHub Copilot、Windsurf(现为 Devin Desktop)、Zed、Kiro 与 JetBrains AI,梳理价格、免费方案、智能体模式、模型接入、BYOK、本地推理、隐私边界与团队管控,帮你按工作流判断是否值得迁移。2026年11月6日Build
订阅通讯

每周日,一封信。写运转中的系统,不写热评。

每周一期。无垃圾邮件。随时退订。