一个本地 AI Agent 要操作多个门店或 SaaS 账号,应该怎样安全切换身份?

一、为什么"已经授权成功"还不够?

假设一位运营人员同时管理两个门店:

text 复制代码
门店 A:开发调试门店
门店 B:正式运营门店

他希望在本地智能体里完成这些任务:

  • 查询门店 A 的员工和商品;
  • 修改门店 A 的一项测试数据;
  • 切换到门店 B 查询真实订单;
  • 必要时撤销门店 A 的授权,但保留门店 B;
  • 第二天重新打开客户端时,仍能分清两个连接。

如果系统只考虑"能不能授权一次",很容易变成下面这样:

text 复制代码
本地 Agent
  -> 一个全局 Token
  -> 一个当前 tenant_id
  -> 所有对话和工具共用

这套方案在单账号演示中可能看不出问题,但进入多账号场景后会立刻暴露风险:

  • 当前 Token 到底属于哪个账号?
  • 切换门店时,旧对话是否也跟着换了身份?
  • 模型能不能自己修改 tenant_id
  • 同一个账号重新授权,是替换旧连接还是再增加一条?
  • 删除本地配置以后,服务端授权是否仍然有效?
  • 一个连接失效,会不会把其他账号一起退出?

所以,多账号能力不是在设置页增加一个下拉框。

它本质上是一个身份生命周期问题。

二、用户切换的不是"网址",而是完整业务身份

很多系统会把多个门店部署在不同路径下:

text 复制代码
https://example.com/tenant/store-a/
https://example.com/tenant/store-b/

于是最直觉的实现是,让用户在客户端里填写不同的业务网址。

但网址只能帮助定位入口,不能独立证明当前业务身份。

一条真正可执行的业务身份至少包含:

text 复制代码
业务系统
+ 当前账号
+ 当前租户或门店
+ 当前角色与实时权限
+ 允许进入的中枢工作区
+ 对应的 Agent Session
+ 本地安全凭据
+ 可撤销状态

其中任何一项变化,都可能改变本次操作是否允许成立。

因此,一个本地连接不能只保存:

json 复制代码
{
  "name": "门店 A",
  "url": "https://example.com/tenant/store-a/"
}

更合理的抽象应该是:

text 复制代码
一个连接
  = 一次由业务系统确认的可信身份授权
  + 一组非秘密连接元数据
  + 一份由操作系统保护的可撤销凭据

用户看到的"门店 A""测试环境""客户 CRM"只是本地别名,不是权限事实。

权限事实仍然来自业务系统当前登录态、中枢绑定结果和执行时的实时校验。

三、四种看起来省事、实际上很危险的实现

1. 让模型填写 user_idtenant_id

例如把工具设计成:

json 复制代码
{
  "tenant_id": "store_a",
  "user_id": "user_001",
  "action": "employee.update"
}

如果服务端相信这些字段,那么模型只需要改一个字符串,就可能尝试访问另一个租户。

模型可以提供业务对象和用户意图,但不能生成自己的行动主体。

2. 给所有门店共用一个超级管理员 Token

这样确实容易调用接口,却把业务系统原有的用户、角色和租户隔离全部压扁成一个高权限入口。

一台电脑丢失、一份日志泄露或一次错误工具调用,都可能影响所有门店。

3. 同一个别名每次授权都静默覆盖

用户原本连接的是测试门店,重新授权时浏览器里登录的却是正式门店。

如果客户端不比较授权后的真实身份,直接覆盖 default,下一轮任务就可能在用户没有意识到的情况下切换业务主体。

4. 切换账号时复用原来的长对话

旧对话中可能已经包含:

  • 门店 A 的员工编号;
  • 门店 A 的订单结果;
  • 门店 A 可用的工具;
  • 针对门店 A 的知识与记忆;
  • 一项尚未完成的写操作或审批。

如果把同一条对话直接切换到门店 B,模型很可能继续引用旧对象。

多身份系统最怕的并不是"无法连接",而是连接成功后发生安静的上下文串用。

四、更合理的入口:一个公开连接配置,浏览器决定真实登录身份

对通用智能体插件来说,开发者当然需要配置自己的中枢,而不是写死某一家业务系统。

最终用户可以填写或导入这样的非秘密配置:

text 复制代码
hubUrl=https://hub.example.com
clientAppId=merchant-agent
workspace=operations
connectionName=default

