API密钥为软件提供身份验证。策略对象决定该软件被允许执行的操作。
本系列上一篇文章以会议室中的一个问题结尾:究竟是谁决定我们可以这样做?当时的论点是,答案必须存在于网关能够强制实施任何策略之前,而网关的职责就是让这个答案可重复执行。
这篇文章要探讨的是,当一个答案不再只是一个决定,而是成为一个对象时,它会是什么样子。
几十年来,凭证只回答一个问题:你是谁?密码识别个人身份。API密钥识别软件身份。OAuth作用域增加了委托机制,因为仅凭身份无法表达应用程序应在限制范围内代表用户行事的意图。每一步都在增加上下文,因为围绕访问权限的决策变得越来越精细复杂。
企业级AI提出了这些凭证从未被设计来回答的问题。两个应用程序都能成功完成身份验证,但一个应该被限制使用低成本模型,而另一个则获准使用前沿推理模型。一个拥有宽松的实验预算,另一个则受到严格的成本控制。一个可以调用敏感的MCP工具,而另一个则永远不应该看到这些工具的存在。
这些不是身份验证决策。它们是在请求到达时发生的治理决策,身份信息无法承载它们。
任何目睹过授权机制演进的人都会熟悉这种模式。凭证最初只是一个标识符,后来逐渐积累上下文,因为其周围的系统需要知道的不只是调用者是谁。
企业级AI将多个问题推入同一时刻。哪个业务部门拥有此工作负载并承担其成本?哪些提供商获得批准?在“已批准使用OpenAI”和“已批准使用GPT-4o”是完全不同的声明的前提下,具体批准了哪些模型?适用哪个预算,预算耗尽时会发生什么?可以调用哪些工具?此访问权限何时到期?
一个仅证明身份的凭证迫使每个应用程序自行回答这些问题。这就是为什么最终会出现二十个代码库包含二十种略微不同的同一策略解释——这正是第一篇文章所描述的策略漂移。
Bifrost的虚拟密钥是该模式的一种实现。虚拟密钥不是将凭证视为标识符,而是将密钥视为治理决策的运行时表示。身份验证仍然建立身份。虚拟密钥决定请求到达后如何处理。
上一篇文章中的营销预算决策——财务部门在会议室做出的那个决定——最终会变成这样:
{
"name": "Marketing Experimentation",
"team_id": "team-marketing",
"provider_configs": [
{ "provider": "openai", "allowed_models": ["gpt-4o-mini"] }
],
"budget": { "max_limit": 2000.00, "reset_duration": "1M" },
"rate_limit": { "token_max_limit": 10000, "token_reset_duration": "1h" },
"expires_at": "2026-12-31T00:00:00Z",
"is_active": true
}
那个对象里的所有内容都来自对话,而非代码库。允许列表来自安全审查。预算来自财务部门。过期时间来自某个决定这只是实验而非永久能力的人。使用此密钥的应用程序并不实现其中的任何部分。
有两个值得注意的细节。允许列表影响的是发现范围,而不仅仅是强制执行:用此密钥调用模型端点时,只会返回它可能访问的提供商,因此应用程序永远不会看到无法使用的清单。而且,由于提供商条目带有权重,同一个表达权限的对象也表达了路由偏好,这使得故障转移成为策略决策,而非应用程序逻辑。
如果每个密钥都独立存在,策略对象将变得难以管理。Bifrost将它们堆叠起来:客户包含团队,团队包含虚拟密钥,预算可以附加在任意层级。一个密钥属于一个团队或一个客户,永远不会同时属于两者,这保证了成本归属的明确性。当请求到达时,该链中的预算会一起被评估,因此一个密钥可能在自己的限额内,却仍然因为其上级团队预算耗尽而被拒绝。听起来这像是一种不便,直到你看到一个热情的团队在九天之内烧掉一个季度的配额。
这种建模的价值在于,结构反映了组织本身。成本归属不再是每月对账的工作,因为所有权在密钥创建时就已经确定,而不是等发票到达时才去追查。预算和速率限制是机制;层级结构才是让它们有意义的东西。
这是大多数治理相关文章跳过的一部分,而正是这部分决定了治理是否能在生产环境中真正发挥作用。
密钥上的每个约束都有明确的失败行为。请求不会在未告知你的情况下悄悄降级或回退到更便宜的方案。
最后这条说明的价值远超表面所见。策略在任何请求离开系统之前就被评估,因此被拒绝的请求不会产生任何成本。能够在阻止的调用上省钱的治理,与只能记录允许调用的治理,是截然不同的两回事。
完整的拒绝情况如下:
| 策略规定 | 调用方收到的结果 |
|---|---|
| 预算耗尽 |
402,budget_exceeded,并在消息中附带超额量 |
| 模型不在允许列表上 |
403,model_blocked
|
| 提供商未获批准 |
403,provider_blocked
|
| 密钥已过期 |
403,并将过期作为原因注明 |
| 令牌或请求速率超限 |
429,rate_limited,并说明时间窗口 |
| 密钥已停用 |
403,virtual_key_blocked
|
先看第一行。财务部门做出的治理决策以402 Payment Required的形式呈现在应用程序面前,这个状态码自HTTP/1.1时代就存在,却几乎从未有过合法用途。组织策略正在用普通的协议语义来表达,这意味着你的团队已有的重试逻辑、告警和错误处理可以直接响应它,而无需学习任何新东西。
这种行为也是确定性的,在事故期间那些看似乏味却很关键的方面表现如此。过期采用失败关闭模式,同时阻止推理和工具执行。过期的密钥不会被删除或停用,因此它仍然可见以供审计,并且可以通过延长时间戳来恢复。如果一个密钥同时处于停用和过期状态,系统会报告为停用,因为当两个条件都为真时,你需要一个可预测的答案,而不是一场竞态。
最后这个细节与上一篇文章中关于证据的论点相呼应。一个过期就消失的密钥会摧毁关于当时允许了什么、何时允许的记录。一个过期后仍然可见的密钥则保留了这些记录。
这是第一天就要检查的事情。
治理默认是可选的。没有携带虚拟密钥头的请求会被放行,不受治理。这是合理的采用默认设置,因为它让你能在第一个下午就引入网关,而不破坏所有现有集成。但六个月后仍然保持这种默认设置就不妙了——那时领导层以为策略已强制执行,而相当一部分流量正在绕过它。
强制执行是一个单独的设置:enforce_auth_on_inference。打开它,有效的密钥就会变成强制要求;没有密钥的请求会被直接拒绝。请把这个设置加入你的治理检查清单,而不要指望安装网关就自动完成了这一步。
这是一个合理的质疑。用数百个策略对象替换分散的提供商密钥,你实际上是从一个管理问题换到了另一个管理问题。
两件事情使它成为真正的交易,而不是横向调动。混乱已经存在,分布在二十个代码库中,没有人能看到它,也没有审计员能枚举它;将其整合成对象会让现有的复杂性变得可见,而不是制造新的复杂性。而且这些对象是配置,所以它们可以比较差异、可以版本化,并且可以通过检查而不是考古来回答“上个季度谁被批准使用哪些模型”。
采用成本也比看上去更低。虚拟密钥沿用了应用程序已经在使用的请求头约定,无论是 OpenAI 风格的 Bearer 令牌、Anthropic 风格的密钥请求头,还是 Google 和 Azure 的等价物。大多数应用程序通过修改基础 URL 和密钥值来采用治理,而不是引入新的 SDK。
虚拟密钥并不取代基于角色的访问控制,把两者视为竞争关系会颠倒它们的因果关系。RBAC 擅长描述一个人被允许做什么。策略对象表达的是一个工作负载被允许做什么,这是一个独立的问题,无论是否有真人参与都始终存在。一个隔夜批处理任务没有用户,但它仍然需要预算、允许列表和所有者。
这种区分只会变得更加重要。智能体系统会发出没人明确发起的请求,调用当下没人选择的工具,花费没人盯着的钱。伴随这些请求的凭证必须承载组织意图,因为没有人类在场来提供这种意图。
第一篇文章认为,治理必须先于基础设施存在,基础设施才能执行治理。执行的形式是这样的:在会议室里做一次决定,在对象里编码一次,然后在每一个请求上评估,而不需要任何人记住它。
如果你想看到它的实现而不是描述,Bifrost 是开源的,治理文档 和 代码仓库 都值得花上一个小时。
本文是与 Bifrost 团队合作撰写的。这里表达的观点和结论是我自己的。
——
一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。