x

企业微信多租户记忆隔离方案

本文档记录企业微信(WeCom)用户级多租户记忆隔离的完整实施方案。

方案状态:待用户确认后执行

更新时间:2026-05-05


一、背景与现状

1.1 当前架构

当前 Hermes Agent 运行在单 gateway 进程模式,所有企业微信用户共享同一套系统资源:

资源 路径 现状
全局记忆 ~/.hermes/memories/MEMORY.md 10个用户共用
全局用户配置 ~/.hermes/memories/USER.md 10个用户共用
会话历史 ~/.hermes/sessions/*.jsonl 按 session_id 区分,物理不隔离
状态数据库 ~/.hermes/state.db(190MB) 全局共用
定时任务 cron 全局池 所有用户共享同一份
技能 ~/.hermes/skills/ 全局共享

核心问题:agent 回复时没有注入用户身份上下文,无法区分"当前在和谁对话",导致记忆互相污染。

1.2 目标

每个 WeCom 用户拥有:

  • 私有记忆文件:只能看到和修改自己的对话记忆
  • 私有用户配置:称呼偏好、提醒频次等个人设置
  • 隔离的会话历史:通过 session key 路由到对应目录
  • 全局共享资源:公司架构、技能文档、系统级配置仍然共用

二、WeCom 用户清单

WeCom ID 真名 角色 Profile 目录
XiaGuoDong 夏雨(夏国栋) 总经理/法人 default(全局视角,不隔离)
Xiao 徐滔 技术部 wecom/Xiao/
SiNeiKe 杨卓 技术部 wecom/SiNeiKe/
YiYeZhiQiu 王嘉成 技术部 wecom/YiYeZhiQiu/
aiden 杨彪 技术部 wecom/aiden/
ShuGuang 张家学 客服部 wecom/ShuGuang/
LiuWenWei 刘文伟 客服部 wecom/LiuWenWei/
ZhangYuXiaoWanZi 张玉杰 客服部 wecom/ZhangYuXiaoWanZi/
ShiHuangZhe 朱一花 客服部 wecom/ShiHuangZhe/
BoWeiYa 戴美秋 招商部 wecom/BoWeiYa/

说明:夏总(XiaGuoDong)保持使用 default 全局 profile,不纳入用户隔离体系,始终拥有全局视角。


三、目录结构设计

~/.hermes/
├── memories/
│   ├── MEMORY.md              # 全局记忆(共享,公司架构/技能等)
│   ├── USER.md                # 全局用户配置
│   └── wecom/                 # WeCom 用户私有记忆
│       ├── Xiao/
│       │   ├── MEMORY.md      # 徐滔私有记忆
│       │   └── USER.md        # 徐滔私有配置
│       ├── SiNeiKe/
│       │   ├── MEMORY.md      # 杨卓私有记忆
│       │   └── USER.md
│       ├── YiYeZhiQiu/
│       │   ├── MEMORY.md      # 王嘉成私有记忆
│       │   └── USER.md
│       ├── aiden/
│       │   ├── MEMORY.md      # 杨彪私有记忆
│       │   └── USER.md
│       ├── ShuGuang/
│       │   ├── MEMORY.md      # 张家学私有记忆
│       │   └── USER.md
│       ├── LiuWenWei/
│       │   ├── MEMORY.md      # 刘文伟私有记忆
│       │   └── USER.md
│       ├── ZhangYuXiaoWanZi/
│       │   ├── MEMORY.md      # 张玉杰私有记忆
│       │   └── USER.md
│       ├── ShiHuangZhe/
│       │   ├── MEMORY.md      # 朱一花私有记忆
│       │   └── USER.md
│       └── BoWeiYa/
│           ├── MEMORY.md      # 戴美秋私有记忆
│           └── USER.md
├── sessions/                   # 会话历史(session key 路由,不物理拆分)
└── state.db                    # 全局状态数据库(暂不拆分)

四、完整隔离维度说明

4.1 私有记忆文件(核心改动)

读取逻辑

最终 prompt = 系统模板 
            + 全局记忆 (memories/MEMORY.md) 
            + 用户私有记忆 (memories/wecom/{user_id}/MEMORY.md)

写入逻辑

  • 普通对话记忆默认写入用户私有 MEMORY.md
  • 标注 [GLOBAL] 的内容写入全局 MEMORY.md
  • 用户私有记忆之间完全隔离,互不干扰

去重机制:每次写入前检查最近 20 条是否有相同内容,有则跳过,避免记忆文件无限膨胀。


4.2 私有用户配置

每个用户的 USER.md 包含:

  • 称呼偏好(如"叫我小张")
  • 提醒频次
  • 常用工作域
  • 个人备注

格式与全局 USER.md 相同,agent 在构建上下文时自动加载对应用户的配置。


4.3 会话历史

  • .jsonl 文件继续存在 ~/.hermes/sessions/ 目录,不按用户物理拆分
  • 通过 session key(wecom:dm:Xiao)路由到对应会话链
  • 全局搜索场景下可跨用户检索会话历史

4.4 群聊记忆

场景 记忆位置 说明
群聊 memories/wecom/group:{group_id}/MEMORY.md 群级别私有记忆
私聊 memories/wecom/{user_id}/MEMORY.md 用户私有记忆
  • 群聊记忆不写入任何个人私有记忆
  • 各用户在群聊中看到的上下文是群共享的,不污染个人记忆

4.5 定时任务(Cron Jobs)

  • 全局任务池skills/ 和系统级任务走全局 cron 调度
  • 用户私有任务:个人提醒类任务写入 wecom/{user_id}/crontab,触发后结果投放到对应用户
  • 定时任务输出默认投递到触发者本人

4.6 技能(Skills)

  • 所有技能全局共享,暂不做权限隔离
  • 如需未来精细化控制,可扩展为 skills/shared/ + skills/private/{user_id}/

4.7 工具能力(Tools)

  • 所有用户共用同一套工具能力(数据库查询、文件读写、git 操作等)
  • 暂不做工具级权限隔离(当前 10 个用户均为内部员工)

4.8 新用户自动创建

wecom.py 消息路由入口处增加逻辑:

user_dir = f"~/.hermes/memories/wecom/{user_id}/"
if not os.path.exists(user_dir):
    os.makedirs(user_dir)
    # 初始化默认 USER.md
    with open(os.path.join(user_dir, "USER.md"), "w") as f:
        f.write(f"# {user_id} 的个人配置\n\n- 创建时间:{datetime.now()}\n")

4.9 记忆容量与清理

  • 暂不做配额限制
  • 写入前去重(最近 20 条)
  • 暂不做自动过期,人工定期清理
  • 预计半年后如单用户记忆超过 1MB,再引入归档策略

4.10 与 Hermes 原生 Profile 模式的关系

Hermes 原生的 hermes profile多实例隔离模式(每个 profile 独立进程、独立端口),适用于不同业务线/服务器隔离。

本方案是单进程内的用户级隔离,路线不同,不使用原生 profile 模式。


4.11 特殊用户:夏总(XiaGuoDong)

夏总保持使用 default profile(全局视角),不切换到 wecom/XiaGuoDong/

  • 始终可读全局记忆
  • 可选择性查看指定用户的私有记忆(需显式调用)
  • 更符合管理者角色定位

4.12 state.db 处理

  • 暂不拆分(190MB,拆分风险高)
  • state.db 按 user_id 过滤查询即可实现逻辑隔离
  • 后续如性能下降,再考虑按用户拆分

五、实施步骤

Step 1:创建目录结构(约 5 分钟)

为 10 个用户(除夏总外)创建目录和初始化文件:

for user in Xiao SiNeiKe YiYeZhiQiu aiden ShuGuang LiuWenWei ZhangYuXiaoWanZi ShiHuangZhe BoWeiYa; do
  mkdir -p ~/.hermes/memories/wecom/$user
  echo "# $user 的个人配置" > ~/.hermes/memories/wecom/$user/USER.md
  echo "# $user 私有记忆" > ~/.hermes/memories/wecom/$user/MEMORY.md
done

Step 2:改造 wecom.py 路由入口(约 20 行)

文件:gateway/platforms/wecom.py

改动点:

  • build_session_key() 返回值中附加 user_id 上下文
  • 在消息处理入口处将 user_id 传递给上层 session 管理器

Step 3:改造 agent 记忆读写层(约 60 行)

文件:agent/memory.pyrun_agent.py

核心改动:

  • read_memory():优先读用户私有记忆,再读全局记忆
  • write_memory():默认写入用户私有记忆,支持 [GLOBAL] 标签
  • load_user_config():加载对应用户的 USER.md

Step 4:改造 session key 生成逻辑(约 20 行)

文件:gateway/session.py

  • 在构建 session key 时注入 platform_user_id 参数
  • 确保 session 路由到正确的用户上下文

Step 5:迁移现有全局记忆(约 1 小时)

  • 读取当前 ~/.hermes/memories/MEMORY.md
  • 识别各条记忆所属用户
  • 写入对应用户的私有 MEMORY.md
  • 全局共享信息保留在全局文件

Step 6:测试验证(约 15 分钟)

测试场景 预期结果
杨彪(aiden)发消息 读写 wecom/aiden/MEMORY.md
王嘉成(YiYeZhiQiu)发消息 读写 wecom/YiYeZhiQiu/MEMORY.md
两人记忆互不干扰 A 说的内容不在 B 的记忆中
夏总发消息 读写全局 MEMORY.md,不受影响

Step 7:处理群聊场景(约 10 行)

  • 判断消息类型(群聊/私聊)
  • 群聊写入 wecom/group:{group_id}/MEMORY.md
  • 私聊写入 wecom/{user_id}/MEMORY.md

六、核心代码改动汇总

步骤 文件 改动量 优先级
Step 1:建目录 shell 脚本 < 10 行 P0
Step 2:wecom 路由 gateway/platforms/wecom.py ~20 行 P0
Step 3:记忆读写层 agent/memory.py ~60 行 P0
Step 4:session key gateway/session.py ~20 行 P1
Step 5:记忆迁移 手动 + shell ~1 小时 P2
Step 6:测试验证 人工测试 ~15 分钟 P0
Step 7:群聊记忆 agent/memory.py ~10 行 P1

总计核心代码改动约 110-130 行


七、风险点

风险 概率 影响 应对
wecom.py 传递 user_id 链路过深 先打印日志验证链路完整性
记忆迁移时用户归属难以判断 保守策略:迁移失败的保留全局
state.db 性能下降 先跑压测再决定是否拆分
新用户首次消息时目录未创建 Step 1 自动创建 + wecom.py 兜底

八、后续扩展方向

  • 技能权限隔离skills/shared/ + skills/private/{user_id}/
  • 工具权限分级:客服岗禁用服务器命令,技术人员全开
  • 记忆可视化面板:用户可手动查看/编辑自己的私有记忆
  • 跨用户共享上下文:显式 @mention 其他用户时,可临时读取对方记忆片段
Left-click: follow link, Right-click: select node, Scroll: zoom
x