@huo15/dingtalk-connector-pro 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +485 -0
- package/LICENSE +21 -0
- package/README.en.md +479 -0
- package/README.md +209 -0
- package/docs/AGENT_ROUTING.md +335 -0
- package/docs/DEAP_AGENT_GUIDE.en.md +115 -0
- package/docs/DEAP_AGENT_GUIDE.md +115 -0
- package/docs/images/dingtalk.svg +1 -0
- package/docs/images/image-1.png +0 -0
- package/docs/images/image-2.png +0 -0
- package/docs/images/image-3.png +0 -0
- package/docs/images/image-4.png +0 -0
- package/docs/images/image-5.png +0 -0
- package/docs/images/image-6.png +0 -0
- package/docs/images/image-7.png +0 -0
- package/index.ts +28 -0
- package/install-beta.sh +438 -0
- package/install-npm.sh +167 -0
- package/openclaw.plugin.json +498 -0
- package/package.json +80 -0
- package/src/channel.ts +463 -0
- package/src/config/accounts.ts +242 -0
- package/src/config/schema.ts +148 -0
- package/src/core/connection.ts +722 -0
- package/src/core/message-handler.ts +1700 -0
- package/src/core/provider.ts +111 -0
- package/src/core/state.ts +54 -0
- package/src/directory.ts +95 -0
- package/src/docs.ts +293 -0
- package/src/gateway-methods.ts +404 -0
- package/src/onboarding.ts +413 -0
- package/src/policy.ts +32 -0
- package/src/probe.ts +212 -0
- package/src/reply-dispatcher.ts +630 -0
- package/src/runtime.ts +32 -0
- package/src/sdk/helpers.ts +322 -0
- package/src/sdk/types.ts +513 -0
- package/src/secret-input.ts +19 -0
- package/src/services/media/audio.ts +54 -0
- package/src/services/media/chunk-upload.ts +296 -0
- package/src/services/media/common.ts +155 -0
- package/src/services/media/file.ts +70 -0
- package/src/services/media/image.ts +81 -0
- package/src/services/media/index.ts +10 -0
- package/src/services/media/video.ts +162 -0
- package/src/services/media.ts +1136 -0
- package/src/services/messaging/card.ts +342 -0
- package/src/services/messaging/index.ts +17 -0
- package/src/services/messaging/send.ts +141 -0
- package/src/services/messaging.ts +1013 -0
- package/src/targets.ts +45 -0
- package/src/types/index.ts +59 -0
- package/src/utils/agent.ts +63 -0
- package/src/utils/async.ts +51 -0
- package/src/utils/constants.ts +27 -0
- package/src/utils/http-client.ts +37 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +78 -0
- package/src/utils/session.ts +147 -0
- package/src/utils/token.ts +93 -0
- package/src/utils/utils-legacy.ts +454 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# Agent 路由与 SessionKey 规范
|
|
2
|
+
|
|
3
|
+
本文档是 DingTalk OpenClaw Connector 中 **Agent 路由(bindings)** 和 **SessionKey 构建** 的完整开发规范,供新功能开发和代码审查参考。
|
|
4
|
+
|
|
5
|
+
涉及文件:
|
|
6
|
+
- `src/utils/session.ts` — `buildSessionContext()` 函数,构建会话上下文
|
|
7
|
+
- `src/core/message-handler.ts` — bindings 路由匹配、sessionKey 构建、消息队列管理
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 一、SessionContext 字段总览
|
|
12
|
+
|
|
13
|
+
`buildSessionContext()` 返回的 `SessionContext` 包含以下字段:
|
|
14
|
+
|
|
15
|
+
| 字段 | 类型 | 说明 |
|
|
16
|
+
|------|------|------|
|
|
17
|
+
| `channel` | `'dingtalk-connector'` | 固定值,标识频道来源 |
|
|
18
|
+
| `accountId` | `string` | 当前钉钉账号 ID |
|
|
19
|
+
| `chatType` | `'direct' \| 'group'` | 会话类型,单聊或群聊 |
|
|
20
|
+
| `peerId` | `string` | **路由匹配专用**,真实 peer 标识(群聊为 `conversationId`,单聊为 `senderId`),**不受任何会话隔离配置影响** |
|
|
21
|
+
| `sessionPeerId` | `string` | **session/memory 隔离键**,用于构建 `sessionKey` 和消息队列 key,**受会话隔离配置影响,可能与 `peerId` 不同** |
|
|
22
|
+
| `conversationId` | `string?` | 群聊会话 ID,单聊时为 `undefined` |
|
|
23
|
+
| `senderName` | `string?` | 发送者昵称 |
|
|
24
|
+
| `groupSubject` | `string?` | 群名称,单聊时为 `undefined` |
|
|
25
|
+
|
|
26
|
+
其中 `channel`、`accountId`、`chatType`、`conversationId`、`senderName`、`groupSubject` 都是直接从消息原始字段透传的,没有歧义。
|
|
27
|
+
|
|
28
|
+
**需要特别注意的是 `peerId` 和 `sessionPeerId` 这两个字段**,它们都是 peer 标识,但职责完全不同,**不能混用**:
|
|
29
|
+
|
|
30
|
+
- **路由匹配(去哪个 Agent)** → 使用 `peerId`
|
|
31
|
+
- **session 隔离(共享多大范围的上下文)** → 使用 `sessionPeerId`
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 二、buildSessionContext() 完整逻辑
|
|
36
|
+
|
|
37
|
+
`buildSessionContext()` 在 `src/utils/session.ts` 中定义,每条消息到达时调用一次,根据消息原始字段和账号配置,决定 `peerId` 和 `sessionPeerId` 的值。
|
|
38
|
+
|
|
39
|
+
### 2.1 输入参数
|
|
40
|
+
|
|
41
|
+
| 参数 | 来源 | 说明 |
|
|
42
|
+
|------|------|------|
|
|
43
|
+
| `accountId` | 账号配置 key | 当前钉钉账号标识 |
|
|
44
|
+
| `senderId` | 消息原始字段 `data.senderStaffId` | 发送者 userId |
|
|
45
|
+
| `senderName` | 消息原始字段 `data.senderNick` | 发送者昵称 |
|
|
46
|
+
| `conversationType` | 消息原始字段 `data.conversationType` | `"1"` = 单聊,其他 = 群聊 |
|
|
47
|
+
| `conversationId` | 消息原始字段 `data.conversationId` | 群聊的会话 ID |
|
|
48
|
+
| `groupSubject` | 消息原始字段 `data.conversationTitle` | 群名称 |
|
|
49
|
+
| `separateSessionByConversation` | `config.separateSessionByConversation` | 是否按会话区分 session |
|
|
50
|
+
| `groupSessionScope` | `config.groupSessionScope` | 群聊 session 粒度 |
|
|
51
|
+
| `sharedMemoryAcrossConversations` | `config.sharedMemoryAcrossConversations` | 是否跨会话共享记忆 |
|
|
52
|
+
|
|
53
|
+
### 2.2 peerId 的计算规则(固定,不受配置影响)
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
peerId = isDirect ? senderId : (conversationId || senderId)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- 单聊:`peerId = senderId`
|
|
60
|
+
- 群聊:`peerId = conversationId`(无 conversationId 时降级为 senderId)
|
|
61
|
+
|
|
62
|
+
### 2.3 sessionPeerId 的决策树
|
|
63
|
+
|
|
64
|
+
配置优先级从高到低,**命中第一条即返回**:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
sharedMemoryAcrossConversations === true
|
|
68
|
+
→ sessionPeerId = accountId
|
|
69
|
+
(所有单聊+群聊共享同一记忆,以 accountId 为 session 键)
|
|
70
|
+
|
|
71
|
+
separateSessionByConversation === false
|
|
72
|
+
→ sessionPeerId = senderId
|
|
73
|
+
(按用户维度隔离,不区分群/单聊,同一用户在不同群共享 session)
|
|
74
|
+
|
|
75
|
+
isDirect(单聊)
|
|
76
|
+
→ sessionPeerId = senderId
|
|
77
|
+
(每个用户独立 session)
|
|
78
|
+
|
|
79
|
+
groupSessionScope === 'group_sender'(群聊)
|
|
80
|
+
→ sessionPeerId = `${conversationId}:${senderId}`
|
|
81
|
+
(群内每个用户独立 session)
|
|
82
|
+
|
|
83
|
+
默认(群聊)
|
|
84
|
+
→ sessionPeerId = conversationId || senderId
|
|
85
|
+
(整个群共享一个 session)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 2.4 各配置组合下的完整取值表
|
|
89
|
+
|
|
90
|
+
| 配置 | 场景 | `peerId` | `sessionPeerId` |
|
|
91
|
+
|------|------|----------|-----------------|
|
|
92
|
+
| `sharedMemoryAcrossConversations: true` | 单聊 | `senderId` | `accountId` |
|
|
93
|
+
| `sharedMemoryAcrossConversations: true` | 群聊 | `conversationId` | `accountId` |
|
|
94
|
+
| `separateSessionByConversation: false` | 单聊 | `senderId` | `senderId` |
|
|
95
|
+
| `separateSessionByConversation: false` | 群聊 | `conversationId` | `senderId` |
|
|
96
|
+
| `groupSessionScope: "group_sender"` | 群聊 | `conversationId` | `${conversationId}:${senderId}` |
|
|
97
|
+
| 默认 | 单聊 | `senderId` | `senderId` |
|
|
98
|
+
| 默认 | 群聊 | `conversationId` | `conversationId` |
|
|
99
|
+
|
|
100
|
+
> **注意**:`sharedMemoryAcrossConversations: true` 优先级最高,会覆盖其他所有配置。
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 三、Agent 路由规则(Bindings)
|
|
105
|
+
|
|
106
|
+
### 3.1 路由流程
|
|
107
|
+
|
|
108
|
+
每条钉钉消息到达后,connector 按以下顺序确定目标 Agent:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
消息到达
|
|
112
|
+
↓
|
|
113
|
+
buildSessionContext() ← 构建会话上下文(含 peerId / sessionPeerId)
|
|
114
|
+
↓
|
|
115
|
+
遍历 cfg.bindings[] ← 按顺序逐条匹配,使用 peerId 进行匹配
|
|
116
|
+
↓ 命中第一条
|
|
117
|
+
matchedAgentId ← 使用该 agentId
|
|
118
|
+
↓ 全部未命中
|
|
119
|
+
cfg.defaultAgent || 'main' ← 回退到默认 Agent
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 3.2 Binding 匹配字段
|
|
123
|
+
|
|
124
|
+
每条 binding 的 `match` 字段支持以下维度,**所有指定的维度必须同时满足**(AND 关系):
|
|
125
|
+
|
|
126
|
+
| 字段 | 类型 | 说明 |
|
|
127
|
+
|------|------|------|
|
|
128
|
+
| `match.channel` | `string?` | 频道名,固定为 `"dingtalk-connector"`,省略则匹配所有频道 |
|
|
129
|
+
| `match.accountId` | `string?` | 钉钉账号 ID(对应 `accounts` 配置中的 key),省略则匹配所有账号 |
|
|
130
|
+
| `match.peer.kind` | `"direct" \| "group"?` | 会话类型,省略则匹配单聊和群聊 |
|
|
131
|
+
| `match.peer.id` | `string?` | Peer 标识,群聊为 `conversationId`,单聊为 `senderId`,`"*"` 表示通配所有 |
|
|
132
|
+
|
|
133
|
+
### 3.3 匹配逻辑(源码)
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// src/core/message-handler.ts
|
|
137
|
+
for (const binding of cfg.bindings) {
|
|
138
|
+
const match = binding.match;
|
|
139
|
+
if (match.channel && match.channel !== 'dingtalk-connector') continue;
|
|
140
|
+
if (match.accountId && match.accountId !== accountId) continue;
|
|
141
|
+
if (match.peer) {
|
|
142
|
+
if (match.peer.kind && match.peer.kind !== sessionContext.chatType) continue;
|
|
143
|
+
// 使用 peerId(真实 peer 标识),不受会话隔离配置影响
|
|
144
|
+
if (match.peer.id && match.peer.id !== '*' && match.peer.id !== sessionContext.peerId) continue;
|
|
145
|
+
}
|
|
146
|
+
matchedAgentId = binding.agentId;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
if (!matchedAgentId) {
|
|
150
|
+
matchedAgentId = cfg.defaultAgent || 'main';
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 3.4 优先级规则
|
|
155
|
+
|
|
156
|
+
- **顺序优先**:bindings 数组按顺序遍历,**第一条命中的规则生效**,后续规则不再检查
|
|
157
|
+
- **精确规则放前面**:将指定了 `peer.id` 的精确规则放在通配规则(`peer.id: "*"`)之前,避免通配规则提前拦截
|
|
158
|
+
|
|
159
|
+
### 3.5 典型配置示例
|
|
160
|
+
|
|
161
|
+
**多群分配不同 Agent**:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"bindings": [
|
|
166
|
+
{
|
|
167
|
+
"agentId": "main",
|
|
168
|
+
"match": {
|
|
169
|
+
"channel": "dingtalk-connector",
|
|
170
|
+
"accountId": "groupbot",
|
|
171
|
+
"peer": { "kind": "group", "id": "cid3RKewszsVbXZYCYmbybVNw==" }
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"agentId": "organizer",
|
|
176
|
+
"match": {
|
|
177
|
+
"channel": "dingtalk-connector",
|
|
178
|
+
"accountId": "groupbot",
|
|
179
|
+
"peer": { "kind": "group", "id": "cidqO7Ne7e+myoRu67AguW+HQ==" }
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"agentId": "atlas",
|
|
184
|
+
"match": {
|
|
185
|
+
"channel": "dingtalk-connector",
|
|
186
|
+
"accountId": "groupbot",
|
|
187
|
+
"peer": { "kind": "group", "id": "cidekzhmRmaKaJ6vnQezRFZWA==" }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**单聊走一个 Agent,群聊走另一个**:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"bindings": [
|
|
199
|
+
{
|
|
200
|
+
"agentId": "personal-assistant",
|
|
201
|
+
"match": { "channel": "dingtalk-connector", "peer": { "kind": "direct" } }
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"agentId": "group-bot",
|
|
205
|
+
"match": { "channel": "dingtalk-connector", "peer": { "kind": "group" } }
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**精确路由 + 通配兜底**:
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"bindings": [
|
|
216
|
+
{
|
|
217
|
+
"agentId": "vip-agent",
|
|
218
|
+
"match": { "channel": "dingtalk-connector", "peer": { "kind": "group", "id": "cidVIP..." } }
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"agentId": "main",
|
|
222
|
+
"match": { "channel": "dingtalk-connector", "peer": { "kind": "group", "id": "*" } }
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 四、SessionKey 构建规范
|
|
231
|
+
|
|
232
|
+
### 4.1 SessionKey 格式
|
|
233
|
+
|
|
234
|
+
SessionKey 由 SDK 的 `core.channel.routing.buildAgentSessionKey()` 生成,格式为:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
agent:{agentId}:{channel}:{peerKind}:{sessionPeerId}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
示例:
|
|
241
|
+
- `agent:main:dingtalk-connector:direct:manager7195` — 默认单聊,按用户隔离
|
|
242
|
+
- `agent:main:dingtalk-connector:group:cid3RKewszsVbXZYCYmbybVNw==` — 默认群聊,按群隔离
|
|
243
|
+
- `agent:main:dingtalk-connector:direct:bot1` — `sharedMemoryAcrossConversations=true`,所有会话共享(sessionPeerId = accountId = "bot1")
|
|
244
|
+
- `agent:main:dingtalk-connector:group:bot1` — 同上,群聊也共享
|
|
245
|
+
|
|
246
|
+
### 4.2 SessionKey 构建代码规范
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// src/core/message-handler.ts
|
|
250
|
+
const dmScope = cfg.session?.dmScope || 'per-channel-peer';
|
|
251
|
+
const sessionKey = core.channel.routing.buildAgentSessionKey({
|
|
252
|
+
agentId: matchedAgentId,
|
|
253
|
+
channel: 'dingtalk-connector', // ✅ 固定值,不能写 'dingtalk'
|
|
254
|
+
accountId: accountId,
|
|
255
|
+
peer: {
|
|
256
|
+
kind: sessionContext.chatType, // 'direct' | 'group'
|
|
257
|
+
id: sessionContext.sessionPeerId, // ✅ 使用 sessionPeerId,不是 peerId
|
|
258
|
+
},
|
|
259
|
+
dmScope: dmScope, // ✅ 必须传递,影响 sessionKey 格式
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**禁止**:
|
|
264
|
+
- 用 `sessionContext.peerId` 构建 sessionKey(`peerId` 是路由匹配专用)
|
|
265
|
+
- 手动拼接 sessionKey 字符串(必须通过 SDK 的 `buildAgentSessionKey` 方法)
|
|
266
|
+
- `channel` 写成 `'dingtalk'`(必须是 `'dingtalk-connector'`)
|
|
267
|
+
|
|
268
|
+
### 4.3 消息队列 Key 规范
|
|
269
|
+
|
|
270
|
+
消息队列(`sessionQueues`)用于确保同一会话+Agent 的消息串行处理,避免并发冲突。队列 key 格式:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
{sessionPeerId}:{agentId}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**必须与 sessionKey 使用相同的 `sessionPeerId`**,确保隔离策略一致:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// src/core/message-handler.ts
|
|
280
|
+
const baseSessionId = sessionContext.sessionPeerId;
|
|
281
|
+
const queueKey = `${baseSessionId}:${matchedAgentId}`;
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
这样不同 Agent 可以并发处理,同一 Agent 的同一会话串行处理。
|
|
285
|
+
|
|
286
|
+
### 4.4 InboundContext 中的 From / To 字段
|
|
287
|
+
|
|
288
|
+
构建 `ctxPayload` 时,`From` 和 `To` 字段规则如下:
|
|
289
|
+
|
|
290
|
+
| 字段 | 单聊 | 群聊 |
|
|
291
|
+
|------|------|------|
|
|
292
|
+
| `From` | `senderId` | `senderId` |
|
|
293
|
+
| `To` | `senderId` | `conversationId` |
|
|
294
|
+
| `OriginatingTo` | `senderId` | `conversationId` |
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const toField = isDirect ? senderId : data.conversationId;
|
|
298
|
+
// From 始终是 senderId,To 单聊用 senderId,群聊用 conversationId
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 五、配置参数速查
|
|
304
|
+
|
|
305
|
+
### 5.1 会话隔离相关配置
|
|
306
|
+
|
|
307
|
+
| 配置字段 | 类型 | 默认值 | 说明 |
|
|
308
|
+
|---------|------|--------|------|
|
|
309
|
+
| `sharedMemoryAcrossConversations` | `boolean` | `false` | 所有会话(单聊+群聊)共享同一记忆,优先级最高 |
|
|
310
|
+
| `separateSessionByConversation` | `boolean` | `true` | 是否按会话(群/单聊)区分 session;`false` 时按用户维度 |
|
|
311
|
+
| `groupSessionScope` | `"group" \| "group_sender"` | `"group"` | 群聊 session 粒度;`group_sender` 时群内每人独立 |
|
|
312
|
+
| `session.dmScope` | `string` | `"per-channel-peer"` | 传递给 SDK 的 dmScope 参数,影响 sessionKey 格式 |
|
|
313
|
+
|
|
314
|
+
### 5.2 路由相关配置
|
|
315
|
+
|
|
316
|
+
| 配置字段 | 类型 | 说明 |
|
|
317
|
+
|---------|------|------|
|
|
318
|
+
| `bindings` | `Binding[]` | Agent 路由规则列表,按顺序匹配 |
|
|
319
|
+
| `defaultAgent` | `string` | 未命中任何 binding 时的默认 Agent,默认为 `"main"` |
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 六、开发规范总结
|
|
324
|
+
|
|
325
|
+
1. **路由匹配用 `peerId`**:`match.peer.id` 与 `sessionContext.peerId` 比较,`peerId` 始终是真实的 `conversationId`(群)或 `senderId`(单聊),不受任何会话隔离配置影响。
|
|
326
|
+
|
|
327
|
+
2. **session 构建用 `sessionPeerId`**:`sessionKey` 和 `queueKey` 的构建均使用 `sessionContext.sessionPeerId`,受会话隔离配置影响,决定记忆/上下文的共享范围。
|
|
328
|
+
|
|
329
|
+
3. **两者职责严格分离**:路由(去哪个 Agent)和记忆隔离(共享多大范围的上下文)是两个独立维度,代码中不能用同一个字段同时承担两种职责。
|
|
330
|
+
|
|
331
|
+
4. **sessionKey 必须通过 SDK 构建**:使用 `core.channel.routing.buildAgentSessionKey()`,不要手动拼接字符串,`channel` 固定为 `'dingtalk-connector'`,`dmScope` 必须传递。
|
|
332
|
+
|
|
333
|
+
5. **bindings 顺序即优先级**:精确规则(指定 `peer.id`)必须放在通配规则(`peer.id: "*"`)之前。
|
|
334
|
+
|
|
335
|
+
6. **`sharedMemoryAcrossConversations` 优先级最高**:该配置开启后,`sessionPeerId` 被强制设为 `accountId`,覆盖其他所有会话隔离配置,但 `peerId` 不受影响,路由仍然正常工作。
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# DingTalk DEAP Agent Integration
|
|
2
|
+
|
|
3
|
+
> [中文版](DEAP_AGENT_GUIDE.md)
|
|
4
|
+
|
|
5
|
+
Connect DingTalk [DEAP](https://deap.dingtalk.com) Agent with [OpenClaw](https://openclaw.ai) Gateway to enable natural language-driven local device operations.
|
|
6
|
+
|
|
7
|
+
## Key Features
|
|
8
|
+
|
|
9
|
+
- ✅ **Natural Language Interaction** - Users type natural language commands in the DingTalk chat (e.g., "Find PDF files on my desktop"), and the Agent automatically parses and executes the corresponding operations
|
|
10
|
+
- ✅ **NAT Traversal** - Designed for local devices without public IPs, establishing a stable communication tunnel between local and cloud environments via the Connector client
|
|
11
|
+
- ✅ **Cross-Platform Support** - Provides native binaries for Windows, macOS, and Linux, ensuring smooth operation across all platforms
|
|
12
|
+
|
|
13
|
+
## System Architecture
|
|
14
|
+
|
|
15
|
+
This solution uses a layered architecture with three core components:
|
|
16
|
+
|
|
17
|
+
1. **OpenClaw Gateway** - Deployed on the local device, provides a standardized HTTP interface for receiving and processing operation commands from the cloud, leveraging the OpenClaw engine to execute tasks
|
|
18
|
+
2. **DingTalk OpenClaw Connector** - Runs locally, building a communication tunnel between local and cloud environments to solve the problem of local devices without public IPs
|
|
19
|
+
3. **DingTalk DEAP MCP** - An extension module for the DEAP Agent, responsible for forwarding user natural language requests to the OpenClaw Gateway via the cloud tunnel
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
graph LR
|
|
23
|
+
subgraph "DingTalk App"
|
|
24
|
+
A["User chats with Agent"] --> B["DEAP Agent"]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
subgraph "Local Environment"
|
|
28
|
+
D["DingTalk OpenClaw Connector"] --> C["OpenClaw Gateway"]
|
|
29
|
+
C --> E["PC Operation Execution"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
B -.-> D
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Implementation Guide
|
|
36
|
+
|
|
37
|
+
### Step 1: Set Up the Local Environment
|
|
38
|
+
|
|
39
|
+
Ensure the OpenClaw Gateway is installed and running on your local device. The default address is `127.0.0.1:18789`:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
openclaw gateway start
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Configure Gateway Parameters
|
|
46
|
+
|
|
47
|
+
1. Visit the [Configuration Page](http://127.0.0.1:18789/config)
|
|
48
|
+
2. In the **Overview**, set the Gateway Token and save it securely:
|
|
49
|
+

|
|
50
|
+
3. Switch to **Infrastructure** and enable the `OpenAI Chat Completions Endpoint`:
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
4. Click the `Save` button in the top-right corner to save your configuration
|
|
54
|
+
|
|
55
|
+
### Step 2: Obtain Required Parameters
|
|
56
|
+
|
|
57
|
+
#### Get corpId
|
|
58
|
+
|
|
59
|
+
Log in to the [DingTalk Developer Platform](https://open-dev.dingtalk.com) to find your enterprise CorpId:
|
|
60
|
+
|
|
61
|
+
<img width="864" height="450" alt="Get corpId from DingTalk Developer Platform" src="https://github.com/user-attachments/assets/18ec9830-2d43-489a-a73f-530972685225" />
|
|
62
|
+
|
|
63
|
+
#### Get apiKey
|
|
64
|
+
|
|
65
|
+
Log in to the [DingTalk DEAP Platform](https://deap.dingtalk.com), navigate to **Security & Permissions** → **API-Key Management** to create a new API Key:
|
|
66
|
+
|
|
67
|
+
<img width="1222" height="545" alt="DingTalk DEAP Platform API-Key Management" src="https://github.com/user-attachments/assets/dfe29984-4432-49c1-8226-0f9b60fbb5bc" />
|
|
68
|
+
|
|
69
|
+
### Step 3: Start the Connector Client
|
|
70
|
+
|
|
71
|
+
1. Download the installer for your operating system from the [Releases](https://github.com/hoskii/dingtalk-openclaw-connector/releases/tag/v0.0.1) page
|
|
72
|
+
2. Extract and run the Connector in the corresponding directory (macOS example):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
unzip connector-mac.zip
|
|
76
|
+
./connector-darwin -deapCorpId YOUR_CORP_ID -deapApiKey YOUR_API_KEY
|
|
77
|
+
```
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
### Step 4: Configure the DEAP Agent
|
|
81
|
+
|
|
82
|
+
1. Log in to the [DingTalk DEAP Platform](https://deap.dingtalk.com) and create a new agent:
|
|
83
|
+
|
|
84
|
+
<img width="2444" height="1486" alt="Create New Agent" src="https://github.com/user-attachments/assets/0b7f0855-f991-4aeb-b6e6-7576346b4477" />
|
|
85
|
+
|
|
86
|
+
2. In the skill management page, search for and integrate the OpenClaw skill:
|
|
87
|
+
|
|
88
|
+
<img width="3430" height="1732" alt="Add OpenClaw Skill" src="https://github.com/user-attachments/assets/d44f0038-f863-4c1f-afa7-b774d875e4ba" />
|
|
89
|
+
|
|
90
|
+
3. Configure skill parameters:
|
|
91
|
+
|
|
92
|
+
| Parameter | Source | Description |
|
|
93
|
+
| ------------ | ---------- | -------------------------------------------------------------------------------------- |
|
|
94
|
+
| apikey | From Step 2 | DEAP Platform API Key |
|
|
95
|
+
| apihost | Default | Typically `127.0.0.1:18789`. On Windows, you may need to use `localhost:18789` instead |
|
|
96
|
+
| gatewayToken | From Step 1 | Gateway authentication token |
|
|
97
|
+
|
|
98
|
+
<img width="3426" height="1752" alt="Configure OpenClaw Skill Parameters" src="https://github.com/user-attachments/assets/bc725789-382f-41b5-bbdb-ba8f29923d5c" />
|
|
99
|
+
|
|
100
|
+
Note that OpenClaw is an MCP, so you also need to configure its trigger rules. The MCP will only be invoked when the rules are satisfied:
|
|
101
|
+
<img width="1088" height="526" alt="image" src="https://github.com/user-attachments/assets/8b0b6f6d-70ff-4edc-b674-7a24126aadfa" />
|
|
102
|
+
|
|
103
|
+
4. Publish the Agent:
|
|
104
|
+
|
|
105
|
+
<img width="3416" height="1762" alt="Publish Agent" src="https://github.com/user-attachments/assets/3f8c3fdb-5f2b-4a4b-8896-35202e713bf3" />
|
|
106
|
+
|
|
107
|
+
### Step 5: Start Using
|
|
108
|
+
|
|
109
|
+
1. Search for and find your Agent in the DingTalk App:
|
|
110
|
+
|
|
111
|
+
<img width="1260" height="436" alt="Search for Agent" src="https://github.com/user-attachments/assets/30feff80-1b28-4274-830b-7045aed14980" />
|
|
112
|
+
|
|
113
|
+
2. Start your natural language conversation:
|
|
114
|
+
|
|
115
|
+
<img width="1896" height="1240" alt="Chat with Agent" src="https://github.com/user-attachments/assets/2a80aab8-3fbf-4d18-beea-770577cb1a40" />
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# 钉钉 DEAP Agent 集成
|
|
2
|
+
|
|
3
|
+
> [English Version](DEAP_AGENT_GUIDE.en.md)
|
|
4
|
+
|
|
5
|
+
通过将钉钉 [DEAP](https://deap.dingtalk.com) Agent 与 [OpenClaw](https://openclaw.ai) Gateway 连接,实现自然语言驱动的本地设备操作能力。
|
|
6
|
+
|
|
7
|
+
## 核心功能
|
|
8
|
+
|
|
9
|
+
- ✅ **自然语言交互** - 用户在钉钉对话框中输入自然语言指令(如"帮我查找桌面上的 PDF 文件"),Agent 将自动解析并执行相应操作
|
|
10
|
+
- ✅ **内网穿透机制** - 专为本地设备无公网 IP 场景设计,通过 Connector 客户端建立稳定的内外网通信隧道
|
|
11
|
+
- ✅ **跨平台兼容** - 提供 Windows、macOS 和 Linux 系统的原生二进制执行文件,确保各平台下的顺畅运行
|
|
12
|
+
|
|
13
|
+
## 系统架构
|
|
14
|
+
|
|
15
|
+
该方案采用分层架构模式,包含三个核心组件:
|
|
16
|
+
|
|
17
|
+
1. **OpenClaw Gateway** - 部署于本地设备,提供标准化 HTTP 接口,负责接收并处理来自云端的操作指令,调动 OpenClaw 引擎执行具体任务
|
|
18
|
+
2. **DingTalk OpenClaw Connector** - 运行于本地环境,构建本地与云端的通信隧道,解决内网设备无公网 IP 的问题
|
|
19
|
+
3. **DingTalk DEAP MCP** - 作为 DEAP Agent 的扩展能力模块,负责将用户自然语言请求经由云端隧道转发至 OpenClaw Gateway
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
graph LR
|
|
23
|
+
subgraph "钉钉 App"
|
|
24
|
+
A["用户与 Agent 对话"] --> B["DEAP Agent"]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
subgraph "本地环境"
|
|
28
|
+
D["DingTalk OpenClaw Connector"] --> C["OpenClaw Gateway"]
|
|
29
|
+
C --> E["PC 操作执行"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
B -.-> D
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 实施指南
|
|
36
|
+
|
|
37
|
+
### 第一步:部署本地环境
|
|
38
|
+
|
|
39
|
+
确认本地设备已成功安装并启动 OpenClaw Gateway,默认监听地址为 `127.0.0.1:18789`:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
openclaw gateway start
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### 配置 Gateway 参数
|
|
46
|
+
|
|
47
|
+
1. 访问 [配置页面](http://127.0.0.1:18789/config)
|
|
48
|
+
2. 在 **概览** 中设置 Gateway Token 并妥善保存:
|
|
49
|
+

|
|
50
|
+
3. 切换至 **基础设施**,启用 `OpenAI Chat Completions Endpoint` 功能:
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
4. 点击右上角 `Save` 按钮完成配置保存
|
|
54
|
+
|
|
55
|
+
### 第二步:获取必要参数
|
|
56
|
+
|
|
57
|
+
#### 获取 corpId
|
|
58
|
+
|
|
59
|
+
登录 [钉钉开发者平台](https://open-dev.dingtalk.com) 查看企业 CorpId:
|
|
60
|
+
|
|
61
|
+
<img width="864" height="450" alt="钉钉开发者平台获取 corpId" src="https://github.com/user-attachments/assets/18ec9830-2d43-489a-a73f-530972685225" />
|
|
62
|
+
|
|
63
|
+
#### 获取 apiKey
|
|
64
|
+
|
|
65
|
+
登录 [钉钉 DEAP 平台](https://deap.dingtalk.com),在 **安全与权限** → **API-Key 管理** 页面创建新的 API Key:
|
|
66
|
+
|
|
67
|
+
<img width="1222" height="545" alt="钉钉 DEAP 平台 API-Key 管理" src="https://github.com/user-attachments/assets/dfe29984-4432-49c1-8226-0f9b60fbb5bc" />
|
|
68
|
+
|
|
69
|
+
### 第三步:启动 Connector 客户端
|
|
70
|
+
|
|
71
|
+
1. 从 [Releases](https://github.com/hoskii/dingtalk-openclaw-connector/releases/tag/v0.0.1) 页面下载适配您操作系统的安装包
|
|
72
|
+
2. 解压后在对应目录运行 Connector(以 macOS 为例):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
unzip connector-mac.zip
|
|
76
|
+
./connector-darwin -deapCorpId YOUR_CORP_ID -deapApiKey YOUR_API_KEY
|
|
77
|
+
```
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
### 第四步:配置 DEAP Agent
|
|
81
|
+
|
|
82
|
+
1. 登录 [钉钉 DEAP 平台](https://deap.dingtalk.com),创建新的智能体:
|
|
83
|
+
|
|
84
|
+
<img width="2444" height="1486" alt="新建智能体界面" src="https://github.com/user-attachments/assets/0b7f0855-f991-4aeb-b6e6-7576346b4477" />
|
|
85
|
+
|
|
86
|
+
2. 在技能管理页面,搜索并集成 OpenClaw 技能:
|
|
87
|
+
|
|
88
|
+
<img width="3430" height="1732" alt="添加 OpenClaw 技能" src="https://github.com/user-attachments/assets/d44f0038-f863-4c1f-afa7-b774d875e4ba" />
|
|
89
|
+
|
|
90
|
+
3. 配置技能参数:
|
|
91
|
+
|
|
92
|
+
| 参数 | 来源 | 说明 |
|
|
93
|
+
| ------------ | ---------- | -------------------------------------------------------------------------------------- |
|
|
94
|
+
| apikey | 第二步获取 | DEAP 平台 API Key |
|
|
95
|
+
| apihost | 默认值 | 通常为 `127.0.0.1:18789`,在Windows环境下可能需要配置为 `localhost:18789` 才能正常工作 |
|
|
96
|
+
| gatewayToken | 第一步获取 | Gateway 配置的认证令牌 |
|
|
97
|
+
|
|
98
|
+
<img width="3426" height="1752" alt="配置 OpenClaw 技能参数" src="https://github.com/user-attachments/assets/bc725789-382f-41b5-bbdb-ba8f29923d5c" />
|
|
99
|
+
|
|
100
|
+
注意 OpenClaw 属于一个MCP,还需要配置他的触发规则,满足规则的情况下才会使用这个MCP:
|
|
101
|
+
<img width="1088" height="526" alt="image" src="https://github.com/user-attachments/assets/8b0b6f6d-70ff-4edc-b674-7a24126aadfa" />
|
|
102
|
+
|
|
103
|
+
4. 发布 Agent:
|
|
104
|
+
|
|
105
|
+
<img width="3416" height="1762" alt="发布 Agent" src="https://github.com/user-attachments/assets/3f8c3fdb-5f2b-4a4b-8896-35202e713bf3" />
|
|
106
|
+
|
|
107
|
+
### 第五步:开始使用
|
|
108
|
+
|
|
109
|
+
1. 在钉钉 App 中搜索并找到您创建的 Agent:
|
|
110
|
+
|
|
111
|
+
<img width="1260" height="436" alt="搜索 Agent" src="https://github.com/user-attachments/assets/30feff80-1b28-4274-830b-7045aed14980" />
|
|
112
|
+
|
|
113
|
+
2. 开始自然语言对话体验:
|
|
114
|
+
|
|
115
|
+
<img width="1896" height="1240" alt="与 Agent 对话" src="https://github.com/user-attachments/assets/2a80aab8-3fbf-4d18-beea-770577cb1a40" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none" viewBox="0 0 48 48"><path fill="#171A1D" d="M37.05 22.783c-6.758-5.216-14.378-12.128-22.73-19.538-.655-.585-1.242-.354-1.536.42-1.88 4.973-.058 9.386 2.889 11.932s7.368 4.912 10.058 6.155c.105.049.013.203-.093.163-4.953-2.182-8.397-3.765-13.07-7.368-.497-.388-1.01-.242-1.07.521-.384 4.748 2.657 8.483 6.058 9.745 2.1.781 4.398 1.212 6.53 1.474.109.015.084.178-.027.178-2.747.01-6.058-.654-8.935-1.751-.606-.233-.818.25-.722.633.491 2.008 2.974 5.076 6.926 5.73a12 12 0 0 0 2.228.115c.164 0 .208.089.154.217q-2.685 4.6-2.803 4.797c-.091.152-.036.275.156.275h3.543c.164 0 .264.106.18.246l-4.958 8.196c-.191.328.035.565.395.301s15.212-11.133 15.636-11.448c.195-.142.148-.327-.124-.327h-3.18c-.206 0-.252-.14-.111-.28.14-.141 3.602-3.594 4.837-4.888 1.283-1.35 1.938-3.825-.231-5.498"/></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DingTalk Connector Plugin for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* 钉钉企业内部机器人插件,使用 Stream 模式连接,支持 AI Card 流式响应。
|
|
5
|
+
* 已迁移到 OpenClaw SDK,支持多账号、安全策略等完整功能。
|
|
6
|
+
*
|
|
7
|
+
* Last updated: 2026-03-24
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* DingTalk Connector Plugin for OpenClaw
|
|
12
|
+
*
|
|
13
|
+
* 注意:本插件使用专用的 HTTP 客户端(见 src/utils/http-client.ts)
|
|
14
|
+
* 不会影响 OpenClaw Gateway 和其他插件的网络请求
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
18
|
+
import { dingtalkPlugin } from "./src/channel.ts";
|
|
19
|
+
import { setDingtalkRuntime } from "./src/runtime.ts";
|
|
20
|
+
import { registerGatewayMethods } from "./src/gateway-methods.ts";
|
|
21
|
+
|
|
22
|
+
export default function register(api: OpenClawPluginApi) {
|
|
23
|
+
setDingtalkRuntime(api.runtime);
|
|
24
|
+
api.registerChannel({ plugin: dingtalkPlugin });
|
|
25
|
+
|
|
26
|
+
// 注册 Gateway Methods
|
|
27
|
+
registerGatewayMethods(api);
|
|
28
|
+
}
|