x

门店层级多租户AI智能体架构设计

版本:v1.0
日期:2026-04-30
概述:一个门店为一个集合,门店内不同人员共享权限允许的记忆和技能,同时保留私有记忆,不同门店之间完全独立隔离

一、整体架构

1.1 架构拓扑图

┌─────────────────────────────────────────────────────────────────┐
│                     Store A(门店A·完全隔离)                    │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────┐          │
│  │ 员工小张     │   │ 员工小李     │   │ 员工小王     │          │
│  │ ├─私有记忆   │   │ ├─私有记忆   │   │ ├─私有记忆   │          │
│  │ └─私有技能   │   │ └─私有技能   │   │ └─私有技能   │          │
│  └─────────────┘   └─────────────┘   └─────────────┘          │
│           ▲              ▲              ▲                        │
│           └──────────────┴──────────────┘                        │
│                    共享层(门店A)                                │
│           ├─ 门店共享记忆(客户资料/商品档案)                   │
│           └─ 门店共享技能(销售话术/盘点流程)                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ✕(完全隔离)
                              │
┌─────────────────────────────────────────────────────────────────┐
│                     Store B(门店B·完全隔离)                    │
│  ┌─────────────┐   ┌─────────────┐                             │
│  │ 员工小赵     │   │ 员工小周     │   ...                     │
│  │ ├─私有记忆   │   │ ├─私有记忆   │                            │
│  │ └─私有技能   │   │ └─私有技能   │                            │
│  └─────────────┘   └─────────────┘                             │
│           ▲              ▲                                      │
│           └──────────────┴────── 共享层(门店B)                │
└─────────────────────────────────────────────────────────────────┘

1.2 设计原则

原则 说明
门店隔离 不同门店之间完全隔离,不可互相访问对方数据
层级记忆 L1私有 > L2门店共享 > L3系统全局,加载优先级递减
技能继承 员工技能 = 私有技能 + 门店共享技能 + 全局技能
单一实例 一个 Hermes Gateway 实例,通过 session 隔离实现逻辑多租户

二、记忆层级设计

2.1 三层记忆模型

层级 名称 可见范围 可写范围 用途
L1 员工私有记忆 仅员工本人 仅员工本人 个人偏好、工作习惯、私人备注
L2 门店共享记忆 门店全员 门店全员 客户资料、商品档案、门店配置
L3 系统全局记忆 全系统 管理员 通用五金知识、公司政策

2.2 加载优先级

用户提问
    │
    ▼
L1 员工私有记忆 ──▶ 注入(最高优先级,覆盖后续同名key)
L2 门店共享记忆 ──▶ 注入
L3 系统全局记忆 ──▶ 注入(最低优先级)
    │
    ▼
AIAgent 处理

注意:同一 memory_key 高优先级会覆盖低优先级。例如员工设置了私有别名"L1_商品A库存=100",则该员工的回答中以此为准。

2.3 记忆示例

L1 员工私有记忆

员工小张:
  - 偏好: 喜欢用语音播报库存预警
  - 习惯: 每周一上午盘库
  - 备注客户: "李总喜欢抹零"

L2 门店共享记忆

门店A共享:
  - 客户档案: { "李总": { "信用额度": 50000, "账期": 30天 } }
  - 商品档案: { "螺纹钢Φ12": { "安全库存": 200, "供应商": "张三供应商" } }
  - 门店配置: { "营业时间": "8:00-20:00", "负责人": "王店长" }

L3 系统全局记忆

系统全局:
  - 五金行业知识库
  - 公司合规政策
  - 系统公告

三、Session 路由策略

3.1 路由逻辑

# 微信用户 → 映射到唯一的 (store_id, employee_id)
# session key = f"wechat_{openid}"

# 消息路由流程
gateway:
  wechat:
    # 解析消息中的 openid
    extract_openid: true

  session:
    # 自动创建新 session
    auto_create: true
    # session 过期时间(小时)
    ttl: 720  # 30天

  memory:
    # 加载顺序:员工私有 → 门店共享 → 系统全局
    load_order:
      - scope: "employee"    # L1
        source: "memory_employee"
      - scope: "store"       # L2
        source: "memory_store"  
      - scope: "global"     # L3
        source: "memory_global"

    # 隔离规则
    isolation:
      # 门店间完全隔离
      store_boundary: "strict"
      # 员工间:私有隔离,共享互通
      employee_rule: "private_isolated_shared_public"

3.2 用户映射表

CREATE TABLE user_store_mapping (
    openid        VARCHAR(64) PRIMARY KEY,   -- 微信 openid
    store_id      VARCHAR(32) NOT NULL,       -- 门店ID
    employee_id   VARCHAR(32) NOT NULL,       -- 员工ID(门店内唯一)
    employee_name VARCHAR(64),                -- 员工姓名
    role          ENUM('owner','manager','staff'),  -- 角色
    created_at    DATETIME DEFAULT NOW(),
    updated_at    DATETIME DEFAULT NOW() ON UPDATE NOW(),

    -- 联合唯一约束
    UNIQUE KEY uk_store_employee (store_id, employee_id)
);

