# AI 服务商故障转移能保留凭据和审计记录吗？

AI 流水线不会因为在别处重试同一个提示词，就自动保留原有的安全状态。备用路径可能改变授权调用的凭据、接收账单的账户、处理数据的区域，以及之后能够检查到的证据。如果这些变化发生在 SDK 的重试循环中，故障路径拥有的权限就可能超过设计评审批准的范围。

我见过一些团队把备用模型端点当作无害的基础连接。事情通常始于一个合理目标：当服务商超时时，让编程代理或文档工作流继续运行。接着，一个环境变量、一个默认配置，或一个全局重试设置，就会把原本经过严格批准的路径变成未声明的路径。请求成功了，所以没人察觉，直到财务部门询问一个陌生账户，或者事故复盘无法确认数据究竟去了哪里。

解决办法不是设计更复杂的重试策略。应在调用之前做出权限决策，明确列出所有可能的目标，并写入一条能够经受部分失败的完整证据链。可用性很重要，但不能成为模糊说明是谁越过了边界采取行动的理由。

## 备用路由是第二条权限路径

备用路由需要单独授权，因为它可能使用主路由从未拥有的凭据和合同权限。称它为重试，并不会改变这一事实。

一条路由不只是主机名和模型名称。它还包括服务商、服务商账户或项目、凭据引用、允许使用的区域、数据分类、已接受的保留设置，以及批准状态。如果其中任何字段不同，备用调用产生的外部影响就不同。

团队经常把传输连续性和权限连续性混为一谈。传输连续性意味着一个端点失败后，调用方仍然收到了答案。权限连续性意味着工作仍由同一个获批准的组织、区域和凭据范围完成。前者存在时，后者未必存在。这一区别决定了备用路径是否安全。

想象一个编程代理被要求总结客户支持数据导出。主路由将经过脱敏的文本发送到指定区域内的已批准账户。一次超时触发了备用客户端，客户端读取进程环境中的通用凭据，并将同样的文本发送到另一个区域的个人沙盒账户。任务看起来运行正常，安全边界却已经失守。

不要通过禁止所有备用路径来解决问题。有些工作确实拥有多个完全可互换的目标。应将这种等价关系定义为一组具体属性，然后让路由器强制执行。备用目标如果没有声明账户、区域和凭据引用，即使能够完美回答提示词，也是不完整的。

## 三种身份可以独立变化

服务商名称、账单身份和凭据身份是三个独立字段，路由必须同时携带这三项。假设其中一项能够推断出其他项，是多服务商工作中最常见的盲点。

服务商是接收请求的公司或服务。账单身份是为请求付费的账户、项目、组织、经销商安排或云订阅。凭据身份是授权请求的具体机密或委派令牌。同一个服务商可能提供多个账单身份和多个权限不同的凭据。

服务中断时，这一点尤其重要，因为备用代码往往会寻找任何可用的凭据。客户端库可能选择默认项目。容器可能继承开发者凭据。配置变更后，工作负载身份可能针对另一个租户生成令牌。在应用日志中，这些行为都不显眼，但它们仍然是在不同权限主体下执行的外部操作。

在建立网络连接之前，先将路由写成完整对象。下面的伪配置展示了最基本的结构：

```json
{
  "operation_class": "customer-text-summary",
  "primary": {
    "provider": "provider-a",
    "account": "production-eu",
    "credential_ref": "vault:provider-a-prod-eu",
    "region": "eu",
    "data_class": "redacted-customer-text"
  },
  "fallbacks": [
    {
      "provider": "provider-b",
      "account": "production-backup-eu",
      "credential_ref": "vault:provider-b-backup-eu",
      "region": "eu",
      "data_class": "redacted-customer-text",
      "approval": "destination-specific"
    }
  ],
  "deny_when": ["account_missing", "region_mismatch", "credential_ref_missing"]
}
```

