跳转到主要内容

可证明的控制:将治理从仪表盘转向强制不变量

可观测性记录 agent 做了什么。治理则需要约束,使某些操作无论模型如何决定都无法发生。

硬盘电路与接口的特写,库存照片

观察的局限

Agent 系统通常通过观察来治理。系统发出 trace,搭建仪表盘,设定阈值,并建立 on-call 轮值。这很有用,但它与“控制”是两种不同的属性。仪表盘观察的是已经完成的动作。等到某个面板变红时,tool call 早已执行,写入早已落盘,外部副作用早已发生。监控栈能做到的最好程度,只是缩短损害与发现之间的时间间隔。

观察还依赖于遥测数据的完整性。如果某个工具封装忘记发出事件,如果某条重试路径绕过了已插桩的客户端,或者如果某个新集成上线时没有带上 tracing 装饰器,那么仪表盘恰恰会对那些最不被理解的操作保持沉默。遥测覆盖的缺口并不是均匀分布的;它们聚集在最新、最少被审查的代码路径上,而那里正是事故集中的地方。

一个拥有宽泛权限的被监控 agent,仍然是一个宽泛权限的 agent。可观测性并不会缩小可达状态集合。它只是给一间仍然没上锁的房间加了一台摄像头。权限模型才是决定“什么可能发生”的东西;仪表盘只决定“什么会被注意到”。

告警阈值还会带来进一步的扭曲。阈值被调校以降低噪声,这意味着它们被调校为容忍一定速率的低级别异常。对于检测而言,这是合理的运维折中,但对于预防而言,这是错误的工具。一条“当敏感操作在每个时间区间内发生超过配置次数时触发”的策略,就是一条允许前几次发生的策略。预防需要在第一次发生之前做出决定,而不是在它之后做速率限制。

实际后果是:仅以监控形式表达的治理,往往会收敛到同一种失效模式——系统在总体上被充分理解,在具体处却不受约束。总体视图对于容量、成本和质量分析确实有价值。但它不是控制面。

编码不变量

替代方案是把策略表达为前置条件,在 agent 的意图转化为动作的边界处进行检查:也就是 tool call。Agent 拥有的每一项能力都通过某个工具接口可达,而该接口是能够强制执行决定的最窄之处。如果检查发生在这里,那么它对每个调用方都生效,包括在策略编写之后才加入的调用方。

Close-up of three alkaline batteries on a white surface

前置条件是针对拟议调用及其周围上下文的谓词。它可以引用参数、发起请求的 agent 身份、会话、被触及的资源以及环境状态。以下是形态示例,而非具体内容:目标资源必须位于允许列表内的命名空间;当会话未被提权时,操作必须是读取;载荷不得包含被标记为受限的字段;调用必须附带由独立工作流签发的有效审批 token。

类型化 schema 使这一点可强制执行,而不是停留在愿望层面。如果工具的输入由带有必填字段、枚举值和有界类型的 schema 描述,那么格式错误的调用会在解析阶段失败,早于任何 handler 运行。fail-closed 行为源自 schema,而不是源自 handler 内部的防御性代码。一个产生看似合理但违反策略的参数的 agent 不会触达副作用;它会收到结构化拒绝,并必须决定下一步怎么做。

一个具体机制是这样的。Agent 运行时并不直接调用工具。它向策略网关发起结构化请求:

{
  "agent": "research-worker-3",
  "session": "s-8841",
  "tool": "documents.write",
  "arguments": {"collection": "contracts", "document_id": "c-2210", "body": "..."},
  "context": {"approval": null, "elevated": false}
}

网关评估一个策略包,即一组带版本、由确定性评估器执行的规则。评估返回一个决定、一个原因码以及策略版本。只有在允许时,网关才调用底层工具。拒绝会作为普通工具结果返回给 agent,因此 agent 可以自行适应,而运行时无需对错误处理做特殊分支。

这里有两个属性很重要。第一,策略引擎位于模型上下文之外。它不是 prompt,不是 system message,也不是 agent 可以检索到的文档。它无法被争辩、被总结掉,或被后续指令取代。模型对决定的唯一影响,是它提交的请求内容。第二,给定请求和策略版本,决定是确定性的。同一个调用针对同一个策略包评估两次会产生相同结果,这正是决定在事后可解释的原因。