四、数据模型

4.1 记忆表设计

-- L1: 员工私有记忆(仅本人可见/可写)
CREATE TABLE memory_employee (
    id           BIGINT AUTO_INCREMENT PRIMARY KEY,
    employee_id  VARCHAR(32) NOT NULL,
    memory_key   VARCHAR(128) NOT NULL,
    memory_value TEXT NOT NULL,
    created_at   DATETIME DEFAULT NOW(),
    updated_at   DATETIME DEFAULT NOW() ON UPDATE NOW(),

    UNIQUE KEY uk_employee_key (employee_id, memory_key),
    INDEX idx_employee (employee_id)
);

-- L2: 门店共享记忆(门店全员可见/可写)
CREATE TABLE memory_store (
    id           BIGINT AUTO_INCREMENT PRIMARY KEY,
    store_id     VARCHAR(32) NOT NULL,
    memory_key   VARCHAR(128) NOT NULL,
    memory_value TEXT NOT NULL,
    updated_by   VARCHAR(32),               -- 最后更新人
    created_at   DATETIME DEFAULT NOW(),
    updated_at   DATETIME DEFAULT NOW() ON UPDATE NOW(),

    UNIQUE KEY uk_store_key (store_id, memory_key),
    INDEX idx_store (store_id)
);

-- L3: 系统全局记忆(所有门店可见/仅管理员可写)
CREATE TABLE memory_global (
    id           BIGINT AUTO_INCREMENT PRIMARY KEY,
    memory_key   VARCHAR(128) NOT NULL UNIQUE,
    memory_value TEXT NOT NULL,
    updated_by   VARCHAR(32),               -- 最后更新管理员
    created_at   DATETIME DEFAULT NOW(),
    updated_at   DATETIME DEFAULT NOW() ON UPDATE NOW(),

    UNIQUE KEY uk_global_key (memory_key)
);

4.2 技能表设计

-- 技能定义表
CREATE TABLE skills (
    id           BIGINT AUTO_INCREMENT PRIMARY KEY,
    skill_code   VARCHAR(64) NOT NULL UNIQUE,
    skill_name   VARCHAR(128),
    skill_type   ENUM('global','store','employee') NOT NULL,
    scope_id     VARCHAR(32),              -- store_id 或 employee_id,global 为 NULL
    content      TEXT,                    -- 技能内容或脚本路径
    enabled      TINYINT DEFAULT 1,
    created_at   DATETIME DEFAULT NOW(),
    updated_at   DATETIME DEFAULT NOW() ON UPDATE NOW()
);

-- 技能示例
INSERT INTO skills (skill_code, skill_name, skill_type, scope_id) VALUES
-- L3 全局技能
('global_wujin_knowledge', '五金行业知识库', 'global', NULL),
('global_policy', '公司合规政策', 'global', NULL),
-- L2 门店共享技能
('store_sales_script', '销售话术模板', 'store', 'STORE_A'),
('store_inventory_process', '盘点流程指引', 'store', 'STORE_A'),
-- L1 员工私有技能
('employee_personal_note', '个人快捷备注', 'employee', 'EMP_A001');

五、技能共享设计

5.1 技能加载层级

skills:
  # L3: 系统全局技能(所有门店所有员工)
  global:
    - 五金产品知识库
    - 公司合规政策
    - 通用AI能力

  # L2: 门店共享技能(门店全员可用)
  store_shared:
    - 五金产品知识库(门店版)
    - 销售话术模板
    - 盘点流程指引
    - 库存预警规则

  # L1: 员工私有技能(仅本人可用)
  employee_private:
    - 个人快捷命令
    - 私人笔记
    - 个人工作习惯配置

5.2 技能加载算法

def load_employee_skills(employee_id: str, store_id: str) -> list:
    """加载某员工的完整技能列表"""
    skills = []

    # L3: 全局技能
    global_skills = db.query(
        "SELECT * FROM skills WHERE skill_type = 'global' AND enabled = 1"
    )
    skills.extend(global_skills)

    # L2: 门店共享技能
    store_skills = db.query(
        "SELECT * FROM skills WHERE skill_type = 'store' AND scope_id = ? AND enabled = 1",
        store_id
    )
    skills.extend(store_skills)

    # L1: 员工私有技能
    emp_skills = db.query(
        "SELECT * FROM skills WHERE skill_type = 'employee' AND scope_id = ? AND enabled = 1",
        employee_id
    )
    skills.extend(emp_skills)

    return skills