这里不应该包含:

  • 业务账号密码;
  • 浏览器 Cookie;
  • Tool Provider Secret;
  • 数据库地址;
  • 超级管理员 Token;
  • 写死的某个门店用户 ID。

当用户点击"添加连接"时,客户端发起一次临时授权请求,并打开系统浏览器:

text 复制代码
本地 Agent
  -> 中枢创建一次性授权请求
  -> 系统浏览器打开业务授权入口
  -> 业务系统读取当前登录态
  -> 用户确认当前账号、租户、设备与允许范围
  -> 返回一次性授权码
  -> 客户端完成 PKCE 兑换
  -> 建立一个可撤销 Agent Session

这种方式有两个重要好处:

第一,用户不需要把业务密码交给插件。

第二,授权的是浏览器当前真实登录身份,而不是客户端猜测出来的门店地址。

OAuth 原生应用最佳实践建议通过外部浏览器完成授权,并要求公共原生客户端使用 PKCE,避免应用直接接触用户凭据,也降低授权码被截获后重放的风险。参见 RFC 8252

需要强调的是,PKCE 只保护授权过程。

最终"当前是谁、属于哪个租户、拥有什么业务权限",仍然必须由业务系统服务端派生。

五、同一个统一授权入口,怎样添加第二个账号?

通用插件不应该要求开发者为每个门店硬编码一个授权地址。

更合理的体验是:

  1. 用户在客户端点击"添加连接";
  2. 系统打开同一个公开授权入口;
  3. 业务系统根据当前浏览器登录状态识别账号;
  4. 如果一个账号能访问多个门店,业务授权页由业务系统让用户选择;
  5. 用户确认后,客户端获得这次授权对应的真实身份摘要;
  6. SDK 决定替换现有连接,还是创建一条新连接。

如果用户想换账号,可以在业务授权页面退出当前账号,再用另一个账号登录。

这和很多 CLI 的浏览器授权体验类似:

text 复制代码
入口保持不变
真正授权哪个账号
取决于浏览器中登录并确认的是谁

对于不同业务系统,中枢可以配置不同 Client App;对于同一个业务系统下的多个门店,不需要让本地插件理解每一家 SaaS 的私有 URL 规则。

门店选择、首次改密、账号退出和业务登录都属于业务系统自己的授权门户。

通用 SDK 只负责发起授权、接收结果和管理连接生命周期。

六、同身份重新授权与不同身份授权,处理规则不能一样

授权完成后,客户端不应该只看用户输入的连接名称,而应该比较服务端返回的稳定身份绑定。

可以遵循下面这组规则。

情况一:同一绑定、同一业务身份

例如用户原来已经授权门店 A,Token 即将过期,于是重新完成授权。

合理结果是:

text 复制代码
更新门店 A 的 Session 和凭据
保留门店 A 的本地别名
旧凭据失效
不增加重复连接

情况二:同一绑定、不同业务身份

例如 default 原来代表门店 A,重新授权时浏览器里登录的是门店 B。

合理结果是:

text 复制代码
保留门店 A
为门店 B 创建一个新的可识别别名
提示用户新身份已经加入
不静默覆盖门店 A

情况三:相同显示名称,但真实身份不同

两个业务系统都可能显示"管理员"或"总店"。

本地别名可以自动追加短标识,例如:

text 复制代码
总店
总店-2

但真正用于隔离的不能是显示名称,而应是服务端返回的稳定主体标识、租户标识和绑定关系。

情况四:授权中途取消或失败

用户关闭浏览器、拒绝授权或网络超时,不应该破坏已有连接。

旧连接继续保持原状态,新连接只有在完整兑换成功并安全保存凭据后才进入可用状态。

这是一条很重要的失败原则:

添加新身份失败,不能顺手把现有身份登出。

七、连接切换必须由用户明确触发,模型不能自己换号

多连接建立以后,本地智能体可以提供:

text 复制代码
connections list
connections add
connections use <name>
connections remove <name>
connections revoke <name>

桌面客户端也可以把它们做成连接管理页面。

但有一条边界必须固定:

模型可以提醒用户当前连接不匹配,但不能自行从门店 A 切换到门店 B。

原因很简单。

用户说"帮我查一下今天的订单",并不代表模型有权决定查哪个租户。

如果当前上下文存在歧义,智能体应该明确展示:

text 复制代码
当前连接:开发调试门店
目标动作:查询今日订单

