你的 Agent 没察觉到那个沙箱其实是真的
我认识的一个团队有一套教科书级别的 staging 设置。生产数据库的只读副本。一个假装会扣款的 Stripe 模拟账户。带着没人拥有的域名邮箱的合成用户。这个 agent 被要求在 staging 里端到端走一遍"账户欠费"的升级流程,作为发版演练的一部分。trace 看起来很干净,agent 做了它该做的事。
三分钟后,一个真实客户——一个付费客户,六个月前流失的,但仍然留在一个开发者用来播种测试数据的休眠导出里——回复了一封措辞礼貌的逾期付款邮件。那个"send_email"工具,与十几个其他全部以 mock 收尾的工具注册在一起,却接在了生产环境的 Mailgun key 上。两个 sprint 前配置它的开发者当时在快速迭代邮件模板,沙箱层级把发送量限制在每小时五封,把内部反馈循环搞坏了,于是他们换上了真 key"就用一下午",然后忘了换回去。没人复查过。agent 没有任何办法知道这件事。
这就是传统的"在 staging 里用 staging 凭证"规则抓不到的失败模式。这条规则在环境边界上是对的——你的数据库 URL、你的存储桶、你的认证服务商——但 agentic 系统让"staging"成了注册表里每一个工具的属性,而不再是部署整体的属性。一旦某个工具的沙箱支持弱于其他工具,就有人会用生产凭证把缺口糊上,整个 staging 环境就变成一个半真半假的复合体:有些调用无害地消散了,有些则真的落到了收件箱里。
凭证层级漂移现在是每个工具的属性
pre-agent 时代的系统集成点很少,每个都由部署管线从一个环境级别的密钥库里取凭证。如果你在 staging,你手上的每一个 secret 都是 staging 的 secret。边界在 workload 这一层,不在调用点这一层。
Agentic 系统把这件事翻了过来。一个现代 agent 在运行时加载它的工具注册表,每个工具的凭证配置在工具作者觉得方便的地方——有时是每个工具自带的 secrets manager,有时是个 .env 文件,有时是集成 wrapper 里一个"本来只是临时用一下"的硬编码 key。2026 年的密钥泄露数据是宏观证据:GitGuardian 数到 2025 年 GitHub 公开仓库新增了将近 2900 万个 secret,其中 AI 服务的 secret 同比增长 81%,编排和 RAG 基础设施的泄露速度是核心模型提供商的五倍。Agent 这一层比下面那一层有更多的工具、更多的凭证面、更多的未处理边角。
让这件事变危险的是异构性。有些工具有一流的沙箱支持:Stripe 有明显不同的测试模式 key(sk_test_…),AWS 有独立的账户,你内部的 MongoDB 有 staging- 前缀。有些工具没有。Mailgun 有测试模式,但需要额外配置,而且会悄悄降级。Twilio 的沙箱把收件人限制在已验证的号码上,这会破坏"发给 agent 决定的任何人"的能力,于是开发者就为"就这一次测试"换上真号码。有些内部服务根本没有沙箱,你的团队被指望"自己小心点"。Agent 用一个统一的接口调所有这些,根本看不出哪些在假装。
Agent 没有"真实"的概念
这一点会击碎很多人对系统的心智模型。当一个人类工程师写一个脚本,他知道——通过上下文,通过五分钟前敲下的那个变量——这一次调用是真的会发邮件出去。他在脑子里给这个调用打了标。Agent 完全没有这些。工具签名两种情况下看起来一样。工具的 docstring 写着"发送邮件给给定收件人"。Agent 不会去解析凭证字符串来推断环境层级;就算它能,凭证也被 wrapper 藏起来了。
最朴素的修法是把"(STAGING)"加到工具描述里。这比什么都不做还糟。它教会 agent 信任一个任何开发者都可以更新、不需要重新验证的字符串。第一次有人把一个工具定义从 staging 复制到 production 而忘了更新注释,agent 就会读到一个自信地错误的标签并据此行动。Prompt 层的环境提示是一种继承下来的虚构,它压根不该出现在任何安全边界附近。
Agent 真正需要的区分不是环境名。是底层调用的可逆性层级。发邮件是不可逆的。扣信用卡是不可逆的。发到公开频道是不可逆的。从数据库读取是可逆的。写入一个幂等的开发队列是可逆的。这个属性需要被打在工具本身上,被 prompt 之外的东西强制执行,并在规划 trace 里可见——因为一旦 agent 把每一步都当作"试一下看看",工具列表里任何一个不可逆的调用都是一个陷阱。
每个工具的环境证明
能够幸存于这种失败模式的模式,是让每个工具用机器可校验的方式声明它在哪些环境里是安全的——并且在声明与 agent 运行时层级不匹配时拒绝加载。MCP 和 agent-registry 社区一直在朝着这个形状收敛,而细节很重要:
- 每个工具都带着一份证明:一份签名过的 manifest,声明
environments: [dev, staging, prod]、它期望的凭证 profile、以及它的副作用的可逆性层级。 - Agent 运行时有它自己的层级,由部署决定,不由 prompt 决定。一个 staging agent 只加载证明里包含
staging的工具。没有什么标志可以让 agent 翻转,也没有什么环境变量是 agent 可以读取并撒谎的。 - 在 staging agent 里加载一个只允许在生产的工具是一次注册时的硬性失败,不是运行时检查。如果 agent 的 manifest 会让它获得跨层级的能力,这份 manifest 会被拒绝。做邮件工具的团队没法靠改一个配置文件就把 staging 不小心升级了;他们得伪造一个签名才行。
- 每个工具的凭证提供方在发放时检查运行时层级,不只是配置时检查。如果一个工具不知怎的针对错误的层级加载了,密钥管理器会拒绝把 key 交出去。
这就是 Microsoft 和 Kong 在 2026 年文档化的 MCP-gateway 模式:一个控制平面坐在 agent 和工具之间,在工具粒度上强制 ACL,把证明作为执行的前置条件,而不是给模型的一个提示。它之所以可行,是因为强制 是确定性的——在 LLM 之外、对 prompt 注入免疫、并且会在启动时大声拒绝不安全的配置,而不是等到通过愤怒的客户来发现错配。
捕捉漂移的审计
即使有证明,系统还是会漂移。有开发者为了一次性的压测重新配置了某个工具然后忘了改回去。有工具作者更新了 wrapper 然后不小心把它的环境列表放宽了。有个新凭证被轮换进来,但轮换脚本只在生产层级跑了,而 staging 层级却继承了生产的 secret。证明只在它被签名的那一刻有效。
捕捉这种漂移的纪律是一种审计:用 agent 实际拿到的凭证、在它实际运行的环境里、跑它实际的工具注册表,并对每一次调用的落点做断言。几种有效的模式:
- 金丝雀执行:在没有真实收件人的闭环里,用已知能触发每一个工具的 prompt 调用 staging agent。审计 harness 检查每一个出站调用的目的地——它真正会打到的 SMTP 中继、真正的 API base URL、真正的数据库 hostname——并标记任何解析到生产端点的调用。这能抓住那种静态配置检查发现不了的 Mailgun-key 替换。
- 凭证指纹对账:每个工具上报它加载的凭证的哈希,审计把这些哈希交叉比对到该层级已知的密钥库,任何未知哈希都触发停机。这能抓住那种把生产 token 粘到 staging 密钥库里的开发者。
- 副作用复式记账:agent 跑一次,所有写入都重定向到一个 tee,再在真实条件下跑一次;如果两条 trace 显示出不同的目的地,那个工具就是在它的沙箱状态上撒谎。
这些都不是什么奇技淫巧。 它们就是把持续部署的卫生标准套用到工具注册表上,而不是只套用到应用代码上。团队跳过这些的原因,跟它们多年来在底层应用上跳过这些的原因一样:在出事的那天之前,它们看起来都像额外的负担。
沙箱是工具的属性,不是环境的属性
要内化的架构性转变是:"沙箱"不再意味着"开发集群"。对一个 agent 来说,它意味着"这次运行里的每一个工具都被单独证明过不会落到一个真实的副作用上——并且只要有任何一个工具证明不了这一点,系统就拒绝启动"。一个 staging 环境的组合现在是工具粒度上的布尔合取,而单个 false 翻转整个答案。这就是能 scale 的认知:如果你的 agent 有二十个工具,十九个是真沙箱,一个是伪装成沙箱的生产凭证,那么你的 staging 环境不是 95% 安全。对任何会触碰到坏工具的计划而言,它是 0% 安全,而 agent 在事后之前没法分辨哪些计划属于这一类。
实际含义是,agent 的规划面需要比它的工具列表更窄。一个 staging agent 根本不应该能规划出经过不可逆工具的路径,即使是假设性地——注册表在加载时就把它们过滤掉了。一个生产 agent 继承了这些工具,但对不可逆层级有强制的确认、dry-run 或人在环路的关卡。这是 OpenAI 的 Codex agent-approvals 模型在做的事的微缩版本:让用户确认每一个有真实副作用的动作,不是因为模型不可信,而是因为这个面本身就是非对称的。
接下来的纪律
我开头说的那个团队重建了他们的工具层。每个工具现在都带签名证明出货,注册表拒绝混合层级的加载,一个夜间金丝雀任务对着一个假收件人触发每一个工具并用流量捕获断言目的地。上线后的第一周,这套审计又抓出了两个带生产凭证的工具——一个 Slack webhook 几个月来一直指向真正的 #alerts 频道,一个分析追踪器一直把每一次 staging 运行重复记到生产仪表盘上。两者都没造成过可见的事故。两者都不会被人工 review 抓到。
教训不是"staging 已死"。教训是,staging 过去是平台团队在部署边界上拥有的一个属性,而 agentic 系统把这个属性推到了每个工具这一层,在那里它需要它自己的契约、它自己的审计、它自己的偏执。你组合进一个 agent 的工具就是环境漂移的新攻击面,把它们当成你的数据库连接字符串一样严谨地对待,是最低门槛。内化这一点的团队交付的是在启动时大声失败的 agent。没内化的团队交付的是在客户收件箱里悄悄失败的 agent。
- https://blog.gitguardian.com/the-state-of-secrets-sprawl-2026/
- https://www.helpnetsecurity.com/2026/04/14/gitguardian-ai-agents-credentials-leak/
- https://konghq.com/blog/product-releases/mcp-tool-acls-ai-gateway
- https://developer.microsoft.com/blog/securing-mcp-a-control-plane-for-agent-tool-execution
- https://portkey.ai/blog/tool-provisioning-in-mcp-servers-controlling-ai-agent-access-in-production/
- https://www.chenyezhu.com/writing/tool-eligibility-deterministic-guardrails-ai-agents/
- https://developers.openai.com/codex/agent-approvals-security
- https://www.mindstudio.ai/blog/ai-agent-safety-system-vs-model-problem
- https://medium.com/@BuildandDebug/we-ran-this-on-production-and-nothing-broke-the-truth-about-dry-runs-055c18fd8164
