跳到主要内容
治理这层卡住的不是规则,是身份

治理这层卡住的不是规则,是身份

2013入行
2013入行

· 阅读约 8 分钟

一个策略,作用域设好、状态激活、规则挑不出毛病,跑一遍,返回 ALLOWED,什么都没拦住;同一套演示系统的另一块仪表盘上挂着 43% 的错误率、37 次执行、7 天累计成本 0.002 美元,而这个错误率是每次演示运行按设计注入一次尝试得来的,不是待修的故障。两个数字并排摆着,比原文里逐条映射监管条款的部分有意思得多:治理层可以在某个具体拓扑里彻底失效,失效得毫无声响,它只是放行;而当它真的开始工作时,第一个尖起来的指标不是吞吐也不是延迟,是拒绝率。我们对一个"正常运行"的服务默认期望错误率是低的,这套系统里恰好反过来。

那个 ALLOWED 来自三种不同的失败,三种都跟规则写得对不对无关。

一种是策略要匹配的信号根本不在 span 里。一个工具密集、单次运行花费接近零的代理团队,挂一个花费上限策略上去,永远不会触发,不是策略写得不对,是每个 span 上都没有可计量的花费。模型边界策略同理,它需要治理层能自动 patch 掉 LLM 客户端才看得到用了哪个模型;如果这个团队的模型调用走的是框架自带的 Bedrock 客户端,不在自动 patch 的白名单里,边界策略也就没有可看的对象。两条原因不同,落到结果上一样:No Match。

另一种是策略作用在了错误的身份上。多代理团队是嵌套的,入口代理只负责委派,真正发起工具调用的是被委派下去的子代理,而每一次调用都是在子代理自己的运行身份下被检查的。策略如果写在入口代理那个身份上(那是你最想当然会写的位置,因为你本来就是从那里进来的),它一辈子不会匹配到任何东西。这不是配置失误,是 agent 拓扑的结构性质。

还有一种最不容易发现:执行平面根本没被叫醒。没有配置 trace 上报端点的时候,SDK 记录过一行警告然后静默跳过逐调用的策略执行,返回值仍然是 ALLOWED。只给 key 不给 endpoint,拿到的是一个和成功长得一模一样的通过。

三种失败指向同一个结构事实:观测、阻断、证据是请求路径上三个不同的保证,不是一个能力的三个成熟度档位。事后告警证明某件事发生过;运行时策略决定这件事能不能发生;审计包事后说明在哪个身份下、依据哪条规则做出了这个决定。拿到其中一个,不自动拿到另外两个。把这三件事混成一个词"治理"来讨论,是现在这个赛道大多数对话跑偏的起点。规则可以写得很漂亮,可达性为零,治理层就是一排装饰。

为什么身份这件事在 agent 上突然变难,拆开看并不复杂。传统 web 服务里,一次请求从头到尾是一个身份:调用方带着凭证进来,鉴权中间件看一眼,后面的所有操作都在这个身份下完成。"谁在问"和"谁在做"是同一件事,所以访问控制模型按前者设计就够了。Agent 把这两件事拆开了。父代理代表的是"这个请求从哪来",子代理代表的是"这一步操作实际以谁的名义执行",中间还夹着若干层委派,层数在运行时才确定。阻断要挂靠的位置永远是后者,而绝大多数人写策略时的直觉指向的是前者。

把镜头拉远一点看,控制平面落后数据平面一个身位,这件事在别处也发生过。云刚起来那几年,IAM 的角色模型比实际服务的拓扑简单得多;service mesh 早期处理调用链身份,也经历过一段"要么全拦要么全放"的粗放期,直到跨进程的身份传播约定成熟,细粒度的准入控制才真正落地。路径是一致的:可观测性先到,准入控制后到,中间隔一层身份。这套分层去年这时候也套过一次,当时用在另一件事上,骨架没怎么变。

