LLM 产品里的多租户支持可以归结为四个问题:谁在调用(凭证)、他们能用多少(配额)、谁为哪些用量付费(成本归属),以及谁能看到谁的数据(隔离)。平台为前三个问题提供了基础原语。第四个问题,也就是数据隔离,完全是你自己的责任:没有任何配额设置或请求头能阻止某个租户的文档出现在另一个租户的搜索结果里。本文把平台原语映射到这四个问题上,用最多的篇幅讨论真正会让生产宕机的两类成本相关故障模式,最后给出本站处理租户工作时采用的检查清单。
先界定你的租户边界
在动手改控制台之前,先确定在你的产品里租户是什么:一个付费客户、某个客户内部的一个团队,还是你自己的一个环境?这个映射很重要,因为平台对象是用来隔离你的应用实例和环境的,而不是用来把每个客户一对一建模。把租户到实例的映射留在你自己的服务层里,你就能自由地把租户在实例之间迁移、把两个小租户合并到一个实例上,或者把一个大租户拆出去,而无需重整你的平台账号结构。
如果你确实要一个平台对象对应一个租户,要知道你会继承一个并非由你选择的上限。Anthropic 的文档说明默认每个组织最多 100 个 workspace(已归档的 workspace 不计入,文档还提到可以联系客户团队申请更多;请查阅官方页面确认当前上限)。这个上限对环境和大产品线来说够用;对注册流程来说不够用。所以映射应该存在你的数据库里,而不是存在你的 workspace 组织图里。
凭证:每个租户一把密钥,还是每个租户组一把
默认的反模式是所有租户共用一把密钥:这会让一次泄露的影响范围等于你的整个业务,让按租户吊销变得不可能,也让归属统计变成猜谜。给每个租户,或者每个信任级别相同的租户组,分配自己的凭证,并按平台允许的范围尽可能收窄权限。完整的安全操作手册,包括轮换以及怀疑泄露时的处理方式,见密钥安全指南。
在 OpenAI 上,自然的单位是项目 API key,管理员可以在组织或项目级别强制设置密钥的最长生命周期。在 Anthropic 上,密钥可以限定到单个 workspace:workspace 密钥只能访问该 workspace 内的资源,而在创建额外 workspace 时,你可以为每个 workspace 分配成员、服务账号、API key 和资源限制。有些密钥可以跨多个 workspace,这种情况下 workspace 是在每次请求里指定的,而不是固化在密钥里。
无论你的端点背后是哪个平台,都要核实请求实际落在哪个上下文,而不是相信配置。Anthropic API 的响应会包含一个anthropic-workspace-id响应头,其取值是请求所用的 API key 或访问令牌解析到的 workspace ID,文档还指出即使该 workspace 是默认 workspace,这个头也会出现。按请求记录这个响应头,事后你就能还原任意一次调用是由哪个租户的上下文服务的。
配额:两个层次,两个不同的控制面
配额工作有一个组织层和一个 workspace 层。正如 Anthropic 限流文档所说:“API 在组织级别执行由服务端配置的限制,但你也可以为组织的 workspace 设置用户可配置的限制。”换句话说,平台执行的是一条底线;你在它之上执行各租户各自的份额。
陷阱在于以为这两个数字在同一处配置。它们不在同一处,文档也直说了:“你在不同的地方设置 workspace 的限流和消费上限。”限流从 Rate limits 页面的 workspace 选择器按 workspace、按模型配置,涵盖每分钟请求数、输入 token 或输出 token。消费上限从 Workspaces 页面的 workspace 面板配置。配了一个而忘了另一个,你要么有一个没有被执行的预算,要么有一个没有被计量的租户。
在 OpenAI 这边,同样的两个层次以不同的形态存在:项目的限流和消费告警可以通过平台的项目管理端点以编程方式管理,而生产最佳实践建议在限额页面设置消费告警,让用量越过阈值时发出通知,并把硬性消费上限作为执行手段(在生产环境启用硬性上限之前,先读一读消费上限的指引)。你不应该做的是把今天的限额当成方案来引用:限额会变,按标准做法,始终以你部署时官方文档的当前内容为准。
两种“你无法使用 API”
这一节值得细读。当一个租户超出自己的额度时,平台可能以两种截然不同的方式失败,分不清这两者的客户端要么永远重试,要么过早放弃。
第一种失败是限流:一个 429,说明超出的是哪一项限制,并且在存在时附带一个retry-after响应头,告诉你需要等待多久。退避并重试;这就是本站此前讲过的重试预算纪律。
第二种失败是消费上限。它看起来像限流,但并不是。错误类型依然是同一种rate_limit_error,但响应里不带retry-after那个响应头,而在 Messages API 中error.details.error_code字段则给出额度耗尽的取值enforced_spend_limit_reached。文档对运维后果说得很直接:重试,包括 SDK 的自动重试,都会失败,直到访问恢复。没有足够长的等待,也没有足够大的指数;预算已经用完了。
你的客户端代码必须在这里分支。凡是带有enforced_spend_limit_reached这类标识的响应,或者一个缺少retry-after的、形态像消费上限的 429,都应该停止重试循环,向租户和你的团队发出可操作的告警,并让你的错误分诊流程决定接下来怎么处理。把它当作临时限流来处理,就是一个租户预算耗尽变成一整面无意义请求墙的原因。
成本归属:到底是谁花的钱
归属要回答一个简单的问题,但 API 给出的答案很琐碎:对每个租户来说,我们花了多少?在 Anthropic 上,工具是 Usage and Cost Admin API,它的报表参数与租户核算对得上:group_by[]有一个参数用于拆分结果(例如按模型),bucket_width另一个参数用于选择按天还是按小时的粒度,还有针对特定 API key 或 workspace 的过滤条件。用量与成本文档:这些端点支持以细粒度、编程方式访问组织的历史用量和成本数据。
凭证要求是最容易绊倒人的部分,文档也直接点出了这一点。这些端点不接受限定到 workspace 的密钥。你需要 Admin API key、带有org:admin相应作用域的 OAuth token,或者一个未限定到 workspace 的个人账号密钥或服务账号密钥。页面还指出 Admin API 不适用于个人账号。为此规划一个专用的报表凭证,它故意不是你的任何一个租户密钥,并用你手上最高等级的防护来保存它。
有一个归属上的细节值得在设计中考虑:用量和成本报表对默认 workspace 以及跨所有 workspace 的密钥会显示为空的workspace_id workspace 取值,而密钥的scope归属相关字段则能把两者区分开。如果你打算按 workspace 计费,确保你关心的流量确实跑在限定到 workspace 的密钥下,否则你的报表里会有一个没有标签的桶。
在 OpenAI 这边,归属有一个请求头的故事:API 参考的请求头章节展示了请求中携带组织标识请求头OpenAI-Organization和项目标识请求头OpenAI-Project的情形,并指出这些请求产生的用量计入指定组织和项目的用量。如果你的架构通过共享基础设施转发租户流量,确保这些请求头反映租户指定的项目,否则一切都会落进同一个桶里。更广泛的埋点纪律,包括每次调用要记录哪些字段,见用量追踪指南。
数据隔离:平台不会替你做的部分
现在是第四个问题。上面讲的都是钱和访问权限;隔离讲的是内容,没有任何平台能替你执行你的数据模型。平台可以按 workspace 隔离文件、批处理和缓存,但它无法知道租户 A 的支持对话记录绝不能出现在租户 B 的查询结果里。这条边界存在于你的技术栈里:
- 存储分区。每一行包含租户内容的记录都带有租户标识,并且每个查询都在数据层按它过滤,而不是在应用层——在应用层,一个被遗忘的
where子句离一次重构只有一步之遥。 - 日志分区。日志把租户标识作为一等字段携带,这样调试、导出和保留策略都能按租户限定。本站推荐的字段清单,以及完全不应进入日志的字段,见可观测性指南。
- 缓存键前缀。缓存的补全结果、embedding 和检索上下文都必须把租户身份放进键的前缀。一个没有按租户限定的共享缓存,就是一次带性能预算的跨租户数据泄露。
- 模板与配置。prompt 模板、模型设置和功能开关按租户限定,改动一个租户的配置不能渗到另一个租户。
- 导出与删除路径。当租户导出或删除自己的数据时,该操作要对照租户边界进行校验,而不是由一个“应该没问题”的管理脚本执行。
要防范的失败模式
同样的失败在多租户评审中反复出现,所以要刻意在设计上把它们排除掉:
- 所有租户共用一把密钥,让吊销和归属变成全有或全无。
- 只在组织级别配置限额,于是一个吵闹的租户就能吃掉所有人的余量。
- 把消费上限的停止当作可重试的限流,把精力耗在一个不会按你的节奏恢复的预算上。
- 用限定到 workspace 的密钥去调用用量和成本端点,而文档说明这样行不通。
- 隔离了平台资源,却让自己的数据库、缓存和日志保持全局。
- 没有按租户对账:发票说的是一回事,用量报表说的是另一回事,没人能解释这个差额。
检查清单
- 定义租户边界,并把租户到实例的映射留在你自己的服务层。
- 按租户或租户组发放凭证,并按平台允许的范围尽可能收窄权限。
- 在两个层次、两个控制面上都配置配额,并记录每一处放在哪里。
- 让客户端行为按两种失败类型分支:限流走重试预算,消费上限走告警。
- 用专用的、非租户的报表凭证生成按租户的成本报表。
- 按租户对你的存储、缓存和索引做分区。
- 给每一行日志打上租户标识。
- 演练只针对单个租户的轮换和吊销,而不影响其他租户。
- 加上按租户的告警,让第一个信号是一条通知,而不是一张支持工单。
四个问题,三个平台原语,一件仍然属于你的工作。把映射和失败分支做对,多租户就不再是架构问题,而变成了一个计费细节。你调用的任何兼容服务的端点级细节都在该平台自己的文档里,并且一如既往,请对照你构建时官方页面的当前内容核实每一处引用到的行为。