统一通道框架:飞书、钉钉、企微、QQ、微信小程序的接入与管理
企业用户在不同场景使用不同的通讯工具:
多通道架构让同一个AI Agent可以通过不同入口为用户服务,而不需要为每个平台单独开发。
多通道统一架构
══════════════
┌────────┐ ┌────────┐ ┌───────┐ ┌──────┐ ┌──────┐ ┌─────┐
│ 飞书 │ │ 钉钉 │ │ 企微 │ │ QQ │ │ 小程序│ │ Web │
│ Bot │ │ Bot │ │ Bot │ │频道 │ │ │ │ │
└───┬────┘ └───┬────┘ └───┬───┘ └───┬──┘ └───┬──┘ └──┬──┘
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ ChannelServiceSupport (共享逻辑) │
│ │
│ · 用户管理 (查找/创建/绑定) │
│ · 会话管理 (查找/创建/关联Agent) │
│ · 安全扫描 (输入扫描 + 输出扫描) │
│ · 消息持久化 (保存到数据库) │
│ · 入站去重 (防止重复处理) │
│ · 流式收集 (收集SSE流为完整文本) │
│ · 历史加载 (按Token预算截断历史消息) │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Agent编排层 (hub-agent-core) │
│ AgentTaskOrchestrator.execute() │
└──────────────────────────────────────────────────────────┘
| 组件 | 类名 | 职责 |
|---|---|---|
| 通道服务基类 | ChannelServiceSupport | 抽取所有通道共享的业务逻辑 |
| Bot注册中心 | AbstractBotRegistry<C> | 模板方法管理Bot生命周期(启动/停止/配置) |
| 消息格式化 | MessageFormatHelper | 统一消息格式转换 |
| 入站去重 | RedisInboundDedupeStore | Redis消息去重(多实例部署) |
| 通道会话 | RedisChannelSessionStore | 会话与Agent绑定(跨实例共享) |
飞书通道的亮点是 WebSocket 事件接入 + Agent 核心链路复用 + 卡片化结果展示:
飞书卡片化回复:
用户发消息 → Lark WebSocket Client
│
▼
FeishuBotRegistry 分发事件
│
▼
FeishuChannelService
│
├── 先回复 "正在思考..."
│
▼
调用 Agent 编排器 → 获取 SSE 流
│
▼
ChannelServiceSupport.streamCollect()
│
▼
输出安全扫描 → 结果卡片 / 长回答分页 / 来源信息 / 推荐追问
技术要点:
· Lark WebSocket 长连接接收消息、菜单、P2P、卡片回调事件
· ChannelServiceSupport 复用用户、会话、历史、安全扫描和 Agent 调用
· 结果以飞书卡片呈现,支持长回答分页和推荐追问
· 支持交互按钮: 重新生成/切换Agent/帮助
当前边界:
· 主链路不是逐 Token 更新卡片
· 不是每 2 秒 PATCH 同一张卡片
每个通道的消息处理安全流程:
入站消息
│
├── 入站去重检查 (Redis SETNX)
│ 重复消息 → 丢弃
│
├── 输入安全扫描 (InputScanner)
│ 危险 → 回复安全警告
│
├── Agent执行
│
├── 输出安全扫描 (OutputScanner)
│ 泄露 → 消毒后发送
│
└── 消息持久化 (数据库记录)
AbstractBotRegistry<C> 模板方法:
┌─────────────────────────────────────┐
│ 通用逻辑 (父类) │
│ │
│ · 从数据库加载Bot配置 │
│ · 校验配置完整性 │
│ · 运行状态管理 │
│ · saveAndRestart() 原子更新 │
│ · 统一状态上报 │
└──────────────┬──────────────────────┘
│ 继承
┌──────────────▼──────────────────────┐
│ 平台特定 (子类实现) │
│ │
│ FeishuBotRegistry: │
│ · startBot(): 注册飞书事件回调 │
│ · stopBot(): 取消回调注册 │
│ · deserialize(): 解析飞书App配置 │
│ │
│ DingTalkBotRegistry: │
│ · startBot(): 注册钉钉Stream │
│ · stopBot(): 关闭Stream连接 │
└─────────────────────────────────────┘
FeishuBotRegistry 启动 Lark WebSocket Client 接收消息事件,FeishuChannelService 先回复“正在思考”,再通过 ChannelServiceSupport.streamCollect() 收集 Agent SSE 输出,完成后发送结果卡片、来源信息和操作按钮。面试时重点讲 WebSocket 事件接入、通道复用和卡片交互。
hub-agent-channels 模块下的 feishu 子包集成了飞书机器人,支持 WebSocket 长连接、交互式卡片、菜单事件、P2P 欢迎等核心能力。项目启动后可按配置拉起连接,无需额外部署独立服务。
FeishuChannelService 消息处理流程:
飞书 WebSocket 事件
│
▼
FeishuBotRegistry(Lark WS Client)
│
▼
FeishuChannelService
├─ 消息事件 → Agent执行 → 思考态文本 + 结果卡片
├─ P2P进入聊天 → 欢迎卡片
├─ 卡片动作回调 → 重新生成 / 切换Agent / 帮助
├─ 机器人菜单 → 菜单命令处理
└─ 追问建议 → 推荐气泡展示
| 组件 | 职责 |
|---|---|
FeishuChannelService | 通道桥接核心 — 连接飞书SDK事件与Agent系统 |
FeishuBotRegistry | 多机器人支持 — 管理多个飞书应用(appId),独立 Lark Client 实例 |
FeishuCardBuilder | 卡片构建器 — Markdown内容 + 操作按钮 + 追问标签 |
| 功能 | 说明 |
|---|---|
/agentId 消息 | 切换到指定Agent(如 /rag 你好) |
| 重新生成 | 卡片按钮,重新执行Agent生成回答 |
| 列出Agent | 卡片按钮,显示所有可用Agent列表 |
| 追问建议 | 回复完成后展示推荐追问气泡 |
| Per-chat状态 | 每个聊天独立的Agent状态追踪 |
| 特性 | Web通道 | 飞书通道 |
|---|---|---|
| 传输方式 | HTTP SSE | WebSocket事件 + 卡片API |
| 实时性 | 毫秒级流式 | 思考态提示 + 完成后卡片 |
| 交互方式 | 输入框 + 工具栏 | 卡片按钮 + 指令 |
| Agent切换 | 下拉选择 | /agentId 指令 |
| 连接方式 | HTTP/SSE | WebSocket长连接 |
ChannelServiceSupport(约800行),飞书通道只需实现平台特定的消息收发和卡片构建(约200行)。用户管理、会话管理、安全扫描、消息持久化等全部走公共逻辑。钉钉、QQ 等入站机器人通道也复用这套模式;企业微信当前更偏出站 Webhook 通知,面试时不要讲成完整双向 Bot。
FeishuBotRegistry 继承自 AbstractBotRegistry<C> 模板方法基类,管理多个飞书应用(appId),每个应用独立的 Lark Client 实例。支持 saveAndRestart() 原子更新,运行时新增/停止机器人不需要重启应用。
hub-miniprogram 基于 uni-app + Vue 3 构建,提供完整的微信小程序端 AI 对话入口。支持微信 OAuth 一键登录、SSE 流式对话、多 Agent 切换、任务看板等核心功能,与 Web 端共享同一套后端 API。
微信小程序架构:
┌──────────────────────────────────────────────┐
│ hub-miniprogram (uni-app + Vue 3 + Pinia) │
│ │
│ 主包 分包 │
│ ├── 首页 (Agent卡片列表) ├── 运维中心 │
│ ├── 对话 (SSE流式) │ ├── 数据统计 │
│ ├── 任务 (看板) │ ├── 系统监控 │
│ ├── 我的 (个人中心) │ └── 安全日志 │
│ ├── 登录/注册 └── 管理后台 │
│ └── 工具页面 (10+) ├── 密钥管理 │
│ └── 渠道管理 │
└──────────────────┬───────────────────────────┘
│
▼ HTTP + SSE (enableChunked)
┌──────────────────────────────────────────────┐
│ hub-api (Spring Boot) │
│ │
│ AuthController → 登录/注册/微信登录 │
│ WxMiniAppController → 小程序配置 CRUD │
│ ChatController → 对话(共用) │
│ WeChatAuthService → code2Session │
└──────────────────────────────────────────────┘
微信登录流程:
用户点击"微信登录"
│
▼
wx.login() → 获取临时 code
│
▼
POST /api/auth/wx-login { appId, code }
│
▼
WeChatAuthService.code2Session(appId, code)
│ → 查询 WxMiniAppEntity 获取 appSecret
│ → 调用微信 API: /sns/jscode2session
│
▼
微信返回 { openid, session_key, unionid }
│
├── 已绑定用户 → 直接签发 JWT
│
└── 未绑定 → 自动创建用户 (wx_{openid前12位})
→ 创建 OAuth 关联
→ 角色: wx-user, 自动审批
│
▼
返回 JWT Token → 小程序存储到 uni.setStorageSync
| 组件 | 类 | 职责 |
|---|---|---|
| 小程序配置管理 | WxMiniAppController | 小程序 CRUD(/api/wechat/mini-apps)+ 用户管理 |
| 微信认证服务 | WeChatAuthService | 调用微信 code2Session API 换取 openid |
| 微信配置 | WeChatConfig | hub.wechat.mini-app-enabled 配置项 |
| 小程序实体 | WxMiniAppEntity | ah_wx_mini_app 表(支持软删除) |
| 认证入口 | AuthController | wx-login / wx-bind / setup-web-account |
uni.request({ enableChunked: true }) 接收 SSE 流,通过 requestTask.onChunkReceived 实时解析事件流(content/sources/tool_call/plan 等),实现与 Web 端一致的流式对话体验。
| 特性 | Web 前端 | 微信小程序 |
|---|---|---|
| 框架 | Vue 3 + Vite | uni-app + Vue 3 |
| SSE 方式 | EventSource / fetch | enableChunked + onChunkReceived |
| 认证方式 | 账号密码 | wx.login + 账号密码 |
| API 层 | axios | uni.request 封装 |
| Agent/对话 | 共享同一套后端 API 和数据 | |
| 权限模型 | 共享 RBAC 权限体系 | |
setup-web-account 设置用户名密码后,可以用同一账号登录 Web 端。对话记录、Agent 列表、权限配置全部共享,存储在同一数据库中。微信用户的 source 标记为 wechat(纯微信)或 wechat-linked(已设置 Web 账号)。
EventSource,改用 uni.request({ enableChunked: true }),通过 onChunkReceived 回调接收 ArrayBuffer,用 TextDecoder 解码后按 \n\n 分割解析 SSE 事件。支持所有事件类型(content/sources/tool_call/plan 等),体验与 Web 端一致。