跳到主要内容

服务商在 API 边界遵守但在缓存处违反的数据驻留契约

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的驻留审计追踪了来自租户流量的每一个出站请求,观察它在法兰克福的一个主机名上终止,并签了字。审计对它所测量的一切都是正确的。但它也看错了层级。请求确实去了欧盟。但满足该请求的字节——即服务商哈希并从最近可用节点提取的缓存前缀——却存放在 us-east-1。你的区域端点向你承诺了一个目的地。而缓存什么也没承诺,因为缓存是一个不同的产品,受不同的 SLA 约束,是为成本而非合规而设计的。

客户的审计员发现了它。不是你的。另一个供应商的事件报告提到 Prompt 缓存的放置与推理区域是解耦的,于是客户的 GRC 团队提出了那个显而易见的后续问题:我们的前缀去了哪里?修补这一差距的合同修正案花了 90 天。续约被暂停了。根据他们拿到的文档,编写集成的团队并没有做错任何事。

两个边界并不是同一个边界

区域 API 端点是一种路由承诺。你将客户端指向 eu-frankfurt.provider.com,请求在区域内的基础设施上终止。这就是你的网络级审计可以查看并认证的边界。出站数据包、目标 IP、TLS 握手——全部可观察、可审计、且清白。

Prompt 缓存是另一回事。它是一个内容寻址存储,通过前缀的哈希值(系统提示词、工具架构、少样本示例)进行索引,以便来自任何租户的相同前缀都可以重用同一个条目。缓存的全部意义在于避免重复计算服务商已经为某些人计算过的注意力状态。这种经济逻辑只有在缓存规模庞大、共享并放置在计算能力附近(而非请求来源附近)时才有效。镜像你路由拓扑的按区域缓存会破坏这种节省模型。

因此,服务商将缓存构建为一个全球池。前缀被哈希,哈希值被写入任何具有淘汰空间的节点,而该节点是由不知道你合同存在的容量启发式算法选择的。你的欧盟租户请求在法兰克福终止。缓存写入则去了 us-east。审计看的是正门。而字节却从后窗溜走了。

这不是疏忽。这两个系统是由不同的团队针对不同的需求设计的。推理层附带区域承诺,因为客户有此要求。缓存层附带成本承诺,因为每个 Token 的经济效益需要如此。这两个团队对于各自的 SLA 都没有错。错的是客户,他们认为这两个 SLA 是可以叠加组合的。

为什么资产清单会遗漏它

合规团队的驻留控制目录中有关于推理的条目。但可能没有关于缓存的条目。原因很平凡:在构建数据清单时,缓存要么是服务商尚未公开的私有优化,要么是一个没有按区域承诺的资产功能。清单捕获了可清单化的内容。服务商后来添加的、作为免费优化销售并默认启用的任何功能都位于目录之外。

这种模式在各服务商中重复出现。仔细阅读驻留承诺,你会发现如下条款:“在不支持区域处理的区域中,扩展 Prompt 缓存可能需要我们在区域外处理并临时存储客户内容,以提供服务。”这句话完整地描述了泄露。但它也埋在帮助中心的文章里,而不是 GRC 团队在采购时审查的主协议中。客户签署的合同谈论的是推理。提到缓存位置的脚注则在更深的两层点击之后。

另一种失败模式同样常见。客户通过超大规模云厂商介导的路径进行路由——如 Bedrock 法兰克福、Vertex EU——认为云厂商的区域承诺涵盖了模型提供商的承诺。对于推理调用,通常确实如此。但对于辅助功能,情况并非总是这样。Bedrock 关于 Prompt 缓存的文档包含免责声明:“缓存是区域性的,因此你可能会向同一个推理配置文件发送两个完全相同的请求,如果它们被路由到不同的区域,第二个请求可能会出现缓存未命中”——这是从另一个方向描述的相同事实。如果服务商确实遵守区域缓存,你将付出缓存未命中率的代价。如果服务商不遵守,你将付出驻留差距的代价。

驻留图表实际涵盖的内容

画一张你会向审计员展示的驻留图表。一个标记为“客户端”的方框,一个指向标记为“欧盟端点”的方框的箭头,一个指向标记为“推理集群 (eu-frankfurt)”的方框的箭头。图表闭合了;字节留在区域内。审计员签字。

现在画一张实现图。同样的客户端,同样的欧盟端点,同样的推理集群。但在集群之前,有一个标记为“缓存查询”的步骤。在响应发出之前,有一个标记为“缓存写入”的步骤。这两个步骤都与一个标记为“全球 Prompt 缓存”的服务通信,该服务的分片位于驻留图表未列出的区域中。其中一些分片保存着你的租户发出的每一个请求的前缀。如果服务商缓存输出,其中一些分片还会保存响应。