需要换账号时,由用户点击或执行明确的切换命令。

这比让模型根据自然语言猜"他说的大概是正式门店"可靠得多。

八、切换连接只影响新会话,旧会话应继续绑定原身份

连接选择和对话上下文不能混成一个全局变量。

推荐规则是:

text 复制代码
当前默认连接
  -> 只决定下一条新会话使用哪个身份

已经开始的会话
  -> 固定绑定创建时的连接、主体与工作区

例如:

text 复制代码
09:00 创建会话 A,绑定门店 A
09:10 用户把默认连接切换为门店 B
09:12 创建会话 B,绑定门店 B
09:15 继续回复会话 A,仍然只能使用门店 A

这样可以避免一条正在等待审批或工具结果的任务,在中途突然更换业务身份。

如果用户确实希望用门店 B 重新处理相同问题,应该创建一条新会话,并重新建立该身份下的能力和知识上下文。

九、身份隔离以后,工具、知识和记忆也要重新投影

切换身份并不只是换一份凭据。

不同租户可能具有不同的:

  • 工具授权范围;
  • 写操作白名单;
  • 审批规则;
  • 业务知识库;
  • 页面上下文;
  • 用户角色;
  • 对象可见范围。

因此,新会话开始时,本地 Agent 应根据当前连接向中枢获取这次身份对应的运行上下文:

text 复制代码
当前业务主体
  -> 允许进入的 workspace / route
  -> 当前可见的能力目录
  -> 本轮相关工具
  -> 相关知识与公开记忆
  -> 风险、审批和审计规则

模型不能把门店 A 搜索过的内部工具列表永久缓存,再拿到门店 B 继续使用。

即使两个门店使用相同的接口名称,执行时也要重新验证当前 Session、路由、能力范围和业务权限。

十、凭据不能明文放进插件配置文件

多账号以后,凭据数量会增加。

如果每个连接都把 refresh token 写进 JSON、.env 或插件配置,用户复制配置、提交仓库、发送截图时就可能一起泄露。

更合理的做法是把数据分成两类。

可以保存在普通配置中的非秘密元数据

text 复制代码
hubUrl
clientAppId
workspace
connectionName
当前选择的连接名称
非敏感身份显示摘要

必须进入系统安全存储的秘密

text 复制代码
access token
refresh token
凭据版本
与刷新和撤销有关的秘密状态

在 macOS 上,可以使用 Keychain Services 保存小型秘密。Apple 官方将 Keychain 定位为应用安全保存密码、密钥等小型敏感数据的机制,参见 Keychain Services

在 Windows 上,可以使用当前用户作用域的 DPAPI。默认情况下,CryptProtectData 保护的数据只能由同一台机器上的同一用户账户解密;它不应该被当作跨机器同步方案,参见 Microsoft DPAPI 文档

这也意味着:

  • 连接配置可以导出;
  • 业务凭据不应该跟着明文导出;
  • 换电脑后通常需要重新授权;
  • 不同系统用户之间不应自动共享连接;
  • 安全存储不可用时应失败关闭,而不是退回明文保存。

跨平台支持不是简单地"Windows 也能运行 Node.js"。

只要 Agent Session 需要长期恢复,就必须把 Windows、macOS 和 Linux 的凭据存储边界一并考虑。

十一、本地删除和服务端撤销不是一回事

连接管理至少需要区分两个动作。

删除本地连接

它表示:

text 复制代码
本机不再保存这条连接和凭据

适合用于清理本地环境,但不一定能证明服务端 Session 已经失效。

撤销远端授权

它表示:

text 复制代码
中枢或业务系统把这条 Agent Session 标记为不可继续使用

适合设备丢失、员工离职、权限收回或用户主动解除授权。

更完整的"移除连接"流程通常是:

  1. 请求服务端撤销对应 Session;
  2. 服务端明确返回撤销结果;
  3. 清除本地安全凭据;
  4. 从连接注册表删除本地元数据;
  5. 如果删除的是当前默认连接,明确选择一个剩余连接或回到未连接状态。

如果远端撤销失败,客户端不应该假装已经全部完成。

它可以清楚提示:

text 复制代码
本地凭据已清除
远端撤销尚未确认
请在中枢后台检查设备授权

对于企业系统,"界面里看不见了"和"凭据已经失效"是两个不同事实。

十二、用两个门店走一遍完整流程