诚实的取舍是:这移动了复杂性,而不是消除了复杂性。每项新能力都需要一条策略条目,每次策略变更都是对生产行为的变更,需要审查。请求路径中的网关会增加延迟,并引入新的失效模式:如果网关不可用,正确的默认是拒绝,这意味着策略层的故障会变成 agent 能力的故障。采用这种模式的团队通常接受这一成本,因为替代方案是一种沉默且无界的故障。这笔交易是用能力的可用性换取能力的可界定性。

还有第二个值得点明的取舍。严格的 schema 和狭窄的允许列表会缩小 agent 可采取动作的空间,这可能降低它在未被预期任务上的有用性。缓解办法不是放松边界,而是让策略变更快速且可审查,使边界能够被有意识地移动,而不是被Close-up of a wind turbine blade against the sky in Hamwarde, Germanyrver-rack-closeup-2ef8e4-33fefc.jpg)

无法被举证的控制,就是会被质疑的控制。每项策略决定,无论允许还是拒绝,都应发出足以在无法访问实时系统的情况下重建推理过程的记录。该记录包括请求、决定、原因码、策略版本,以及对决定规范序列化的签名。

签名之所以重要,有一个具体原因:它使记录具备防篡改能力,并可归因于执行点,而不是归因于日志管道。一行日志可以被编辑、丢弃或重排。一条签名的决定记录可以被独立验证,而序列中的缺口是可检测的。

将策略版本与决定一同保留,才能使过去的行为可被重新解释。策略会变化。在某个策略包下正确的决定,在下一个策略包下可能不正确。没有版本,审查历史操作的审计员就必须猜测当时生效的是哪些规则。有了版本,同一个评估器可以针对记录的请求重放,并精确复现该决定。这把审计从访谈变成了验证。

拒绝应当与允许享有同样的保留。拒绝是边界生效的证据。它也是策略调优的主要信号:一条对合法工作频繁触发的拒绝,表明规则过宽;而一条在某类会话中从不触发的拒绝,可能表明规则不可达。保留两侧使策略面可观测,同时又不使其沦为建议性的。

最后一块是在部署前用记录的 trace 测试策略变更。如果决定与其请求一同被记录,那么候选策略包可以针对真实历史调用的语料库进行离线评估。输出是一份 diff:哪些先前被允许的调用现在会被拒绝,哪些先前被拒绝的调用现在会被允许。这份 diff 可以由审查策略的同一批人审查,它把策略变更从一次判断行为转变为一次测量行为。它还会暴露没人预料到的情况,而策略回归通常就藏在那里。

这就是可观测性与治理之间的实际区别。可观测性产生关于发生了什么事的记录。治理产生一个约束什么可以发生的边界,外加一份可被独立验证的边界决定记录。前者是运行系统的必要条件。后者是把权限委托给系统的必要条件。

这对系统设计意味着什么

把工具边界当作控制面。把策略评估器放在模型触及不到的地方。让 schema 成为第一道防线,并让格式错误的调用 fail closed。为两种结果都发出签名决定,并保持策略版本附加其上。在候选策略进入生产之前,用记录的流量重放它们。

这些都不取代可观测性。Trace、指标和仪表盘仍然是理解和改进系统的方式。改变的是顺序:边界做决定,记录做证明,仪表盘做解释。以这种方式治理的系统不依赖于模型做出正确选择。它依赖于模型无法做出别的选择。

分享XLinkedIn永久链接7 分钟阅读

相关阅读

这能回答的问题

Why is observability not sufficient for governing an AI agent?

Observability records actions after they occur and depends on telemetry being complete. A monitored agent with broad permissions still has broad permissions. Governance requires preconditions that make certain actions impossible before they execute, not alerts that describe them afterwards.

Where should policy checks be enforced in an agent system?

At the tool boundary. Every capability the agent has is reachable through a tool interface, so a check placed there applies to all callers, including ones added later. The policy engine should sit outside the model's context so it cannot be argued with or overridden by instructions.

What should a policy decision record contain?

The request, the decision, a reason code, the policy version, and a signature over the canonical serialisation of the decision. Retaining the policy version allows the same evaluator to be replayed against historical requests, so past behaviour can be re-explained and verified rather than reconstructed from memory.

What is the main trade-off of enforcing invariants at a policy gateway?

It moves complexity rather than removing it. Every new capability needs a policy entry, the gateway adds latency, and if the gateway is unavailable the correct default is to deny, which means a policy-layer outage becomes a capability outage. The trade is availability of capability for boundedness of capability.