事件模式
当有报告称某个智能体集群触碰了它本不该接触的政府或医疗门户网站时,事后复盘很少会发现权限提升。没有内核漏洞利用,没有被盗凭证,没有授权服务中的混淆代理缺陷。只有一个发往智能体本不该解析的主机名的出站 HTTP 请求,以及一个返回的响应。
这类报告摘要中容易丢失的细节是:智能体并未获得新能力。它获得的是一个可触达的主机名。它发起请求所需的一切——TLS 栈、DNS 解析器、清单中的 fetch 工具——早已存在并按设计正常工作。失效不在于智能体能做什么,而在于智能体能去哪里。
针对公共部门门户网站的智能体活动报告都集中在这一形态上。有趣的问题不是模型为何选择了那个主机名,而是网络为何允许了该连接。
为什么提示词层面的护栏守不住这条线
指令是概率性的。网络可达性是确定性的。这种不对称就是全部论点。
一个写着“不要联系政府系统”的系统提示词,是 token 序列上的先验。它在大多数时候成立,但会以难以枚举的方式退化:一个重新框定目标的多步计划、一个提供了意外 URL 的工具结果、一个约束远离决策点的长上下文、一个将外部链接当作指令的检索命中。这些都不需要对抗性行为者,只需要一个看似合理的 token 序列。
被封锁的主机名则性质不同。它在智能体无法控制的咽喉点被强制执行一次,并对每一次未来运行、每一个模型版本、每一个温度设置、每一次重试都同样适用。它不需要在提示词中重复声明,不会随上下文长度衰减,也无法被足够流畅的补全说服。
这不是反对提示词层面护栏的论点。它们对于塑造意图和廉价、早期的拒绝很有用。但对于一个必须在模型出错、疲惫或被引导时仍然成立的边界来说,它们只是错误的层次。一个能被说服而不存在的边界,不是边界。
设计出口契约
实用单元是出口契约:一组声明式规则,定义智能体身份可以触达什么、在什么条件下、持续多久。做得好时,它比主机名允许列表更具体。
按目的地、方法和载荷类别允许

仅凭主机名太粗粒度。一条允许 api.example.gov 的规则允许了每条路径、每种方法、每个请求体。更严格的契约将三件事绑定在一起:
- 目的地:scheme、host 和路径前缀——
https://api.example.gov/v1/records/而非整个源。 - 方法:
GET和HEAD是读取路径;POST、PUT、PATCH和DELETE是写入路径,应单独论证。 - 载荷类别:可以离开的内容形态。只发送标识符的检索智能体与能附带文档正文的智能体风险不同。载荷类别可以通过 schema、大小上限或代理处的分类器来强制执行。
绑定这三者的规则是可审计的,而主机名列表则不是。当审查者问“这个智能体是否可能向医疗门户提交了数据?”时,答案是查表,而非推断。
默认拒绝,凭显式工单放行
默认姿态是拒绝。触达通过例外授予,例外需列明:
requesting_agent: intake-classifier-v3
purpose: fetch public reference data for citation
destination: https://api.example.gov/v1/records/
methods: [GET, HEAD]
payload_class: identifier-only
requested_by: platform-team
justification: ticket OPS-4821
expires_at: 2026-09-30T00:00:00Z
过期时间是承重字段。永久例外会累积;会过期的例外则强制定期重新论证,而过时的触达正是在此被清除。工单标识符将网络规则与授权它的人工决策绑定,因此事件复盘可以从一个连接追溯到一个人和一条理由。
将拒绝记录为一等事件
大多数代理详细记录被允许的流量,而将拒绝记录为一个计数器。反转这种侧重。一次拒绝应携带智能体身份、尝试的目的地、方法、触发的规则,以及回溯到该次运行的关联标识符。
拒绝频率是提示词漂移的先行指标。稳定的智能体产生稳定、低频率的拒绝,大多来自重试和拼写错误。频率上升,或针对某一目的地类别出现新的拒绝集群,通常意味着智能体行为已经偏移,且尚未发生任何成功的窃取。拒绝是早期信号,成功是晚期信号。
一个具体机制:出口代理
一种可行的实现是在智能体运行时与网络之间放置一个代理。智能体的 fetch 工具不打开套接字,而是用结构化请求调用代理:

{
"agent": "intake-classifier-v3",
"run_id": "r-8f21c",
"method": "GET",
"url": "https://api.example.go
代理根据编译后的契约解析请求,评估目的地、方法和载荷类别,然后代理该调用或拒绝它。拒绝与成功写入同一个仅追加日志,附带规则标识符和运行关联。智能体工作负载的 DNS 被固定到代理,因此不在契约中的主机名从一开始就无法解析。
代理是唯一可审计的边界。只有一个地方需要审查,一个地方需要变更,一个地方让拒绝成为记录在案的事实,而非证据的缺失。
## 诚实的权衡
集中式出口代理是一种依赖。它为每次出站调用增加一跳延迟,对需要网络的智能体而言是单点故障,并且需要运维纪律:契约变更必须像代码一样被审查、部署和版本化。跳过这种纪律的团队最终会得到一个被“临时”绕过的代理,而临时绕过会变成永久漏洞。
代价是真实的。替代方案是一个触达范围由工具结果中恰好出现的主机名所定义的集群,那根本不是边界。延迟和审查队列比无界的出口面更便宜。
## 运维后果
将出口视为契约会带来两种实践。
**出口规则成为版本化产物。** 它们与智能体的工具清单存放在同一仓库,并在同一次变更中审查。一个添加了从新源抓取的工具的拉取请求,必须添加或修改相应的出口规则,审查者会同时看到两者。工具清单说明智能体打算做什么;出口契约说明它被允许做什么。两者之间的分歧是审查发现,而非运行时意外。
**事件复盘从网络日志开始。** 出问题时,第一个产物是受影响运行窗口的代理日志:目的地、方法、载荷类别、拒绝,以及涉及的规则标识符。之后才查阅对话记录以理解意图。这个顺序很重要,因为网络日志是确定性的,而对话记录是一种重建。从日志开始,能在任何人争论“模型是什么意思”之前回答“实际离开了什么”。
## 结语
智能体触达了它不该触碰的门户,主要不是一个模型行为的故事,而是一个路由的故事。触达是在某处被授予的——通过一个宽松的默认值、一个过时的例外、一个未固定的解析器——而修复正属于那个授予点。提示词层面的护栏塑造意图;它们不强制执行触达。一个可审计的出口边界,默认拒绝,以与成功同等的保真度记录拒绝,并与工具清单一起版本化,才能让“这个智能体是否可能触达那个主机?”的答案成为查表,而非希望。
_封面照片:Brett Sayles / Pexels。_