假设本地智能体已经连接开发调试门店。

第一步:查看当前连接

text 复制代码
当前:开发调试门店
状态:可用
工作区:门店运营

用户发起:

查询当前门店的一位员工,并修改测试备注。

本地 Agent 在开发调试门店身份下完成查询和允许的可回滚写操作,中枢记录会话、工具调用和审计轨迹。

第二步:添加第二个门店

用户点击"添加连接"。

浏览器打开统一授权入口。用户退出原账号,登录另一个真实账号,并在业务授权页确认正式运营门店。

客户端识别这是不同业务身份,于是保留原连接并增加:

text 复制代码
开发调试门店
正式运营门店

第三步:明确切换

用户选择"正式运营门店"。

客户端提示:

text 复制代码
已将正式运营门店设为新会话默认连接
现有会话不会改变身份

接下来新建的对话使用正式运营门店;之前的测试对话仍固定在开发调试门店。

第四步:单独撤销测试门店

用户撤销"开发调试门店"。

服务端 Session 失效,本地安全凭据被清理;正式运营门店继续保持可用。

这个流程真正证明的不是"客户端有一个下拉框",而是:

text 复制代码
不同身份能够共存
+ 切换由用户明确触发
+ 对话不会跨身份漂移
+ 一个身份可以单独撤销
+ 另一个身份不受影响

十三、BailingHub 在这套架构里负责什么?

BailingHub v0.5.0 已经公开 Agent Client v1 的基础接入边界,包括:

  • 外部浏览器授权与 PKCE;
  • 可撤销 Agent Session;
  • 本地 Agent 编排使用的 Runtime;
  • route、工具范围与 ACC 审批继承;
  • Conversation、Agent Run、工具调用和审计轨迹;
  • 宿主无关的 Agent Client SDK;
  • DeepSeek Harness 等宿主通过独立插件接入。

这些公开能力解决的是"本地 Agent 怎样在不接触业务账号密码和 Cookie 的前提下,获得一个受治理业务身份"。

当系统继续扩展到多账号、多门店与多业务身份时,应继续保持相同边界:

组件 负责什么
本地 Agent / Desktop 用户交互、明确选择连接、本地推理和多步编排
通用 SDK / 插件 浏览器授权、连接注册、凭据安全存储、刷新、切换与撤销
BailingHub 中枢 Client App、允许路由、身份绑定、能力投影、审批、执行与审计
业务系统 登录页面、账号与门店选择、可信身份派生、实时权限和最终业务裁决

这里不应该出现一个"专门为某家 SaaS 写死门店 URL"的通用插件。

不同业务系统只需要接入统一 Agent Auth 方法,并由自己的授权页面处理登录和业务主体选择。

ACC 在其中描述工具风险、审批和执行语义,但不承担账号登录、租户选择或本地凭据存储。

十四、开发者接入多业务身份时的检查清单

授权入口

  • 客户端打开的是公开统一授权入口;
  • 业务账号密码只输入业务系统页面;
  • 使用系统浏览器和 PKCE;
  • 授权页面清楚展示当前账号、租户、设备和范围;
  • 用户可以退出并换号;
  • 多门店选择由业务系统根据当前账号权限提供。

身份与连接

  • 同一连接保存稳定身份绑定,不依赖显示名称;
  • 同身份重新授权会替换旧凭据,而不是制造重复连接;
  • 不同身份授权会共存,不静默覆盖;
  • 模型不能自行切换连接;
  • 切换默认连接只影响新会话;
  • 旧会话继续固定原身份和工作区。

凭据与撤销

  • 秘密不进入 JSON、.env、日志和导出配置;
  • macOS 使用 Keychain 或等价安全存储;
  • Windows 使用当前用户作用域 DPAPI 或等价安全存储;
  • 安全存储不可用时失败关闭;
  • 本地删除和远端撤销状态能够区分;
  • 一个连接撤销不会清除其他连接。

业务执行

  • 每轮新会话按当前身份重新投影工具、知识和规则;
  • user_idtenant_id 不由模型自由填写;
  • 中枢重新校验 Session、route、工具与审批;
  • 业务系统保留实时权限和对象归属的最终裁决;
  • 后台能按 Conversation、Run 和工具步骤还原行动;
  • 日志不记录 Token、Cookie 和未脱敏业务数据。

常见问题

1. 一个本地 AI Agent 可以连接多个业务系统吗?