比上面三条更值得记的是逐调用策略检查的默认姿态:始终 fail-open,而且没有覆盖开关。把 fail_open 设成 False,读起来像是"任何时候都要停下来",实际加固的只是运行前的状态检查,真正在调用路径上做决定的那个引擎,网络抖动一下就会放行单个调用,配置上关不掉。这不一定是个坏设计。对绝大多数还在验证阶段的部署者来说,一个默认阻断的治理层会在他们把它调对之前先把所有演示拦死,获客成本太高。厂商按获客的成本结构选默认值,这个选择本身没什么可指责的。但对高风险流程来说,"治理在请求路径里"这句话有一个边界,得由部署方自己画出来,而不是靠拧一个开关。

同一套材料里还有一个切分方式值得看:进程内的阻断和脱敏不需要任何 key,也不用账号,本地就能跑;跨网络的运行时执行和证据中心要 key。翻译一下,阻断免费,证据收费。厂商自己判断价值在哪一侧,答案很明确,在事后那一侧。

说到证据,这层现在的成熟度从几个细节能看出来。审计包用的完整性哈希是无密钥的 SHA-256,只能防篡改,不是签名。任何能重写这个包的人,也能把哈希重新算一遍,重新生成的记录会用自己的方式完美自洽。这不是某家产品特有的毛病,是所有"记录系统自己给自己盖章"方案的共同缺口。要让证据在事件发生之后仍然站得住,摘要必须离开被记录系统的边界,交给一个它控制不了的见证者,哪怕只是把一个不透明的 32 字节摘要在外部时间戳服务上锚一下,PII 那条线也不用碰。

另一头,从注册系统导出的那份监管预填里,高风险分类字段是空的,因为注册表的字段不会从 span 上的标记自动流过去,需要人手填。产品在这里带了免责声明,说它本身不是官方注册机构。这个诚实挺好,但也暴露了证据层现在的实际形态:结构化框架已经有了,字段级的自动化还没接上,中间那段靠人手补。

还有一个反直觉的后果:一次被硬阻断的运行,trace 树是空的,下游子代理一个 span 都没有。证据最要紧的那些时刻(一次成功的阻断),手头的 trace 反而最贫瘠,真正扛事的是一条 decision id,不是一棵丰满的调用树。监管要的恰恰是风险事件的记录,不是干净事件的记录。

接下来这条脉络会往哪走,我的判断是治理这层会分家,不会合并。阻断留在进程内,推理循环已经跑起来之后要打断它,只有和执行同进程的组件做得到,任何跨网络的调用在时序上都慢一拍,最多做到"下一次别再来";证据、审查、事件记录、影响评估这些事后能力会被平台层吃掉,因为它们的规模效应来自跨系统聚合。进程内那半层会趋于免费和标准化,像今天的日志库;平台那半层会收订阅费,像今天的安全信息和事件管理系统。这个分法和操作系统、中间件走过的路径是反的,通常是控制面吃掉数据面,原因是 agent 的执行时序太特殊,阻断这个动作对延迟的容忍度接近零。

如果这个判断成立,接下来真正稀缺的东西不是策略引擎,是一套跨进程、跨框架传递调用身份的约定。有它在,控制面才有可能在事后把每一次阻断、每一次放行、每一次委派准确归到具体的代理身份上;没有它,平台层能做的事就永远停在聚合和展示。上面那三条"什么都没匹配到"的策略,在最底层给出的是同一个提示。

压注:我倾向于认为未来两三年里,进程内治理会先一步标准化,大概率以一个开源 SDK 加一组 span 上的约定字段的形式成形;平台层的证据能力晚半步收敛,两者之间的接口就是身份传播。如果哪天出现一套被主流 agent 框架普遍采纳的跨进程身份传播约定,而且控制面能可靠地拿到逐调用的委派身份,那"阻断留在进程内"这个判断就该扔掉,换成"控制面重新吃掉执行"。在那之前,把策略挂在入口代理的身份上、然后在仪表盘上看着 No Match 发呆,会是这个行业最常见的一种配置错误。