Skip to content

Commit 1e1687f

Browse files
authored
Merge pull request #15 from liut/feat/w582-channels
feat: add platform adapter layer for WeCom and Feishu integration
2 parents a2ace64 + ce6804c commit 1e1687f

26 files changed

Lines changed: 3619 additions & 58 deletions

data/preset.example.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,46 @@ tools:
2323
kb_search: "在知识库中搜索相关内容。当遇到未知或不确定的问题时,优先查阅知识库。"
2424
kb_create: "创建新的知识库文档,所有参数必填。注意:除非用户明确要求补充内容,否则不要调用。"
2525
fetch: "从互联网获取 URL 内容并可选地提取为 markdown 格式"
26+
27+
# 平台适配器配置(支持多实例)
28+
channels:
29+
# WeCom WebSocket 长连接模式
30+
wecom:
31+
enable: true
32+
mode: websocket
33+
config:
34+
bot_id: "YOUR_BOT_ID"
35+
bot_secret: "YOUR_BOT_SECRET"
36+
allow_from: "" # 可选,限制来源 UserID
37+
38+
# WeCom Webhook 回调模式
39+
# wecom:
40+
# enable: true
41+
# mode: webhook
42+
# config:
43+
# corp_id: "YOUR_CORP_ID"
44+
# corp_secret: "YOUR_CORP_SECRET"
45+
# agent_id: "YOUR_AGENT_ID"
46+
# callback_token: "YOUR_CALLBACK_TOKEN"
47+
# callback_aes_key: "YOUR_CALLBACK_AES_KEY"
48+
# callback_path: "/wecom/callback"
49+
50+
# 飞书 WebSocket 长连接模式
51+
# feishu:
52+
# enable: true
53+
# mode: websocket
54+
# config:
55+
# app_id: "YOUR_APP_ID"
56+
# app_secret: "YOUR_APP_SECRET"
57+
# allow_from: "" # 可选,限制来源 UserID
58+
59+
# 飞书 Webhook 回调模式
60+
# feishu:
61+
# enable: true
62+
# mode: webhook
63+
# config:
64+
# app_id: "YOUR_APP_ID"
65+
# app_secret: "YOUR_APP_SECRET"
66+
# encrypt_key: "YOUR_ENCRYPT_KEY" # 可选,加密密钥
67+
# callback_path: "/feishu/callback"
68+
# allow_from: "" # 可选,限制来源 UserID
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
---
2+
title: Add Platform Adapter Layer for Chat Platform Integration
3+
type: feat
4+
status: completed
5+
date: 2026-03-25
6+
---
7+
8+
# Add Platform Adapter Layer for Chat Platform Integration
9+
10+
## Overview
11+
12+
在 morrigan 中引入平台适配器层,使 AI 对话能力可以对接微信企业版(WeCom)、飞书等聊天平台。以 WeCom 为突破点,验证架构设计后扩展至其他平台。
13+
14+
## Problem Statement
15+
16+
当前 morrigan 只支持 HTTP API 方式的对话接入(通过前端)。需要支持将 AI 对话能力以 Bot 形式接入到企业常用的聊天平台(WeCom、飞书等),让用户可以在这些平台中直接与 AI 对话。
17+
18+
## Proposed Solution
19+
20+
### 架构设计
21+
22+
```
23+
┌─────────────────────────────────────────────────────────────┐
24+
│ Chat Platforms │
25+
│ WeCom │ 飞书 │ 钉钉 │ Telegram ... │
26+
└──────┬──────┴─────┬─────┴─────┬────┴────────┬──────────────┘
27+
│ │ │ │
28+
▼ ▼ ▼ ▼
29+
┌─────────────────────────────────────────────────────────────┐
30+
│ Channel Adapter Layer (pkg/channels/) │
31+
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
32+
│ │ wecom │ │ feishu │ │dingtalk │ │ ... │ (可扩展) │
33+
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
34+
│ │ │ │ │ │
35+
│ └────────────┴────────────┴────────────┘ │
36+
│ │ │
37+
│ ┌──────────┴──────────┐ │
38+
│ │ Platform Bridge │ (统一消息格式) │
39+
│ │ (pkg/channels/) │ │
40+
│ └──────────┬──────────┘ │
41+
└─────────────────────────┼───────────────────────────────────┘
42+
43+
44+
┌─────────────────────────────────────────────────────────────┐
45+
│ Morrigan Core (Existing) │
46+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
47+
│ │ handle_convo│ │ LLM │ │ tools/registry │ │
48+
│ │ (chat) │◄─┤ Client │◄─┤ (MCP tools) │ │
49+
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
50+
└─────────────────────────────────────────────────────────────┘
51+
```
52+
53+
54+
## Technical Considerations
55+
56+
### 核心接口设计
57+
58+
参考 `cc-connect/core/interfaces.go`,定义以下接口:
59+
60+
```go
61+
// pkg/models/channel/channel.go
62+
63+
// Channel 平台适配器必须实现的接口
64+
type Channel interface {
65+
Name() string // 平台名称: "wecom", "feishu"
66+
Start(handler MessageHandler) error // 启动平台连接
67+
Reply(ctx context.Context, replyCtx any, content string) error // 回复消息
68+
Send(ctx context.Context, replyCtx any, content string) error // 发送消息
69+
Stop() error // 停止平台连接
70+
}
71+
72+
// MessageHandler 消息处理函数类型
73+
type MessageHandler func(p Channel, msg *Message)
74+
75+
// ReplyContextReconstructor 可选接口:支持从 sessionKey 重建回复上下文
76+
type ReplyContextReconstructor interface {
77+
ReconstructReplyCtx(sessionKey string) (any, error)
78+
}
79+
80+
// ImageSender 可选接口:支持发送图片
81+
type ImageSender interface {
82+
SendImage(ctx context.Context, replyCtx any, img ImageAttachment) error
83+
}
84+
85+
// 统一消息结构
86+
type Message struct {
87+
Channel string // 平台名称
88+
SessionKey string // 唯一标识: "{platform}:{chatID}:{userID}"
89+
MessageID string // 平台原始消息ID (用于去重)
90+
UserID string
91+
UserName string
92+
ChatName string
93+
Content string // 消息内容
94+
Images []ImageAttachment
95+
Files []FileAttachment
96+
Audio *AudioAttachment
97+
ReplyCtx any // 平台特定回复上下文
98+
FromVoice bool // 语音转文字
99+
}
100+
```
101+
102+
### 平台注册机制
103+
104+
```go
105+
// pkg/channels/registry.go
106+
107+
type Registry struct {
108+
channels map[string]Factory
109+
}
110+
111+
type PlatformFactory func(opts map[string]any) (Channel, error)
112+
113+
// 全局注册表
114+
var registry *PlatformRegistry
115+
116+
func RegisterPlatform(name string, factory PlatformFactory)
117+
func NewPlatform(name string, opts map[string]any) (Platform, error)
118+
```
119+
120+
每个平台在 `init()` 中注册:
121+
122+
```go
123+
// pkg/channels/wecom/wecom.go
124+
func init() {
125+
channels.RegisterPlatform("wecom", New)
126+
}
127+
```
128+
129+
### WeCom 适配器设计
130+
131+
**HTTP Webhook 模式:**
132+
- 接收 GET 请求验证回调 URL
133+
- 接收 POST 请求处理加密消息(XML + AES-256-CBC)
134+
- 消息类型:文本、图片、语音
135+
- 回复:POST XML 消息
136+
- Access token 缓存(提前 60 秒刷新)
137+
138+
**WebSocket 长连接模式:**
139+
- 独立进程运行,通过 bridge 与主进程通信
140+
- 支持重连(指数退避:1s -> 30s 最大)
141+
- 心跳保活(30s ping/pong)
142+
- 流式响应支持
143+
144+
### 与 Morrigan Core 的集成
145+
146+
消息流程:
147+
148+
1. WeCom 适配器接收消息
149+
2. 解析并构建统一 `Message` 结构
150+
3. 检查去重、过滤老消息、白名单
151+
4. 构建 sessionKey: `wecom:{chatID}:{userID}`
152+
5. 调用 `MessageHandler` → 转发给现有 `handle_convo.go` 的对话处理逻辑
153+
6. AI 回复通过适配器的 `Reply()` 方法发送回平台
154+
155+
**配置扩展:**
156+
157+
在现有 `settings` 中添加平台配置:
158+
159+
```go
160+
// pkg/settings/settings.go
161+
type PlatformConfig struct {
162+
Enable bool
163+
Type string // "wecom", "feishu"
164+
Config map[string]any
165+
}
166+
```
167+
168+
### 关键实现细节
169+
170+
1. **去重机制**:使用 Redis 缓存 MsgId,60 秒 TTL
171+
2. **Token 缓存**:Access Token 缓存,提前刷新
172+
3. **消息分片**:大消息按 UTF-8 拆分(WeCom 限制 2000 字符)
173+
4. **异步处理**`go handler(p, msg)` 非阻塞处理
174+
5. **优雅关闭**:Context 取消和连接 draining
175+
176+
## System-Wide Impact
177+
178+
### Interaction Graph
179+
180+
```
181+
WeCom Message Received
182+
183+
[wecom.go: message handler]
184+
185+
Parse XML + AES decrypt
186+
187+
[bridge.go: dispatch]
188+
189+
Check dedup (Redis)
190+
191+
Check allowlist
192+
193+
[handle_convo.go: postChat]
194+
195+
[llm.Client: Chat]
196+
197+
AI Response
198+
199+
[wecom.go: Reply]
200+
201+
POST XML to WeCom API
202+
```
203+
204+
### 错误处理
205+
206+
- 平台 API 调用失败:重试 + 告警
207+
- LLM 调用失败:返回友好错误消息
208+
- 消息处理超时:平台一般有超时限制,考虑异步回复
209+
210+
## Acceptance Criteria
211+
212+
- [ ] `pkg/channels/` 目录创建完成,核心接口定义完成
213+
- [ ] WeCom HTTP Webhook 适配器可接收消息并回复
214+
- [ ] 消息正确路由到现有 `handle_convo.go` 对话处理
215+
- [ ] 集成测试通过(WeCom 模拟消息)
216+
- [ ] 配置可通过 `settings` 管理
217+
- [ ] 文档说明如何添加新平台
218+
219+
## Dependencies & Risks
220+
221+
**依赖:**
222+
- 现有 Redis 存储(去重、Token 缓存)
223+
- 现有 LLM 客户端
224+
- 现有对话处理逻辑
225+
226+
**风险:**
227+
- WeCom API 变更需同步更新
228+
- 消息加密/签名验证需严格实现
229+
- 平台限流需处理
230+
231+
## Implementation Phases
232+
233+
### Phase 1: Core Platform Layer (基础设施)
234+
235+
- 创建 `pkg/channels/` 目录结构
236+
- 实现 `channel.go` 核心接口
237+
- 实现 `registry.go` 平台注册表
238+
- 实现 `message.go` 统一消息结构
239+
- 实现 `dedup.go` 去重机制
240+
241+
### Phase 2: WeCom HTTP Adapter (WeCom HTTP 适配器)
242+
243+
- 实现 `pkg/channels/wecom/wecom.go`
244+
- 实现消息解析(AES 解密)
245+
- 实现回复发送
246+
- 实现 Access Token 管理
247+
248+
### Phase 3: Integration (集成)
249+
250+
- 创建 `pkg/web/api/handle_platform.go`
251+
- 集成到路由系统
252+
- 对接现有 `handle_convo.go`
253+
- 配置管理
254+
255+
### Phase 4: Testing & Polish (测试完善)
256+
257+
- 单元测试
258+
- 集成测试
259+
- 飞书适配器(扩展)

0 commit comments

Comments
 (0)