可以。不同中枢或不同 Client App 可以形成独立公开绑定;同一绑定下也可以存在多个经过业务系统确认的身份。关键是每条连接必须独立存储、独立刷新、独立撤销,不能共用一个高权限 Token。

2. 切换门店时需要重新填写中枢地址吗?

通常不需要。同一个业务系统可以使用统一授权入口,由浏览器当前登录账号和业务授权页决定具体门店。更换中枢或接入完全不同的业务系统时,才需要增加另一组公开连接配置。

3. 可以让模型自动选择最合适的门店吗?

不建议。模型可以发现当前连接可能不匹配并请求用户确认,但身份切换应由用户明确触发。否则一句含糊的"查今天订单"就可能访问错误租户。

4. 同一个账号拥有多个门店怎么办?

业务系统授权页应根据该账号的真实权限列出可选门店,用户确认其中一个或一个明确范围。客户端不应该通过猜测 URL 或让模型填写门店编号来决定。

5. 关闭本地客户端算退出业务系统吗?

不一定。关闭程序通常只结束本地进程;可恢复 Agent Session 仍可能存在。需要解除授权时,应执行明确的远端撤销并清理本地安全凭据。

6. Windows 客户端为什么不能直接把 Token 写进配置文件?

因为 Windows 能运行插件不等于具备安全的长期凭据恢复。需要使用 DPAPI 等操作系统能力,把凭据绑定到当前用户或设备,并在保护上下文不可用时失败关闭。

7. 多账号功能属于 ACC 吗?

不属于 ACC Core。ACC 关注动作风险、审批和执行结果等治理语义;账号登录、连接管理、凭据存储和租户切换属于 Agent Client、中枢与业务系统的工程职责。

8. 多身份共存是否代表用户可以跨租户操作?

不是。共存只表示本机保存了多条独立授权。每次会话仍然绑定其中一个身份,业务系统继续按当前主体、角色和对象归属作最终授权。

结语:多账号的关键不是"切得快",而是"永远不会悄悄切错"

本地 AI Agent 开始操作真实业务系统以后,多门店、多租户和多账号几乎一定会出现。

真正可靠的方案,不是把更多 Token 塞进配置文件,也不是让模型自己猜应该使用哪个租户。

它应该满足:

text 复制代码
一个公开授权入口
+ 浏览器确认真实业务身份
+ 同身份重授权安全替换
+ 不同身份独立共存
+ 用户显式切换
+ 旧会话身份不漂移
+ 工具和知识按身份重新投影
+ 凭据进入操作系统安全存储
+ 每条连接可以单独撤销
+ 中枢和业务系统共同保留审计与最终授权

当这些边界成立以后,本地智能体才真正具备从"连接一个后台"走向"管理多个业务身份"的基础。

如果你正在为商城、CRM、ERP、工单或 SaaS 后台开发本地 Agent,不妨先验证两条真实身份:

同一个公开绑定,授权两个业务账号,确认它们可以共存、明确切换、会话隔离并单独撤销。

这比先做一个漂亮的账号下拉框,更接近系统是否真的安全可用。


项目与延伸阅读:

相关推荐
wangchunyu11416 分钟前
Aider介绍和安装说明
人工智能
SpaceAIGlobal27 分钟前
AI做PPT 工具哪个好:先搞懂原理,再按 3 个维度挑对那一个(2026)
人工智能·powerpoint
β添砖java39 分钟前
深度学习28RNN循环神经网络
人工智能·深度学习
2301_818474441 小时前
福建中小微企业云 ERP 落地实践:轻量化信息化项目实施指南
大数据·人工智能
咕泡科技1 小时前
咕泡科技×创业酵母俞头私享会:AI时代,组织如何长出“破局力”?
大数据·人工智能·科技
啊阿狸不会拉杆1 小时前
《自然语言处理:基于大语言模型的方法》第1章 绪论 读书笔记
人工智能·自然语言处理·nlp·easyui·智能体
我是大AI1 小时前
实战解析:基于多源交叉验证的AI幻觉治理架构与GEO行业解决方
人工智能·架构
LTD营销SaaS1 小时前
22站点智能成功申请“AI 创建业务型网站”发明专利
人工智能·ai建站·站点智能·22集团·ai创建业务型网站
Acrel12341 小时前
DC800V 大规模落地,算力机房安全该如何保障
安全