智能体的多租户:五个你的行级策略从没听说过的存储。
你的应用早就把租户隔开了——行级策略、受限令牌、按租户分库——而这一切都够不着智能体新添的那五个存储:厂商的提示词缓存、你的语义缓存、向量索引、记忆存储,以及限流额度池。其中四个泄漏的是元数据或容量。而语义缓存这一个,会把租户 A 的答案交给租户 B,因为它靠相似度而不是相等来判定命中。仅这一条区别,就分开了"性能回退"与"信息披露事故",而它也正是那条你必须亲手划出的边界。
你买到的隔离画在你的账号周围,不是画在你的租户周围。
厂商确实隔离了它们的提示词缓存。Anthropic 声明缓存条目在组织之间相互隔离,且在若干平台上还在组织内的工作区之间隔离;OpenAI 声明提示词缓存不在组织之间共享。请站在你真实所处的位置读这两句话:你是一个组织,而你所有的客户都住在它里面。厂商的边界把你和别家厂商的客户分开,它在你的租户 A 与租户 B 之间什么也没做——因为从厂商那一侧看,只有一个你。
这意味着什么、又不意味着什么,值得说准,因为惊慌和满不在乎都是错的:
- 前缀缓存交不出内容。命中要求前缀逐字节相同。租户 B 的请求只有在 B 已经发过完全相同的那串字节时,才会命中租户 A 建立的条目——而那种情况下 B 本来就有那份内容。这是清单里唯一一个在结构上无法泄漏数据的缓存。
- 但它仍会泄漏一个推断。命中比未命中更快也更便宜,而这两件事都能被调用方从延迟与响应里的用量字段上看见。一个猜得出前缀的租户,可以由此得知是否有别人发过它。对多数产品这只是噪声;但如果你的租户彼此是竞争对手,或者前缀里可能编入客户名称,那它就是一条侧信道,你应当把任何能标识租户的内容彻底挡在被缓存的前缀之外。
- 缓存的经济性是共享的,而那是个计费问题。缓存写入要付溢价、读取只要零头;碰巧写下条目的那个请求,替其他人付了钱。如果你按用量计费,这会在租户之间悄悄形成交叉补贴——见成本归因,那里讲了为什么账单自己解释不了自己。
可操作的规则很简单,而且能扛过每一家厂商的实现细节:被缓存的前缀承载你的系统提示词、工具 schema 与共享指令;租户的数据放在缓存断点之后。这恰好也是提示词缓存出于命中率考虑所要的写法——安全的设计与快的设计正好是同一个设计,这种情况可不多见。
语义缓存是唯一可能答错的那个,而它偏偏是团队拿来共享的那个。
你栈里其他每一个缓存,最坏也只是慢或者旧。语义缓存靠嵌入距离判定命中,于是租户 B 的"我们的续约日期是哪天"可以匹配上租户 A 那条"我们的合同什么时候续"的存档答案——然后笃定地把它返回,全程没有任何地方报错。没有哪个阈值能让这件事变安全,因为整套机制就是近似匹配;而跨租户之间最相似的那些查询,恰恰是人人都用同样措辞问出的高价值问题。
- 租户 ID 属于缓存键,不属于过滤条件。查找必须在最近邻搜索跑起来之前就被限定范围,而不是事后作用在它的结果上。事后过滤意味着那次跨租户搜索已经发生过了,于是一次实现变更或一个索引 bug 就能悄悄把口子重新打开。
- 共享缓存只对与租户无关的内容才成立。产品文档、公开政策文本、"我怎么导出 CSV"——那些答案不取决于谁在问的内容。把它们放进一个单独的、显式共享的缓存,用它自己的键命名空间,让"共享"成为一个被写下来的决定,而不是一个继承来的默认。
- 把误命中率当作安全指标来插桩,而不是性能指标。对命中做抽样,绕开缓存重答一次,然后比对。语义缓存是一套带着误报预算的检索系统;如果你不知道那个数字,你就没有设过那个预算。见语义缓存。
- 也可以考虑干脆不要它。对多数智能体负载而言命中率本就很低——智能体的提示词带着又长又独特的会话状态——而前缀缓存已经拿下了绝大部分节省,且不带这种失败模式。
真正跑进生产环境的那个版本,几乎从来不是有人刻意做了一个跨租户的缓存,而是某个单租户试点里按查询文本做键的缓存,在第二个客户到来时没人给它重新换键。在代码库里搜一下只由用户输入派生出来的缓存键——这就是整场审计。
在向量索引里,命名空间与元数据过滤不是同一种控制。
每一个向量数据库都同时提供"分区数据"和"过滤数据"两种手段,而文档把它们说得像是可以互换。它们不可互换,而这个区别恰恰在你最不想操心的场景里显形:高负载、大规模、近似索引。
- 前置过滤或分区搜索,在近似搜索跑起来之前就限定了候选集。这是一条边界。正确性不依赖于搜索是否返回了足够多的邻居。
- 后置过滤先全局取回 top-k,再把不满足条件的行丢掉。这不是内容泄漏,却是一道召回悬崖:对于文档只占语料一小部分的租户,跨全体租户的 top-50 里可能只有两条是它的,于是智能体在一份明明有答案的语料上回答"我没找到"。团队会把这个当成模型问题调上好几周。
- 每租户一个独立索引,是最强也最费运维的选项。它同时让删除变得诚实——租户离场就是丢掉一个索引,而不是一次你之后还得举证的带条件删除。如果你签了按客户的删除承诺,请在选定共享索引方案之前把这笔成本算进去;事后改造意味着一次全量重建索引。见选择向量数据库与数据治理。
- 重排序与混合检索会把这道题重新打开。一个从共享池里拿候选的重排序器,或者一个从没跟向量索引一起做过分区的关键词索引,会心安理得地把向量侧那条精心划好的边界拆掉。规则是:每一条检索路径都带着租户范围,包括上个季度某人为了演示临时加的那一条。
记忆是租户真正渗血的地方,因为写入路径会做总结。
检索读出的是你放进去的东西。记忆写下的是新东西——一条被提炼的事实、一项偏好、一个教训——而提炼这一步是一次模型调用,它会心甘情愿地把上下文里的一切揉在一起。故障不是某个查询返回了错误的行;而是一段由两个租户的材料撰写出来的总结,如今作为某一个租户的事实被持久存下。下游没有任何过滤能撤销它,因为那条冒犯性的记录,在归属上确实属于它被写给的那个租户。
- 一次总结调用只处理一个租户,永远如此。为了效率把记忆固化跨租户批处理,是整页里风险最高的一项优化。如果一个作业要处理很多租户,那它就要发很多次调用。
- 记忆键按租户与用户双重限定,并想清楚这条记忆归谁。"我们公司偏好公制单位"和"Priya 偏好简短回答"归属不同、删除语义也不同;把两者混为一谈,意味着一位离职员工的偏好会作为公司政策留存下来。
- 抵制跨租户学习,否则就把它做成一个有知情同意的显式产品。"智能体越学越好,人人受益"是一句动人的路线图文案,也是一个合同问题;任何从租户 A 的数据里派生、又作用到租户 B 身上的东西,都是一次你的数据处理协议多半没设想过的数据使用。见记忆写入路径架构。
- 删除必须够到派生层。租户删掉一份文档;两个月前从它提炼出来的那条记忆还在,那条向量也还在。在你签下删除 SLA 之前,先把每一个可能持有派生物的存储枚举干净。记忆投毒防御讲的是同一条写入路径的对抗版本。
吵闹的邻居是一个带着延迟指纹的容量问题。
厂商的限流是按账号强制的,所以不管你有没有为此建模,你的租户共用同一个桶。一个租户跑批量导入就能吃光你整个组织的每分钟令牌额度,而其他每一个租户看到的症状是"智能体好慢",不是一条你能归因的错误。在自建推理上这个动力学更锋利:单条 200k token 的上下文占住的 KV 缓存,本可以容纳好几个普通会话,于是一个租户的长文档削减了所有人的并发——算术在为智能体自建推理里。
- 给每个租户一份按时间窗的令牌预算,由你强制,且低于厂商上限。你的队列、你的公平策略、你的错误信息。靠厂商的 429 来发现公平性,意味着嗓门最大的租户获胜。
- 把交互车道与批处理车道分开。量都在批量作业那边;一个正等着聊天回合的用户永远不该排在它后面。把长作业送去批处理档,那里的经济性本来也更好。
- 按租户的 p95 报警,而不是全局 p95。全局均值可以一直健康,与此同时某一个租户已经全面崩坏——而那个租户正是会给你发邮件的那个。
- 想好失控循环由谁买单。一个打转的智能体几分钟就能烧光某个租户的预算。按租户的硬性花费上限,加上范围限定到单个租户而非整个集群的熔断开关,就是"糟糕的一个下午"与"所有人一起宕机"之间的差别。
去证明它,因为这些故障没有一个会报错。
本页上的每一种泄漏在构造上都是无声的:错误答案是良构的,被削掉的召回看起来像知识缺口,吵闹的邻居看起来像模型变慢。你的错误预算纹丝不动。所以唯一诚实的姿态,是写一套故意去越界的测试,并让它持续运行。
- CI 里的双租户金丝雀套件。两个刻意做得语料相似的合成租户,各自带一个独特而鲜明的秘密。每次变更之后,就答案是租户 A 秘密的每一个问题去问租户 B,走遍每一条路径:检索、缓存、记忆,以及端到端的智能体。这套测试很便宜,永不过期,而且是唯一能让你诚实回答安全问卷的那件东西。
- 给每一个跨度打上租户标签。没有租户 ID 的追踪回答不了"这次运行读到了它不该读的东西吗",而那正是事故中被问的第一个问题。智能体可观测性是底座;租户标签才让它成为一项控制。
- 测的是删除路径,不是删除接口。为某个租户插入一份鲜明的文档,让智能体跑到它落进记忆与每一个索引,然后删掉它再重新问一遍。多数团队正是这样发现自己那些派生存储的。
- 演练一次披露。如果跨租户泄漏真的发生了,你能从追踪里枚举出涉及哪些租户、哪些记录吗?如果答案是不能,那就是审计轨迹上的缺口——它决定你发出的是一份有范围的通知,还是一份大水漫灌的通知。
本周就把便宜的那 80% 做掉:把租户 ID 放进每一个缓存键与每一条追踪,把租户数据挪到提示词缓存断点之后,把检索切到分区或前置过滤的路径上,并且永不把记忆总结调用跨租户批处理。然后把双租户金丝雀套件加进 CI,让这条性质被强制执行而不是被记在脑子里。智能体里的隔离不是一项配置一次的策略——它是一条五个彼此独立的存储都能悄悄打破的不变量,因此它需要一个会大声失败的测试。
相关:按客户的经济性是同一道分区的计费一侧,数据驻留与数据主权讲这条边界还必须是地理性的情形,而RAG 安全是第 3 步的对抗性读法。