六、隔离规则总结

6.1 访问控制矩阵

操作 本店员工 跨店员工 管理员
读自己的私有记忆
写自己的私有记忆
读本店共享记忆
写本店共享记忆
读他店共享记忆
写他店共享记忆
读系统全局记忆
写系统全局记忆

6.2 记忆隔离示意

┌─────────────────────────────────────────────────────────────┐
│                        系统全局 (L3)                        │
│   [ 全员可见 ]  ─────────────────────────────────────────▶  │
│   [ 仅管理员写 ]                                            │
└─────────────────────────────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────────────────────────────┐
│                    门店A共享层 (L2)                         │
│                                                             │
│   员工A ──▶ 可见 ✅ ──▶ 可写 ✅                            │
│   员工B ──▶ 可见 ✅ ──▶ 可写 ✅                            │
│   员工C ──▶ 可见 ✅ ──▶ 可写 ✅                            │
│                                                             │
│   门店B ──▶ 可见 ❌ ──▶ 可写 ❌                            │
│   门店C ──▶ 可见 ❌ ──▶ 可写 ❌                            │
└─────────────────────────────────────────────────────────────┘
           │
           ▼
┌─────────────────────────────────────────────────────────────┐
│                    员工A私有层 (L1)                          │
│                                                             │
│   员工A ──▶ 可见 ✅ ──▶ 可写 ✅                            │
│   员工B ──▶ 可见 ❌ ──▶ 可写 ❌                            │
│   门店B全员 ──▶ 可见 ❌ ──▶ 可写 ❌                        │
└─────────────────────────────────────────────────────────────┘

七、技术实现架构

7.1 系统拓扑

微信用户消息
    │
    ▼
┌──────────────────────────────────────────────────────────────┐
│                    Hermes Gateway(单实例)                    │
│  解析消息 → 提取 openid → 查询用户档案                       │
└────────────────────────┬───────────────────────────────────┘
                         │
                         ▼
┌──────────────────────────────────────────────────────────────┐
│                 Session Router                                │
│                                                              │
│  session_key = "wechat_{openid}"                           │
│  自动创建 / 自动 resume                                     │
│                                                              │
│  → AIAgent 处理(单一实例,session 隔离)                   │
└────────────────────────┬───────────────────────────────────┘
                         │
                         ▼
┌──────────────────────────────────────────────────────────────┐
│                 上下文注入层(记忆加载)                       │
│                                                              │
│  1. 查 user_store_mapping 获取 (store_id, employee_id)       │
│  2. 加载 L1 私有记忆(employee_id)                        │
│  3. 加载 L2 门店共享记忆(store_id)                       │
│  4. 加载 L3 系统全局记忆                                    │
│                                                              │
│  注入顺序:L1 → L2 → L3                                     │
└────────────────────────┬───────────────────────────────────┘
                         │
                         ▼
┌──────────────────────────────────────────────────────────────┐
│                    AIAgent                                   │
│  基于注入的上下文进行处理                                   │
│  回复中不会泄露其他门店/员工的私密信息                       │
└──────────────────────────────────────────────────────────────┘

7.2 核心代码示例

# AIAgent 启动时加载用户上下文
def load_user_context(openid: str) -> dict:
    """根据 openid 加载完整上下文"""

    # 1. 查用户属于哪个门店
    user = db.query_one(
        "SELECT * FROM user_store_mapping WHERE openid = ?",
        openid
    )
    if not user:
        raise ValueError(f"未知用户: {openid}")

    # 2. 加载三层记忆
    private_memory = db.query(
        "SELECT memory_key, memory_value FROM memory_employee "
        "WHERE employee_id = ?",
        user.employee_id
    )

    store_memory = db.query(
        "SELECT memory_key, memory_value, updated_by FROM memory_store "
        "WHERE store_id = ?",
        user.store_id
    )

    global_memory = db.query(
        "SELECT memory_key, memory_value FROM memory_global"
    )

    # 3. 加载技能
    skills = load_employee_skills(user.employee_id, user.store_id)

    # 4. 构建上下文
    return {
        "employee_id": user.employee_id,
        "employee_name": user.employee_name,
        "store_id": user.store_id,
        "role": user.role,
        "private_memory": private_memory,   # L1
        "store_memory": store_memory,       # L2
        "global_memory": global_memory,     # L3
        "skills": skills,
    }


