从运维角度看,AI 智能体就是一个持有凭证的进程。有人为它配置了一个服务账户、一个 API key 或一个 IAM 角色,让它能够完成自己的工作;从那一刻起,凡是这些凭证能够触达之处,它都能触达。岗位职责写的是「每晚导出账单数据」。凭证授予的却是「读写生产数据库、对象存储以及队列」。没有人去对齐这二者。几个月后,团队里没有人能回答一个本应轻而易举的问题:这个智能体究竟能触达什么,又实际触碰了什么?
这一差距并非假设。独立行业研究(CSA/Token)发现,约 82% 的组织运行着自己并不知情的 AI 智能体,而仅有约 21% 维护着对它们的实时清单。看不见的东西无法治理,而当下大多数环境根本看不见自己的智能体——更遑论看清连接每个智能体与其可触达资源之间的那些边。
三个截然不同的问题,却被习惯性混为一谈
「这个智能体能触达什么」之所以难以回答,是因为它把三个相互独立的问题压缩成了一个。把它们区分清楚,才是关键所在。
- 持有(Held)——智能体所拥有的凭证。一个 token、一个 key、一项角色绑定。这是一个身份事实:智能体手里握有哪些门的钥匙。
- 被允许(Permitted)——按照策略,这些凭证被允许执行哪些操作:IAM 授权、数据库角色、网络规则。这是一个策略事实,而且它几乎总是比任何人记忆中的范围更宽,因为授权会不断累积,却没有人去回收。
- 被观测(Observed)——智能体实际被看到在做什么:它触达了哪些资源,以及每一次访问是读还是写。这是一个行为事实,而在您采集到它之前,它根本不存在于任何地方。
大多数安全工具止步于被允许。它读取您的 IAM,然后告诉您该智能体能够向账单导出存储桶写入数据。这是必要的,但远远不够,因为「能够」是成千上万项潜在能力的集合,而人无法对成千上万个「也许」逐一分诊。真正改变对话走向的,是把被观测摆在被允许旁边,去看二者之间的差异。
访问地图
访问地图就是这些边构成的图:对每一个被发现的智能体,绘制一条通向它所能触达的每个资源的边,并标注这条边上实际被观测到的内容。真正重要的单元是边,而边上最重要的标注就是读/写的区分。
**R(读)**表示智能体检索了数据而未对其作出改动:一次 SELECT、一次 GET、一次文件读取。**RW(读/写)**表示它改变了状态:一次 INSERT/UPDATE/DELETE、一次对对象存储的 PUT、一次数据库 schema 变更。这两者的影响范围不可同日而语。一条指向敏感表的读边,是一个暴露面问题。而一条写边——尤其是一条非预期的写边——则是一个完整性问题,也是一个事件(incident)问题。把它们当作同一种「访问」来对待,正是团队错失关键发现的根源。
地图构建完成后,单个智能体的一行看起来是这样的:
| Agent | Resource | Permitted | Observed | Confidence |
|---|---|---|---|---|
| data-export-job | prod-postgres | RW | RW | attributed |
| data-export-job | s3://billing-exports | RW | R | attributed |
| data-export-job | internal-metrics-api | R | — | approximate |
看第一行。这个导出作业被预期从 prod-postgres 读取,再把结果写入 s3://billing-exports。读取源,写入目的地。但它被授予的凭证赋予了对数据库的 RW 权限,而采集器观测到它正在向 prod-postgres 写入——这是一次发生在生产系统记录之源上的变更,没有人审查过、预期过,目前也没有人能解释清楚。被允许(permitted)这一列从一开始就悄无声息地表明这是被准许的。这正是一起事件的种子,赤裸裸地摆在眼前,而如果没有被观测(observed)这一列,就根本没有任何东西可供标记。
被允许减去被观测,等于漂移
最具标志性的产出就是这个差集。**最小权限漂移(least-privilege drift)**正是您用一列减去另一列时所得到的结果,而且它会双向显现:
- 被允许、却从未被观测到——智能体能够触达
internal-metrics-api,但在整个观测窗口内从未触达过。这就是失效范围(dead scope):一项您可以放心提议回收的授权,在不改变任何行为的前提下缩小攻击面。 - 被观测到的行为超出了被审查的范围——那次没有任何人签字批准的、对
prod-postgres的写入。这就是高信号发现:一项正在发生的操作,而如果在配置时有人被征询过,他们绝不会批准它。
第二类正是头条风险,而诚实的产品绝不会凭空捏造它。每一条被观测到的边都带有一个置信度(confidence level)——当操作被关联到某个具体的智能体身份且有佐证信号时为 attributed(已归因),当证据较弱时为 approximate(近似)。您展示的是这一差异本身,而不是把猜测包装成确定性。一个您无法为之背书的发现,比没有发现更糟糕。
被动发现,按智能体逐一归因
有两项设计决策让这一切值得信赖,而不只是又一块仪表盘。
第一,发现是被动的。采集器进行观测——日志、OpenTelemetry 追踪、像 PostgreSQL 的 pgAudit 这样的数据库审计流,以及作为基准事实兜底的内核级 eBPF——并且它不会置身于智能体的数据路径之中。不存在强制代理(proxy)。即使采集器失效,智能体也照常工作;退化的是可观测性,而非生产。对于一个全部意义就在于治理关键基础设施的系统而言,这种不对称性正是其设计所在:下行风险低,信号价值高。
第二,每一项操作都被归因到一个具体的智能体身份,而不是一个共享的服务账户。这就是「导出角色在 02:14 写入了 prod-postgres」这样的审计账本,与「哪个智能体这么做了、归属于谁、对照的是哪一项预期范围」这样的审计账本之间的区别。按智能体逐一归因,是让最小权限具备任何实际意义的前提条件——没有它,一项操作可以被看见,却永远无法追溯到某个负有责任的归属者。
唯一被存储的,就是这些边以及读/写事实。地图记录的是关系——智能体到资源、R 还是 RW——而非负载(payload),不是被读取的数据行,也不是密钥。任何可能携带密钥或 PII 的输入,都会在写入之前被脱敏并经过密钥扫描。这张图之所以敏感,恰恰因为它是一张访问地图,因此它被设计为只保留让治理成为可能的最小信息,绝不多存一分。
来自智能体自身生态的信号有所帮助,但不能单凭其自身就予以信任。一个 MCP server 可能通过诸如 readOnlyHint 这样的注解(annotation)声明某个工具是只读的;按照 MCP 规范,这类提示是不可信的,必须经过佐证,绝不能盲目相信。内核级视角正是用来抓出那种声称只读、却仍然执行了写入的工具。
地图一旦建立,下一步就是执行(enforcement):把导出作业在数据库上钉死为只读,拒绝写入,并在违规时告警而不仅仅是记录日志——这是在访问发生的那一刻就应用的策略,而不是在事件发生之后才去重建的策略。
agent "data-export-job" {
resource "prod-postgres" {
access = "read" # 预期范围:仅读取源
deny = ["write", "ddl"]
}
resource "s3://billing-exports" {
access = "read-write" # 预期范围:写入目标
}
on_violation {
action = "block"
alert = "secops"
}
}
地图会告诉您本应写下的那条策略,因为它向您展示了智能体真正需要的那一项访问,以及它从未用到的那十几项访问。这就是「凭空猜测最小权限」与「从基准事实中推导出最小权限」之间的区别。
如果这正是您的环境今天无法回答的问题——每个智能体能触达什么,又实际触碰了什么——那么这恰恰就是访问地图存在的意义。产品概览详细讲解了发现机制以及「被允许 vs 被观测」的差集;架构则阐述了被动采集、按智能体逐一归因的身份以及审计账本如何协同工作,且自始至终不会置身于您智能体的数据路径之中。