第一张图是你的合同涵盖的。第二张图是你的数据实际流经的。你拥有的合规姿态是第一张图的函数。你实际需要的合规姿态则是第二张图的函数。两者之间的差距就是缓存层,从你这一侧进行再精细的路由也无法弥合它。

关于这一差距已有研究文献。引入 MemPool(一个跨推理实例管理分布式 KV 缓存的弹性内存池)的 arXiv 论文描述了一个全球调度器,通过基于全球 Prompt 树的局部性感知策略来增强缓存重用。调度器优化的局部性是“前缀局部性”,而非“租户局部性”。另一篇关于通过共享 KV 缓存泄露 Prompt 的 NDSS 论文指出,在接受调查的 8 家大模型服务商中,有 7 家在全球范围内跨用户共享缓存。两篇论文都是关于系统级效率的。它们也都无意中描述了一种驻留风险。

弥补差距的模式

仅靠消费端的架构技巧无法解决这个问题。每一条解决路径要么需要与供应商变更合同,要么需要刻意牺牲缓存所提供的优化。选择一个你能承受的成本方案。

修改合同以明确缓存的数据驻留(Residency)。 规定 推理 驻留的合同并不等同于规定 缓存 驻留。解决方法是增加一个明确针对每个区域的条款,并要求供应商证明缓存层的数据驻留是独立于推理层进行审计的。主要供应商的企业级合同可以包含此类措辞;这通常不是默认提供的,采购部门必须主动提出。典型的成本是 90 天的修订周期。在你的续约窗口中预留出这段缓冲时间。

针对受监管流量选择禁用共享缓存。 每个主流供应商都允许你按请求禁用 Prompt 缓存,通常是通过 Header 或参数实现的。对于欧盟租户的流量禁用它,是用 Token 节省换取数据驻留保证。这取决于缓存命中率;如果你的前缀(Prefixes)稳定且经常复用,那么成本确实存在。如果你的前缀本就随请求变化,那么成本几乎可以忽略不计。在假设它太贵之前,先计算一下。

像审计推理层一样审计缓存层。 仅追踪出站请求目的地的驻留审查是在审查错误的层级。将审查扩展到追踪一个样本前缀,从请求到缓存写入,以验证目的地。供应商并不总是会提供你所需的自省(Introspection)能力。去争取它。你无法看到缓存层这一事实本身就是一个审计发现。

将每一项“免费优化”都视为合同约定的范畴。 Prompt 缓存只是一个例子。模型路由、请求批处理、子处理器变更、默认内容安全过滤——所有这些都是供应商为了优化成本或质量而默认开启的功能,其中任何一项都可能导致字节跨越合同规定的边界。采购时的架构评审应列举这些功能,并根据数据驻留清单对每一项进行验证。那些不在清单上的,就是你需要询问的对象。

架构层面的认知

供应商的区域端点(Regional Endpoint)是对请求去向的承诺。但这并不是对满足你请求的字节存储位置的承诺。这是关于不同层级的不同陈述,如果团队将前者解读为对后者的保证,那么其建立的合规态势就依赖于一个供应商从未披露过的拓扑结构。

这不仅限于 Prompt 缓存。同样的情况也出现在非 AI 工作负载的 CDN 边缘缓存、特征存储(Feature Stores)的跨区域复制、以及基于队列的消息传递中(队列的区域部署独立于生产者和消费者)。在每种情况下,可审计边界(请求路由)和不可审计边界(下游存储位置)都受制于同一供应商的不同 SLA,而客户的合规态势仅取决于两者中最薄弱的一环。

防御性的姿态是假设缓存拓扑是未披露的,在签署合同时明确询问,并将每一项默认开启的优化视为潜在的越界行为,直到供应商书面承诺该特定功能的数据驻留。而进攻性的姿态是围绕列举跨边界接触面(Surface)而非列举端点来设计采购评审。这两种姿态最终会得到相同的清单;但只有第二种姿态能捕捉到供应商在合同签署后推出的下一个功能。

将数据驻留合同视为路由保证的团队,构建的是一个依赖于供应商披露其行为的控制面。而将其视为逐层承诺的团队,构建的是一个依赖于供应商同意他们“不会做什么”的控制面。第二种姿态才能在下一次产品发布中生存下来。

References:Let's stay in touch and Follow me for more thoughts and updates