# 系统提示词组装
def build_system_prompt(ctx: dict) -> str:
    """组装带记忆层级的系统提示词"""

    prompt_parts = []

    # 1. 基础身份
    prompt_parts.append(f"""
你是「{ctx['store_id']}门店」的「{ctx['employee_name']}」({ctx['role']}角色)。
当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%s')}
""")

    # 2. L3 系统全局(最先注入,最低优先级)
    if ctx['global_memory']:
        prompt_parts.append("【系统全局知识】")
        for row in ctx['global_memory']:
            prompt_parts.append(f"  {row['memory_key']}: {row['memory_value']}")

    # 3. L2 门店共享
    if ctx['store_memory']:
        prompt_parts.append("【本店共享资料】(全员可见,可更新)")
        for row in ctx['store_memory']:
            prompt_parts.append(f"  {row['memory_key']}: {row['memory_value']} [更新人:{row['updated_by']}]")

    # 4. L1 员工私有(最后注入,最高优先级)
    if ctx['private_memory']:
        prompt_parts.append("【你的私人笔记】(仅你可见)")
        for row in ctx['private_memory']:
            prompt_parts.append(f"  {row['memory_key']}: {row['memory_value']}")

    return "\n".join(prompt_parts)

八、跨门店协作(可选扩展)

8.1 临时借调场景

员工临时借调到其他门店时,授予临时访问权限:

-- 临时跨店权限表
CREATE TABLE cross_store_access (
    id            BIGINT AUTO_INCREMENT PRIMARY KEY,
    employee_id   VARCHAR(32) NOT NULL,
    store_id      VARCHAR(32) NOT NULL,    -- 被借调到的门店
    access_level  ENUM('read','read_write') DEFAULT 'read',
    valid_from    DATE NOT NULL,
    valid_to      DATE NOT NULL,
    reason        VARCHAR(256),
    approved_by   VARCHAR(32),
    created_at    DATETIME DEFAULT NOW(),

    INDEX idx_employee (employee_id),
    INDEX idx_store (store_id),
    INDEX idx_valid (valid_from, valid_to)
);
def has_cross_store_access(employee_id: str, store_id: str) -> bool:
    """检查员工是否有跨店访问权限"""
    result = db.query_one("""
        SELECT access_level FROM cross_store_access
        WHERE employee_id = ?
          AND store_id = ?
          AND valid_from <= CURDATE()
          AND valid_to >= CURDATE()
    """, employee_id, store_id)
    return result is not None

九、部署架构

9.1 单实例多租户部署

┌─────────────────────────────────────────────────────────────┐
│                    Hermes Gateway(单实例)                    │
│                                                             │
│  一个 Gateway 进程,服务所有门店                             │
│  通过 session 隔离 + 记忆层级注入实现逻辑多租户              │
│                                                             │
│  ┌─ memory_employee (L1) ─── per employee                  │
│  ├─ memory_store (L2) ────── per store                    │
│  └─ memory_global (L3) ─────── global                     │
└────────────────────────┬──────────────────────────────────┘
                         │
                         ▼
              ┌──────────────────────┐
              │   MySQL(主数据库)   │
              │                       │
              │  user_store_mapping   │
              │  memory_employee (L1) │
              │  memory_store (L2)    │
              │  memory_global (L3)  │
              │  skills              │
              │  cross_store_access  │
              └──────────────────────┘

9.2 优势

优势 说明
资源节省 一个 Gateway 实例,服务所有门店
运维简单 无需为每个门店部署独立服务
线性扩展 新增门店无需重启服务,配置即可
逻辑隔离 session 隔离 + 记忆层级,安全可靠

十、与现有架构结合

10.1 集成你的 MySQL + ClickHouse 架构

当前你已有 MySQL 主库和 ClickHouse 分析库,这套多租户架构可以复用:

┌──────────────────────────────────────────────────────────────┐
│                      MySQL 主库                               │
│                                                              │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │ 业务表           │  │ 多租户表        │                   │
│  │ (orders等)      │  │ (本架构新增)    │                   │
│  └─────────────────┘  └─────────────────┘                   │
└────────────────────────┬───────────────────────────────────┘
                         │ DTS CDC
                         ▼
┌──────────────────────────────────────────────────────────────┐
│                    ClickHouse(分析库)                       │
│                                                              │
│  - 订单统计(按门店隔离查询)                                │
│  - 库存预测                                                  │
│  - 财务月报                                                  │
│  - 供应商/客户分析                                           │
│                                                              │
│  WHERE store_id = 'STORE_A'  -- 自然隔离                     │
└──────────────────────────────────────────────────────────────┘

10.2 实施步骤

阶段 时间 任务
Phase 1 第1周 数据库表创建(user_store_mapping, memory_*, skills等)
Phase 2 第2周 Hermes Gateway session 路由改造
Phase 3 第3周 记忆加载层开发(L1/L2/L3注入逻辑)
Phase 4 第4周 技能加载层开发
Phase 5 第5周 跨店协作(可选)开发
Phase 6 第6周 测试与调优

文档版本:v1.0
更新日期:2026-04-30
架构:Hermes Gateway 单实例 + MySQL 多租户记忆层

Left-click: follow link, Right-click: select node, Scroll: zoom
x