企业微信多租户记忆隔离方案
本文档记录企业微信(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.py 或 run_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其他用户时,可临时读取对方记忆片段