跳到主要内容

多用户 AI 会话:没人在设计阶段考虑的上下文归属问题

· 阅读需 10 分钟
Tian Pan
Software Engineer

2024 年 8 月,安全研究人员发现 Slack AI 在回答查询时会将公开频道和私密频道的内容同时拉入同一个上下文窗口。公开频道中的攻击者可以精心构造一条消息,当 Slack AI 摄取该消息时,就会将指令注入受害者的会话——由于 Slack AI 不引用来源,由此导致的数据外泄几乎无从追踪。这种攻击甚至可以泄露私信中嵌入的 API 密钥。Slack 在负责任披露后修复了这一问题。

这并不是传统意义上的漏洞。它是将上下文视为无用户访问控制的共享可变资源所带来的后果。而这正是大多数正在构建共享 AI 助手的团队现在都在犯的错误,只是更加悄无声息而已。

当你为单个用户构建 AI 功能时,你大多可以不去考虑上下文归属。会话属于一个人;上下文窗口中的任何内容都是他的。但当你部署一个团队 Slack 机器人、共享工作空间助手或实时协作 AI 层时,你就引入了一个仅靠身份验证无法解决的问题:多个用户、多种意图,以及一个不知道自己属于谁的上下文窗口

为什么应用层的授权还不够

工程师们倾向于用身份验证和授权来思考多用户安全:检查 JWT、验证权限,然后继续。对于传统 API,这种思维模型是成立的。对于 AI 系统,它在上下文层就失效了。

原因如下:在大多数共享 AI 实现中,上下文窗口在请求时一次性组装完毕后交给模型。这个组装步骤从对话历史、记忆存储、检索到的文档和当前会话状态中提取信息。如果组装逻辑在每个步骤都没有执行每用户边界,就会产生交叉污染——而模型对此毫不知情,它只是对窗口中的内容进行推理。

这就是 Giskard 研究所称的跨会话泄漏:模型向错误的用户返回了有效数据,因为运行时在推理之前未能强制执行边界,而不是因为模型本身出了问题。事后用输出过滤器来修复,无异于覆水难收。

到 2025 年上半年,仅 Microsoft Copilot 就通过这类失败在每个组织中暴露了约 300 万条敏感记录——不是因为身份验证被破坏,而是因为该工具在上下文组装步骤中没有对每用户进行范围限定地访问了共享组织数据存储。

生产环境中出现的三种失效模式

用户间的上下文泄漏是最明显的失效。用户 A 的对话历史或记忆泄漏到用户 B 的上下文中。当会话状态按团队或工作空间 ID 而不是用户 ID 存储时,当对话摘要被写入共享池时,或者当检索系统使用组织级嵌入而没有用户范围过滤时,就会发生这种情况。结果是用户 B 的 AI 响应被用户 A 的私有数据微妙地(或明显地)影响。

共享历史中的意图竞争更为隐蔽。当一个团队机器人维护共享对话线程时——大多数 Slack 机器人默认如此——模型将所有先前的轮次读取为一段连贯的历史记录。但线程中不同的用户有不同的目标、不同的领域知识和不同的期望。模型将它们混淆了。线程后期用户 A 的提问会被三轮之前用户 B 所说的内容所解读。这种叠加效应意味着共享线程机器人随着团队规模的扩大,使用价值往往会下降,而且没有人能说清楚为什么。

跨会话的个性化污染是持续时间最长的失效。保存用户偏好、学习行为和对话上下文的记忆系统是最有价值的 AI 功能之一——也是多用户环境中最危险的功能之一。当记忆的范围过于宽泛时,用户的偏好会污染组织级上下文。更糟糕的是,对抗性记忆投毒——将指令注入持久存在于会话终止后的共享记忆存储——可以影响所有未来用户的体验。Microsoft Security 在 2026 年记录了这种攻击模式:注入 AI 记忆中的指令在会话终止后仍然存在,并重定向了后续用户的交互。

真正有效的隔离模式

基本设计原则是:上下文是投影,而不是存储。持久状态存储在以 userId 为键的存储中。每次推理调用通过从该用户的基础加上当前会话投影来组装上下文窗口。上下文窗口本身是短暂的,永远不会写回共享状态。将 sessionId(当前对话)与 memoryId(用户身份)分离,是大多数团队跳过的第一步。

双层记忆架构将此正式化。私有记忆按用户隔离敏感、个人和会话特定的数据。共享记忆支持受控的知识传输——团队规范、代码库上下文、项目历史——并有明确的访问策略来控制可以检索什么以及由谁检索。协作记忆系统的研究通过动态访问控制将此正式化:对共享记忆的读写在继续之前会检查策略层,而不是在全局存储上自由操作。