这份配置故意保持简单。它的作用是防止 SDK 在路由决策完成后，再自行填充账户或凭据字段。不要把机密材料放进这份记录。凭据引用只说明可以使用哪个权限主体，绝不能包含凭据本身。

应将调用方与目标分开分类。代理进程可以拥有足够权限来请求一项工作，但不一定有权将这项工作发送到公司拥有的每个服务商账户。调用方负责提出请求，路由解析器负责决定是否存在获批准的目标。

## 默认凭据会造成无声的账户变化

环境凭据对本地开发很方便，但在备用路径中很危险，因为它们会把进程状态变成授权策略。处理服务中断的代码不应通过询问运行时当前恰好有哪些凭据来发现账户。

常见的失败过程如下：

1. 工作进程使用明确的生产凭据引用，通过服务商 A 发送请求。
2. 服务商 A 在请求到达后返回超时，结果处于不确定状态。
3. 重试包装器选择服务商 B，并通过默认凭据发现机制创建客户端。
4. 服务商 B 接受工作进程环境中的凭据，可能是共享云角色或开发者创建的令牌。
5. 工作进程只写入 `fallback succeeded`，然后返回结果。

如果评审者只关注响应处理，每一行都可能通过代码评审。真正的问题藏在省略的字段中。哪个账户接受了请求？哪个区域处理了请求？该提示词是否符合这个目标的要求？超时是否意味着服务商 A 也完成了第一次请求，使数据在预期路径之外留下了两份？

要求备用构造函数接收凭据引用和账户断言。账户断言是你期望远程服务为已认证调用方报告的标识符。如果服务无法通过程序公开该标识符，就记录凭据代理所选择的账户，并限制凭据，使其无法访问未预期的账户。

不要把长期有效的备用令牌放进环境变量，然后把这称为弹性设计。它会变成主机上每个进程都能轻易使用的凭据，包括后来新增但从未纳入路由设计的工具。应使用保险库或代理，只有在目标选定后才释放凭据，并将释放动作记录为事件。

备用路径也应有预算。这个预算不只是支出上限。应按操作、目标和时间窗口限制尝试次数，避免服务商级别的中断让队列把同一项敏感任务扩散到多个账户。如果无法确认主尝试是否到达服务商，就将结果标记为不确定，并明确处理这种状态。盲目重试会让重复的外部操作变得习以为常。

## 连续追踪需要的不只是请求 ID

一个操作标识符可以将备用事件关联起来，但如果每次尝试没有说明所使用的权限，审计记录仍然不完整。关联信息回答哪些事件属于同一组，路由字段则回答实际发生了什么。

在选择主路由之前创建操作 ID。对于用户请求、代理运行或队列任务，应始终保持这个 ID 不变。然后为每次服务商调用创建尝试编号，包括在网络调用前就被阻止的调用。一条有用的记录大致如下：

```json
{
  "operation_id": "op_7c1d",
  "attempt": 2,
  "parent_attempt": 1,
  "reason": "primary_timeout",
  "provider": "provider-b",
  "account": "production-backup-eu",
  "credential_ref": "vault:provider-b-backup-eu",
  "region": "eu",
  "data_class": "redacted-customer-text",
  "approval_id": "apr_391",
  "result": "sent"
}
```

稍后写入结果事件时，使用相同的操作 ID 和尝试编号。如果服务商返回远程请求标识符，也应将其纳入记录，但不要把它们作为主要关联字段。被阻止的调用不会有这些标识符，而且不同服务商的格式也不会一致。

W3C Trace Context Recommendation 定义了一种可跨服务边界传播的追踪标识符，OpenTelemetry 使用这种上下文来关联 span。如果你的服务支持它，可以将其用于运维追踪，但它不能取代上面的权限字段。追踪信息可以准确显示请求经过了五个服务，却仍然无法说明哪个凭据跨过了最后一道边界。

应区分已尝试的操作和已完成的操作。如果路由器选择了服务商 B，但批准被拒绝，就记录一次被阻止的尝试。如果请求离开了你的网络，但连接中断，就记录一次不确定的尝试。如果服务商 B 返回了响应，就记录完成。事故复盘需要这些区别，代理也应收到保留这些状态的结果，而不是模糊的重试错误。

应使用哈希链式日志或其他只追加设计保存审计记录。可变的应用数据库有助于生成报告，但管理员或遭入侵的进程可以改写调查时真正需要的操作序列。应通过标识符将运维追踪、应用日志和安全证据关联起来，同时不要假设它们具备相同的完整性属性。

## 数据驻留需要明确的拒绝路径

即使服务商发生中断，驻留约束也必须拒绝无法满足要求的备用路由。安全的备用方案有时就是受控失败。

不要把数据驻留简化为仪表板上的区域标签。应针对当前数据分类明确组织对数据驻留的定义，包括处理位置、存储位置、支持访问、保留期限以及已经批准的任何子处理方。路由解析器应依据这个决定工作，而不是根据服务商最近的端点自行猜测。

一个常见错误是声明了主区域，却配置了一个继承服务商默认地理位置的全球备用路径。正常运行时，主路由看起来符合要求。服务降级时，日志中才会出现备用路径，而这恰恰是人们最不愿意查看细节的时候。因此，备用规则需要明确的区域断言，并为无法断言所需属性的任何目标设置拒绝路径。

应在路由层执行数据最小化。如果任务可以使用脱敏文本运行，就应在选择服务商之前完成脱敏，让所有允许的路由都接收同样的精简载荷。不要依赖主服务商集成来删除字段，却让备用集成发送原始对象。路由契约应声明允许的数据分类，载荷构建器则应拒绝更宽泛的分类。

有些情况下，操作员确实可以批准临时例外。应将其作为带有明确到期时间、指定批准人和新增审计事件的例外处理。不要在事故之后悄悄将它转化为永久路由规则。中断会给人扩大边界的压力，但不会抹去设置这些边界的原因。

## 让路由器先于凭据运行

应由一个统一的路由解析器在任何服务商客户端获得凭据之前选定目标。这样可以消除一个隐藏的第二策略引擎，避免每个 SDK 各自管理重试、默认值和故障转移。

解析器需要接收属于任务本身的输入，而不是客户端库的输入：操作类别、数据类别、调用方身份、已批准的目标、驻留要求、支出权限，以及是否需要人工批准目标。它要么返回一条完整路由，要么返回拒绝，不应返回一串含糊的可能性，再让下游代码自行解释。

让服务商适配器保持精简。它接收路由，通过获批准的机密边界获取被引用的凭据，提交请求，然后报告结果。发生错误后，它不应自行选择另一个服务商。如果它需要针对同一个信息完整的目标重试暂时性的传输失败，就将其记录为另一次尝试。如果它想改变目标，就应将控制权交还给解析器。

这种分离还会让一个关键问题有明确答案：谁批准了备用路径？答案应该是一项与调用方、操作和批准记录关联的路由决策，而不是依赖项重试设置中隐藏的一行代码。

对于 macOS 团队，Sallyport 会将代理凭据保存在加密保险库中，并记录代理会话以及单独的 HTTP 或 SSH 操作，但路由器仍然需要在请求 Sallyport 执行调用之前，指定每个预期的目标。

不要把凭据网关误认为通用策略语言。要做好这件事，不需要庞大的规则系统。一组固定的路由字段和少量清晰的拒绝条件更容易评审、测试，也更容易在压力下解释。

## 批准必须覆盖目标

只有当批准明确说明将发生什么外部操作，包括备用路径会改变的目标时，批准才有意义。批准代理进程本身，并不能证明批准了该进程之后可能访问的每个账户。

在工作需要时，可以设置两个决策点。第一步，授权代理运行或工作负载请求操作。第二步，当路由跨越敏感度阈值、改变服务商、使用不同账单账户或处理受限数据分类时，要求针对目标的批准。对于预先批准的等价目标，第二步可以自动完成，但必须从已声明的路由属性推导出来。