共享上下文契约明确定义了什么是世界状态(可以跨所有用户安全共享:公共文档、代码库、团队规范),什么是用户私有(永不共享:私信、个人偏好、个人历史),什么是角色范围(在权限组内共享:给定项目团队成员的项目上下文)。将其视为 API 契约,而不是实现细节。写下来,在添加新的上下文来源时审查它。

带检索时过滤的每用户上下文组装是操作上最重要的模式。每个检索调用——向量搜索、文档查找、记忆提取——都必须将用户身份作为过滤器,而不仅仅是作为日志字段。检索系统是跨用户污染最常见进入上下文窗口的地方,因为它是距离面向用户的身份验证层最远的步骤。

子 Agent 功能隔离适用于更复杂的系统。规划 Agent 携带任务状态,检索 Agent 处理查找,执行 Agent 只接收它们所需的子任务上下文。没有 Agent 在设计上会积累跨用户上下文。当 Agent 专门化且范围明确时,一个 Agent 中意外的跨用户读取不会通过整个管道传播。

团队规模下的竞态条件

共享 AI 基础设施引入了一类大多数团队只有在生产环境中才会发现的并发错误。

最常见的是 Token 预算上的 TOCTOU(检查时间到使用时间)竞态:每用户或每团队的配额在推理调用之前被检查,但到调用分派时,来自同一租户的另一个并发请求已经消耗了可用预算。模型调用无论如何都会继续。在低流量下这是不可见的;在团队规模下,它会变成失控的成本和不公平的资源分配。修复方法是同步执行——在分派之前原子地检查并预留预算,而不是之后。

共享记忆中的写冲突更为隐蔽。如果两个用户的会话同时触发记忆写入,最后写入者获胜意味着一个用户的记忆更新会悄悄覆盖另一个用户的。对于大多数记忆系统来说,这意味着在并发负载下,记忆在系统最活跃的时候恰恰不那么可靠。乐观并发——对记忆记录进行版本控制并拒绝冲突的写入——在不需要全局锁的情况下处理这个问题。

上下文组装排序是第三个竞态,特别出现在实时协作场景中。当多个用户同时向共享会话贡献内容时(带有 AI 助手的实时文档编辑、带机器人的结对编程),将贡献组装到上下文窗口的顺序决定了模型对协作意图的解读。非确定性的组装顺序即使在完全确定性的模型中也会产生非确定性的输出。上下文组装的显式排序——按到达顺序合并的时间戳事件,而不是随机顺序——可以防止这种情况。

衡量你是否存在这个问题

大多数团队在导致可见事件之前不知道自己是否存在上下文泄漏。到那时,很多敏感数据可能已经流向了错误的方向。

Microsoft Research 的 PrivacyChecker 系统通过添加上下文完整性检查,将 GPT-4o 上的信息泄漏率从 33% 降低到 8%——但你必须先测量泄漏率才能知道有问题需要修复。一个简单的审计:取你的系统为用户 A 最近生成的 20 个 AI 响应。如果用户 B 的数据在上下文中,其中有任何一个会有显著变化吗?如果你不能自信地回答"不会",你的上下文组装就存在范围问题。

对于使用共享向量索引的团队:审计当你针对足够通用可以匹配多个用户历史的查询进行检索时,哪个用户的数据浮现出来。像"总结最近的决策"这样在组织范围索引中的查询,会浮现出嵌入空间中碰巧最近的内容,而不是请求用户的适当内容。每用户索引分区或强制元数据过滤是解决方案。

组织盲点

大多数团队不在前期就设计上下文归属的原因是,共享 AI 功能通常由将自己视为在构建生产力工具而非多租户系统的团队构建。多租户需要在访问模型、隔离原语和数据治理方面的前期投资——当你试图发布一个 Slack 机器人时,这些工作感觉像是管道工程。

但在共享 AI 功能部署后再进行上下文隔离改造的成本很高。记忆存储必须迁移和重新划定范围。检索索引必须重新分区。对话历史必须审计以发现泄漏的数据。工作量随部署规模的扩大而增加。

防止这种情况的思维模型转变是:一旦超过一个人的数据可以影响 AI 响应,你就在构建一个多租户系统。隔离要求由此而来,而不是取决于产品团队是否考虑过将其标记为多租户系统。

将共享 AI 功能视为多用户 API 来构建的团队——具有明确的所有权模型、访问策略和数据边界——发布的系统更安全,在事件响应上花费的时间更少。AI 组件是概率性的这一事实并不会改变这些要求,只会使违规行为在发生时更难检测。

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