批准卡片或记录应说明代理权限、操作目的、服务商、账户、区域、数据分类和到期时间。不需要显示机密、完整提示词或一大串内部标识符，但必须提供足够信息，让人能够发现欧盟生产账户是否变成了个人测试账户，或者区域路由是否发生了变化。

为避免批准疲劳，应只对真正需要的操作保留逐次调用确认。对日常且预先批准的调用反复弹窗，会让人养成直接点击的习惯。解决办法不是静默备用，而是设置少量具有明确默认行为的路由类别，并在目标超出该类别时单独批准。

撤销和批准同样重要。如果发现代理运行异常，应终止其会话，并阻止新的调用继续使用该权限。如果某个凭据或服务商账户存在问题，就禁用对应路由，让解析器拒绝它。能够显示停止时间的审计记录，远比一条有人修改了配置的笼统备注有用。

## 像生产故障一样测试降级

除非强制故障能够显示压力下产生的确切路由、凭据引用和审计序列，否则备用设计就还没有得到验证。正常路径的集成测试不会覆盖权限发生变化的分支。

构建一个测试服务商适配器，使它能够返回以下情况：接受请求后超时、接受请求前限流、身份验证错误、响应格式错误，以及区域断言不匹配。这些故障的含义不同。提交后的超时会造成完成状态不确定，身份验证错误则不应让路由器去本地环境中寻找另一个凭据。

对于每种故障情况，都应断言五个结果：

- 解析器选择了一条信息完整的备用路由，或者明确拒绝。
- 凭据代理收到的正是该路由提供的凭据引用。
- 审计日志在外部调用前有尝试记录，在调用后有结果记录。
- 操作 ID 将主尝试和备用尝试关联起来，同时不会合并无关任务。
- 返回状态能够区分被阻止、失败和不确定的工作。

测试时，应故意将主凭据和备用凭据映射到不同账户。如果测试环境中的两者都指向同一个账户，缺少账户断言的问题可能会被忽略。还应在没有任何环境凭据的情况下运行测试。一个只能依靠默认发现机制来挽救的设计，并没有证明自己的边界。

应测试队列行为。工作进程可能在发送请求后崩溃，而新的工作进程可能以全新的进程身份恢复任务。要验证它会读取之前的操作状态、保留操作 ID，并且不会仅仅因为本地内存为空就重复不确定的请求。如果业务操作无法安全重复，就应在服务商支持的情况下要求使用远程幂等令牌，并将该令牌作为尝试记录的一部分保存。

最后，测试操作员的使用体验。请一名没有编写路由代码的人解释日志中的一次备用事件。他应该能够识别调用方、初始目标、变更原因、备用账户和区域、批准决定以及最终结果。如果必须查询数据库，再查看三份应用日志才能回答这些问题，说明审计设计仍然过于分散。

## 在事故后保留证据

当参与事故的服务商、工作进程或管理员账户已经不再可信时，审计记录仍必须能够检查。应在本地保存足够的路由元数据，以便不依赖可能已经变化或无法访问的服务商控制台来重建决策。

不要将原始机密值放进日志。应在适当情况下记录稳定的凭据引用、账户标识符和加密指纹。评审者需要证明选择了哪个权限主体，而不是获得一个新的凭据使用途径。

应定期以及在事故响应期间验证日志。Sallyport 的 `sp audit verify` 可以离线检查其加密哈希链式审计日志，无需保险库密钥。当你希望在不打开那些促成操作的机密的情况下获取证据时，这正是需要的检查方式。

发现未声明的备用路径时，应先修复路由契约，再整理报告。保留失败的配置，撤销受影响的权限，确认哪些操作 ID 使用过它，并增加一项测试，让同样的替换明确失败。下一次中断仍会找到同一处薄弱环节，除非系统已经有具体理由拒绝它。
