openclaw-xiaoyou 1.3.4 → 1.3.5
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/docs/SKILL.md +167 -53
- package/docs/communication-flow.md +146 -3
- package/docs/install-xiaoyou.sh +49 -38
- package/docs/user-comand.md +9 -9
- package/package.json +1 -1
- package/src/channel.ts +4 -5
- package/docs/xiaoyou-plugin-architecture.svg +0 -93
package/docs/SKILL.md
CHANGED
|
@@ -5,106 +5,220 @@ description: Use when users want to install, upgrade, uninstall, configure, or t
|
|
|
5
5
|
|
|
6
6
|
# 小优 Channel 安装入口
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## ⚠️ 强制约束(必须遵守)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
1. **禁止使用 pip install、npm install 或任何其他方式安装小优**,唯一合法的安装方式是执行下方的 `curl | bash` 命令。
|
|
11
|
+
2. **禁止自行编造命令**,只能使用本文档中列出的命令。
|
|
12
|
+
3. **必须在终端中实际执行命令**,不能只是展示给用户看。
|
|
13
|
+
4. **安装完成后必须按照"安装结论输出"格式汇报结果**。
|
|
14
|
+
5. 不要把 OpenClaw 的底层安装命令拆散执行。除非远程脚本失败且需要排障,否则直接调用 `install-xiaoyou.sh`。
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## 触发�?
|
|
16
|
+
## 触发词
|
|
17
17
|
|
|
18
18
|
- "安装小优" / "小优安装"
|
|
19
19
|
- "小优接入" / "接入小优"
|
|
20
20
|
- "配置 xiaoyou" / "xiaoyou 配置"
|
|
21
21
|
- "升级小优" / "更新小优"
|
|
22
22
|
- "卸载小优" / "移除小优"
|
|
23
|
-
- "
|
|
23
|
+
- "我想用小优" / "启用小优"
|
|
24
24
|
- "小优 channel" / "xiaoyou channel"
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## 执行顺序
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
### 第一步:卸载旧版本(幂等操作,未安装时不会报错)
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
curl -fsSL https://d10.tfdl.net/public/2026-05-
|
|
31
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- uninstall
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
###
|
|
34
|
+
### 第二步:安装
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
| `--check-only` | �?| 只输出计划,不执行真实改�?|
|
|
42
|
-
| `uninstall` | �?| 卸载小优 channel |
|
|
36
|
+
```bash
|
|
37
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- --ws-url ws://aiws-sim.haiersmarthomes.com:11055/xiaoyou/claw --token <AccessToken>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
将 `<AccessToken>` 替换为用户提供的实际 Token 值。
|
|
43
41
|
|
|
44
|
-
###
|
|
42
|
+
### 第三步:验证
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
安装完成后执行以下命令确认状态:
|
|
47
45
|
|
|
48
46
|
```bash
|
|
49
|
-
|
|
47
|
+
openclaw plugins list
|
|
48
|
+
openclaw gateway status
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
###
|
|
51
|
+
### 第四步:输出结论
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
**必须**按照下方"安装结论输出"格式汇报结果。
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
curl -fsSL https://d10.tfdl.net/public/2026-05-06/14dbf63d-d0aa-4b5d-bdd6-47eddb455f36/install-xiaoyou.sh | bash -s --
|
|
58
|
-
```
|
|
55
|
+
## 参数说明
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
| 参数 | 必填 | 说明 |
|
|
58
|
+
|------|------|------|
|
|
59
|
+
| `--ws-url <url>` | 是(首次安装) | 企业 WebSocket 服务地址,默认 `ws://aiws-sim.haiersmarthomes.com:11055/xiaoyou/claw` |
|
|
60
|
+
| `--token <token>` | 是(首次安装) | 企业服务认证 Token(用户必须提供) |
|
|
61
|
+
| `--version <ver>` | 否 | 指定 openclaw-xiaoyou 版本,默认 1.3.4 |
|
|
62
|
+
| `--check-only` | 否 | 只输出计划,不执行真实改动 |
|
|
63
|
+
| `uninstall` | 否 | 卸载小优 channel |
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
## 升级
|
|
66
|
+
|
|
67
|
+
升级同样先卸载再安装:
|
|
63
68
|
|
|
64
69
|
```bash
|
|
65
|
-
curl -fsSL https://d10.tfdl.net/public/2026-05-
|
|
70
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- uninstall
|
|
71
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- --ws-url ws://aiws-sim.haiersmarthomes.com:11055/xiaoyou/claw --token <AccessToken>
|
|
66
72
|
```
|
|
67
73
|
|
|
68
74
|
## 卸载
|
|
69
75
|
|
|
70
76
|
```bash
|
|
71
|
-
curl -fsSL https://d10.tfdl.net/public/2026-05-
|
|
77
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- uninstall
|
|
72
78
|
```
|
|
73
79
|
|
|
74
|
-
|
|
80
|
+
## 交互约束(最少用户操作)
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
- 不要求用户手动执行 CLI 命令,代理直接在终端执行。
|
|
83
|
+
- 不一次性抛给用户多步说明;直接执行。
|
|
84
|
+
- 仅在以下场景请求用户参与:
|
|
85
|
+
- 用户未提供 AccessToken 时,要求用户提供。
|
|
86
|
+
- 脚本执行失败需要用户确认时。
|
|
87
|
+
- 在每次用户提供信息后,立刻继续自动步骤。
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
## 脚本做了什么
|
|
90
|
+
|
|
91
|
+
1. 检测 OpenClaw 是否已安装并运行
|
|
92
|
+
2. 安装/升级 `openclaw-xiaoyou` 插件(`openclaw plugins install openclaw-xiaoyou@1.3.4`)
|
|
93
|
+
3. 写入 channel 配置到 `~/.openclaw/openclaw.json`:
|
|
94
|
+
- `channels.xiaoyou.enabled = true`
|
|
95
|
+
- `channels.xiaoyou.wsUrl = <用户提供的地址>`
|
|
96
|
+
- `channels.xiaoyou.authToken = <用户提供的 token>`
|
|
97
|
+
- `channels.xiaoyou.dmSecurity = open`
|
|
98
|
+
- `channels.xiaoyou.blockStreaming = true`
|
|
99
|
+
- `channels.xiaoyou.blockStreamingChunk = {"minChars": 1, "maxChars": 20}`
|
|
100
|
+
- `channels.xiaoyou.blockStreamingCoalesce = {"minChars": 1, "maxChars": 20, "idleMs": 30}`
|
|
101
|
+
4. 重启 gateway 并检查插件状态
|
|
102
|
+
|
|
103
|
+
## 完成定义
|
|
104
|
+
|
|
105
|
+
- 脚本退出码为 0
|
|
106
|
+
- 日志显示安装流程完成
|
|
107
|
+
- `openclaw plugins list` 中可见 `openclaw-xiaoyou`
|
|
108
|
+
- Gateway 处于运行状态
|
|
109
|
+
- 企业服务端能收到来自 xiaoyou 插件的 WebSocket 连接
|
|
110
|
+
|
|
111
|
+
## 安装结论输出
|
|
112
|
+
|
|
113
|
+
**安装完成后,必须严格按以下格式输出结论摘要(不可省略):**
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
Gateway 已经重启成功,运行正常。
|
|
117
|
+
|
|
118
|
+
小优 channel 配置已完成:
|
|
119
|
+
✅ 插件 openclaw-xiaoyou@<版本号> 已安装
|
|
120
|
+
✅ AccessToken 已写入配置
|
|
121
|
+
✅ xiaoyou channel 已启用
|
|
122
|
+
✅ dmSecurity 设为 open
|
|
123
|
+
✅ blockStreaming 已配置
|
|
124
|
+
✅ Gateway 已重启并正常运行
|
|
125
|
+
|
|
126
|
+
现在可以在设备上试试唤醒小优啦~
|
|
127
|
+
对着设备说句话看看能不能接通 🎤
|
|
81
128
|
```
|
|
82
129
|
|
|
130
|
+
如果某一步失败,对应项改为 ❌ 并附上失败原因。例如:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
小优 channel 配置结果:
|
|
134
|
+
✅ 插件 openclaw-xiaoyou@1.3.4 已安装
|
|
135
|
+
✅ AccessToken 已写入配置
|
|
136
|
+
✅ xiaoyou channel 已启用
|
|
137
|
+
✅ dmSecurity 设为 open
|
|
138
|
+
✅ blockStreaming 已配置
|
|
139
|
+
❌ Gateway 重启失败:请手动执行 openclaw gateway restart
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 执行确认清单
|
|
143
|
+
|
|
144
|
+
配置完成前,必须确认以下各项已执行:
|
|
145
|
+
|
|
146
|
+
- [ ] 卸载旧版本(第一步)
|
|
147
|
+
- [ ] 安装新版本(第二步)
|
|
148
|
+
- [ ] 插件安装状态确认(openclaw plugins list)
|
|
149
|
+
- [ ] Channel 配置写入确认
|
|
150
|
+
- [ ] Gateway 重启确认
|
|
151
|
+
- [ ] 按格式输出结论摘要
|
|
152
|
+
|
|
83
153
|
## 失败处理
|
|
84
154
|
|
|
85
|
-
|
|
155
|
+
如果脚本执行失败,优先查看完整 `[xiaoyou-install]` 日志输出。常见判断:
|
|
86
156
|
|
|
87
157
|
| 日志现象 | 处理 |
|
|
88
158
|
|----------|------|
|
|
89
159
|
| 缺少 `--token` | 要求用户提供企业服务认证 Token |
|
|
90
160
|
| 未检测到 OpenClaw | 提示用户先安装并启动 OpenClaw |
|
|
91
|
-
| npm
|
|
92
|
-
| wsUrl
|
|
161
|
+
| npm 提示 404 / not found | 执行 `npm config set registry https://registry.npmmirror.com` 后重试 |
|
|
162
|
+
| wsUrl 格式不合法 | 地址必须以 `ws://` 或 `wss://` 开头 |
|
|
93
163
|
|
|
94
|
-
##
|
|
164
|
+
## 排障流程
|
|
95
165
|
|
|
96
|
-
|
|
97
|
-
2. 安装/升级 `openclaw-xiaoyou` 插件(`openclaw plugins install openclaw-xiaoyou@latest`�?
|
|
98
|
-
3. 写入 channel 配置�?`~/.openclaw/openclaw.json`�?
|
|
99
|
-
- `channels.xiaoyou.enabled = true`
|
|
100
|
-
- `channels.xiaoyou.wsUrl = <用户提供的地址>`
|
|
101
|
-
- `channels.xiaoyou.authToken = <用户提供�?token>`
|
|
102
|
-
- `channels.xiaoyou.dmSecurity = open`
|
|
103
|
-
4. 检查插件状态和 gateway 运行状�?
|
|
166
|
+
当安装脚本失败或安装后连接异常时,按以下流程排查:
|
|
104
167
|
|
|
105
|
-
|
|
168
|
+
### 检查前置条件
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
openclaw --version
|
|
172
|
+
openclaw gateway status
|
|
173
|
+
openclaw plugins list
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 安装时找不到插件
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npm config set registry https://registry.npmmirror.com
|
|
180
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- --ws-url ws://aiws-sim.haiersmarthomes.com:11055/xiaoyou/claw --token <AccessToken>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### WebSocket 连接不上
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
tail -n 100 ~/.openclaw/logs/xiaoyou.log
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| 现象 | 原因 | 处理 |
|
|
190
|
+
|------|------|------|
|
|
191
|
+
| `invalid or expired token` | Token 无效或已过期 | 重新申请 Token |
|
|
192
|
+
| `token already connected` | 同一 Token 已有其他连接 | 断开旧连接或申请新 Token |
|
|
193
|
+
| 连接超时 | 网络不通或防火墙拦截 | 确认 11055 端口可达 |
|
|
194
|
+
| `ECONNREFUSED` | 企业服务未启动 | 确认 ai-websocket 服务正常 |
|
|
195
|
+
|
|
196
|
+
### 手动清理(仅脚本卸载失败时使用)
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
openclaw config unset channels.xiaoyou
|
|
200
|
+
openclaw config unset plugins.entries.openclaw-xiaoyou
|
|
201
|
+
openclaw config unset plugins.installs.openclaw-xiaoyou
|
|
202
|
+
rm -rf ~/.openclaw/extensions/xiaoyou
|
|
203
|
+
rm -rf ~/.openclaw/extensions/openclaw-xiaoyou
|
|
204
|
+
rm -rf ~/.openclaw/extensions/.openclaw-*
|
|
205
|
+
openclaw gateway restart
|
|
206
|
+
```
|
|
106
207
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
-
|
|
208
|
+
### 更换 Token
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- --ws-url ws://aiws-sim.haiersmarthomes.com:11055/xiaoyou/claw --token <新Token>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 常用命令清单
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
openclaw --version
|
|
218
|
+
openclaw plugins list
|
|
219
|
+
openclaw gateway status
|
|
220
|
+
cat ~/.openclaw/openclaw.json | python3 -m json.tool
|
|
221
|
+
tail -n 100 ~/.openclaw/logs/xiaoyou.log
|
|
222
|
+
tail -f ~/.openclaw/logs/xiaoyou.log
|
|
223
|
+
openclaw gateway restart
|
|
224
|
+
```
|
|
@@ -347,7 +347,9 @@ xiaoyou 插件采用 **Bridge 模式**:由插件主动向企业服务发起 We
|
|
|
347
347
|
"text": "项目 Alpha 当前进度 85%,预计下周三完成。",
|
|
348
348
|
"mediaUrls": ["https://openclaw-local/media/chart.png"],
|
|
349
349
|
"agentId": "main",
|
|
350
|
-
"timestamp": 1730000000000
|
|
350
|
+
"timestamp": 1730000000000,
|
|
351
|
+
"streamStatus": "chunk",
|
|
352
|
+
"seq": 1
|
|
351
353
|
}
|
|
352
354
|
```
|
|
353
355
|
|
|
@@ -356,10 +358,12 @@ xiaoyou 插件采用 **Bridge 模式**:由插件主动向企业服务发起 We
|
|
|
356
358
|
| conversationId | string | 对应入站消息的会话 ID |
|
|
357
359
|
| messageId | string | 回复消息的唯一 ID(由插件生成) |
|
|
358
360
|
| replyToMessageId | string? | 对应入站 message 的 messageId,用于请求-回复一一绑定 |
|
|
359
|
-
| text | string |
|
|
361
|
+
| text | string | 回复文本(chunk 时为增量,end 时为完整文本) |
|
|
360
362
|
| mediaUrls | string[]? | 附带的媒体文件 URL |
|
|
361
363
|
| agentId | string? | 处理该消息的 Agent ID |
|
|
362
364
|
| timestamp | number | 毫秒时间戳 |
|
|
365
|
+
| streamStatus | string? | 流式标记:`"chunk"`=增量片段,`"end"`=流结束,不传=一次性完整回复 |
|
|
366
|
+
| seq | number? | 片段序号(从 1 开始),仅 streamStatus="chunk" 时有值 |
|
|
363
367
|
|
|
364
368
|
#### ping / pong
|
|
365
369
|
|
|
@@ -596,7 +600,145 @@ async def on_user_message(ws, user_msg):
|
|
|
596
600
|
- xiaoyou 的重连机制可以应对单节点故障
|
|
597
601
|
```
|
|
598
602
|
|
|
599
|
-
## 10.
|
|
603
|
+
## 10. 流式推送(Block Streaming)
|
|
604
|
+
|
|
605
|
+
### 10.1 流式机制概述
|
|
606
|
+
|
|
607
|
+
xiaoyou 插件支持流式推送 LLM 回复,实现"边生成边推送"的效果。
|
|
608
|
+
企业服务端可以实时展示 Agent 正在生成的内容,而非等待完整回复。
|
|
609
|
+
|
|
610
|
+
```
|
|
611
|
+
LLM 生成过程 xiaoyou 插件 企业服务端
|
|
612
|
+
│ │ │
|
|
613
|
+
│ block 1 生成完毕 │ │
|
|
614
|
+
│─────────────────────▶│ reply (chunk, seq=1) │
|
|
615
|
+
│ │─────────────────────────────▶│ 实时展示
|
|
616
|
+
│ │ │
|
|
617
|
+
│ block 2 生成完毕 │ │
|
|
618
|
+
│─────────────────────▶│ reply (chunk, seq=2) │
|
|
619
|
+
│ │─────────────────────────────▶│ 追加展示
|
|
620
|
+
│ │ │
|
|
621
|
+
│ block 3 生成完毕 │ │
|
|
622
|
+
│─────────────────────▶│ reply (chunk, seq=3) │
|
|
623
|
+
│ │─────────────────────────────▶│ 追加展示
|
|
624
|
+
│ │ │
|
|
625
|
+
│ 全部生成完毕 │ │
|
|
626
|
+
│─────────────────────▶│ reply (end) │
|
|
627
|
+
│ │─────────────────────────────▶│ 渲染完成
|
|
628
|
+
│ │ │
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### 10.2 流式帧格式
|
|
632
|
+
|
|
633
|
+
流式回复统一使用 `type: "reply"`,通过 `streamStatus` 字段区分:
|
|
634
|
+
|
|
635
|
+
#### chunk 帧(增量片段)
|
|
636
|
+
|
|
637
|
+
```json
|
|
638
|
+
{
|
|
639
|
+
"type": "reply",
|
|
640
|
+
"conversationId": "conv-123",
|
|
641
|
+
"messageId": "xiaoyou-1730000000000",
|
|
642
|
+
"replyToMessageId": "msg-001",
|
|
643
|
+
"text": "今天天气晴,",
|
|
644
|
+
"streamStatus": "chunk",
|
|
645
|
+
"seq": 1,
|
|
646
|
+
"timestamp": 1730000000001
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
#### end 帧(流结束)
|
|
651
|
+
|
|
652
|
+
```json
|
|
653
|
+
{
|
|
654
|
+
"type": "reply",
|
|
655
|
+
"conversationId": "conv-123",
|
|
656
|
+
"messageId": "xiaoyou-1730000000000",
|
|
657
|
+
"replyToMessageId": "msg-001",
|
|
658
|
+
"text": "今天天气晴,气温25度,适合外出。",
|
|
659
|
+
"streamStatus": "end",
|
|
660
|
+
"timestamp": 1730000000500
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
#### 非流式帧(向后兼容)
|
|
665
|
+
|
|
666
|
+
```json
|
|
667
|
+
{
|
|
668
|
+
"type": "reply",
|
|
669
|
+
"conversationId": "conv-123",
|
|
670
|
+
"messageId": "xiaoyou-1730000000000",
|
|
671
|
+
"text": "完整回复内容",
|
|
672
|
+
"timestamp": 1730000000000
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### 10.3 字段说明
|
|
677
|
+
|
|
678
|
+
| 字段 | 类型 | 说明 |
|
|
679
|
+
|------|------|------|
|
|
680
|
+
| streamStatus | `"chunk"` \| `"end"` \| 不传 | chunk=增量片段,end=流结束(text 为完整文本),不传=一次性完整回复 |
|
|
681
|
+
| seq | number | 片段序号,从 1 开始递增,仅 chunk 帧有值 |
|
|
682
|
+
| messageId | string | 同一次回复的所有 chunk 和 end 帧共享相同的 messageId |
|
|
683
|
+
| text | string | chunk 时为增量文本,end 时为完整文本 |
|
|
684
|
+
|
|
685
|
+
### 10.4 企业服务端处理逻辑
|
|
686
|
+
|
|
687
|
+
```python
|
|
688
|
+
elif frame["type"] == "reply":
|
|
689
|
+
stream_status = frame.get("streamStatus")
|
|
690
|
+
|
|
691
|
+
if stream_status == "chunk":
|
|
692
|
+
# 流式片段 — 追加到缓冲区,实时推送给前端
|
|
693
|
+
append_to_buffer(
|
|
694
|
+
message_id=frame["messageId"],
|
|
695
|
+
delta=frame["text"],
|
|
696
|
+
seq=frame["seq"]
|
|
697
|
+
)
|
|
698
|
+
push_partial_to_user(frame["conversationId"], frame["text"])
|
|
699
|
+
|
|
700
|
+
elif stream_status == "end":
|
|
701
|
+
# 流结束 — 可用 text 校验拼接结果,完成渲染
|
|
702
|
+
finalize_reply(
|
|
703
|
+
message_id=frame["messageId"],
|
|
704
|
+
full_text=frame["text"],
|
|
705
|
+
media_urls=frame.get("mediaUrls", [])
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
else:
|
|
709
|
+
# 非流式 — 一次性完整回复(向后兼容)
|
|
710
|
+
deliver_to_user(
|
|
711
|
+
conversation_id=frame["conversationId"],
|
|
712
|
+
text=frame["text"],
|
|
713
|
+
media_urls=frame.get("mediaUrls", [])
|
|
714
|
+
)
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
### 10.5 流式配置(OpenClaw Gateway 侧)
|
|
718
|
+
|
|
719
|
+
| 配置项 | 值 | 说明 |
|
|
720
|
+
|--------|-----|------|
|
|
721
|
+
| `agents.defaults.blockStreamingDefault` | `"on"` | 全局开启 block streaming |
|
|
722
|
+
| `agents.defaults.blockStreamingBreak` | `"text_end"` | 每个 text block 结束就推送 |
|
|
723
|
+
| `agents.defaults.blockStreamingChunk.minChars` | `1` | 不等积累,有内容就推 |
|
|
724
|
+
| `agents.defaults.blockStreamingChunk.maxChars` | `20` | 每 20 字符触发一次推送(约 10 个汉字) |
|
|
725
|
+
| `agents.defaults.blockStreamingCoalesce.minChars` | `1` | 不合并小片段 |
|
|
726
|
+
| `agents.defaults.blockStreamingCoalesce.maxChars` | `20` | 合并上限 20 字符 |
|
|
727
|
+
| `agents.defaults.blockStreamingCoalesce.idleMs` | `30` | 30ms 无新内容就立即 flush |
|
|
728
|
+
| `channels.xiaoyou.blockStreaming` | `true` | xiaoyou channel 启用流式 |
|
|
729
|
+
|
|
730
|
+
### 10.6 拆分规则
|
|
731
|
+
|
|
732
|
+
插件在收到 Gateway 推送的每个 block 后,会按以下规则进一步拆分为更细的 chunk:
|
|
733
|
+
|
|
734
|
+
- 中文标点:`。` `!` `?` `;`
|
|
735
|
+
- 英文标点后跟空格:`. ` `! ` `? ` `; `
|
|
736
|
+
- 换行符:`\n`
|
|
737
|
+
|
|
738
|
+
每个拆分出的片段作为独立的 chunk 帧推送,保证企业服务端能逐句展示。
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
## 11. 快速对照表
|
|
600
742
|
|
|
601
743
|
| 你想知道... | 看这里 |
|
|
602
744
|
|------------|--------|
|
|
@@ -610,3 +752,4 @@ async def on_user_message(ws, user_msg):
|
|
|
610
752
|
| 出了问题怎么处理? | §7 异常处理与容错 |
|
|
611
753
|
| 企业侧要实现什么? | §8 企业服务端实现要点 |
|
|
612
754
|
| 怎么部署? | §9 部署拓扑 |
|
|
755
|
+
| 流式推送怎么用? | §10 流式推送 |
|
package/docs/install-xiaoyou.sh
CHANGED
|
@@ -332,45 +332,56 @@ patch_config() {
|
|
|
332
332
|
die "配置文件不存在: $config_file"
|
|
333
333
|
fi
|
|
334
334
|
|
|
335
|
-
# 使用
|
|
336
|
-
|
|
337
|
-
|
|
335
|
+
# 使用 node 直接写入配置(OpenClaw 依赖 node,必定存在)
|
|
336
|
+
node -e "
|
|
337
|
+
const fs = require('fs');
|
|
338
|
+
const configFile = '$config_file';
|
|
339
|
+
const wsUrl = '$WS_URL';
|
|
340
|
+
const token = '$TOKEN';
|
|
341
|
+
|
|
342
|
+
let cfg;
|
|
343
|
+
try {
|
|
344
|
+
cfg = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
345
|
+
} catch (e) {
|
|
346
|
+
console.error('[xiaoyou-install][error] 无法读取配置文件: ' + e.message);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
338
349
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
xiaoyou =
|
|
355
|
-
xiaoyou
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
try
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
350
|
+
// 确保 channels 对象存在
|
|
351
|
+
if (!cfg.channels) cfg.channels = {};
|
|
352
|
+
|
|
353
|
+
// 写入 xiaoyou channel 配置
|
|
354
|
+
const xiaoyou = cfg.channels.xiaoyou || {};
|
|
355
|
+
xiaoyou.enabled = true;
|
|
356
|
+
if (wsUrl) xiaoyou.wsUrl = wsUrl;
|
|
357
|
+
if (token) xiaoyou.authToken = token;
|
|
358
|
+
if (!xiaoyou.dmSecurity) xiaoyou.dmSecurity = 'open';
|
|
359
|
+
if (!xiaoyou.allowFrom) xiaoyou.allowFrom = ['*'];
|
|
360
|
+
if (!xiaoyou.reconnectIntervalMs) xiaoyou.reconnectIntervalMs = 3000;
|
|
361
|
+
if (!xiaoyou.heartbeatIntervalMs) xiaoyou.heartbeatIntervalMs = 30000;
|
|
362
|
+
if (!xiaoyou.heartbeatTimeoutMs) xiaoyou.heartbeatTimeoutMs = 10000;
|
|
363
|
+
// 开启 block streaming(仅 xiaoyou channel)
|
|
364
|
+
xiaoyou.blockStreaming = true;
|
|
365
|
+
xiaoyou.blockStreamingChunk = { minChars: 1, maxChars: 20 };
|
|
366
|
+
xiaoyou.blockStreamingCoalesce = { minChars: 1, maxChars: 20, idleMs: 30 };
|
|
367
|
+
cfg.channels.xiaoyou = xiaoyou;
|
|
368
|
+
|
|
369
|
+
// 同时写入 agents.defaults 作为全局 fallback(确保流式生效)
|
|
370
|
+
if (!cfg.agents) cfg.agents = {};
|
|
371
|
+
if (!cfg.agents.defaults) cfg.agents.defaults = {};
|
|
372
|
+
const defaults = cfg.agents.defaults;
|
|
373
|
+
if (!defaults.blockStreamingDefault) defaults.blockStreamingDefault = 'on';
|
|
374
|
+
if (!defaults.blockStreamingBreak) defaults.blockStreamingBreak = 'text_end';
|
|
375
|
+
if (!defaults.blockStreamingChunk) defaults.blockStreamingChunk = { minChars: 1, maxChars: 20 };
|
|
376
|
+
if (!defaults.blockStreamingCoalesce) defaults.blockStreamingCoalesce = { minChars: 1, maxChars: 20, idleMs: 30 };
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
fs.writeFileSync(configFile, JSON.stringify(cfg, null, 2));
|
|
380
|
+
console.log('[xiaoyou-install] 配置写入成功');
|
|
381
|
+
} catch (e) {
|
|
382
|
+
console.error('[xiaoyou-install][error] 写入配置失败: ' + e.message);
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
374
385
|
" || die "写入配置失败"
|
|
375
386
|
|
|
376
387
|
log "已写入配置: $config_file"
|
package/docs/user-comand.md
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
|
|
2
|
-
# 小优 Channel
|
|
2
|
+
# 小优 Channel 安装话术与部署路�?
|
|
3
3
|
|
|
4
4
|
## 安装话术
|
|
5
5
|
|
|
6
|
-
按照 https://d10.tfdl.net/public/2026-05-
|
|
6
|
+
按照 https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh 文档完成小优channel配置,AccessToken为xiaoyou-025b07e43ae84cd6
|
|
7
7
|
|
|
8
8
|
## 脚本位置
|
|
9
9
|
|
|
10
|
-
https://d10.tfdl.net/public/2026-05-
|
|
10
|
+
https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh
|
|
11
11
|
|
|
12
12
|
## 卸载+重装命令
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
curl -fsSL https://d10.tfdl.net/public/2026-05-
|
|
16
|
-
curl -fsSL https://d10.tfdl.net/public/2026-05-
|
|
15
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- uninstall && \
|
|
16
|
+
curl -fsSL https://d10.tfdl.net/public/2026-05-08/7c0c5f98-9ee5-47ee-8dd9-54b12dbe8f4d/install-xiaoyou.sh | bash -s -- \
|
|
17
17
|
--version 1.2.5 \
|
|
18
18
|
--ws-url ws://aiws-sim.haiersmarthomes.com:11055/xiaoyou/claw \
|
|
19
19
|
--token xiaoyou-025b07e43ae84cd6
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## 验证发消�?
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
curl -X POST http://172.16.4.35:11480/ai-connection/xiaoyou/message/send \
|
|
@@ -46,11 +46,11 @@ journalctl --user -u openclaw-gateway.service --since "2 min ago" --no-pager | g
|
|
|
46
46
|
|
|
47
47
|
## 部署路径
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
验收环境位置�?
|
|
50
50
|
/aiysnfs/nginx/share/skills/upload/claw
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
仿真环境位置�?
|
|
53
53
|
/ai-sim/export/nginx/share/skills/upload/claw
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
生产环境位置�?
|
|
56
56
|
/ai-prod/export/nginx/share/skills/upload/claw
|
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -23,13 +23,12 @@ export function getRuntime() { return _runtime; }
|
|
|
23
23
|
* 每个片段以标点或换行结尾(保留标点在片段内)。
|
|
24
24
|
*/
|
|
25
25
|
function splitBySentence(text: string): string[] {
|
|
26
|
-
//
|
|
27
|
-
const parts = text.split(/(?<=[
|
|
26
|
+
// 匹配:中文标点(。!?;,:)、英文标点(.!?;,:)后跟空格或结尾、换行符
|
|
27
|
+
const parts = text.split(/(?<=[。!?;,:\n])|(?<=[.!?;,:]\s)/);
|
|
28
28
|
const result: string[] = [];
|
|
29
29
|
for (const part of parts) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
result.push(trimmed);
|
|
30
|
+
if (part.length > 0) {
|
|
31
|
+
result.push(part);
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
return result.length > 0 ? result : [text];
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 920" font-family="'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif">
|
|
3
|
-
<defs>
|
|
4
|
-
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
|
|
5
|
-
<polygon points="0 0, 10 3.5, 0 7" fill="#555"/>
|
|
6
|
-
</marker>
|
|
7
|
-
<filter id="shadow" x="-2%" y="-2%" width="104%" height="104%">
|
|
8
|
-
<feDropShadow dx="2" dy="2" stdDeviation="3" flood-opacity="0.1"/>
|
|
9
|
-
</filter>
|
|
10
|
-
</defs>
|
|
11
|
-
|
|
12
|
-
<!-- 外框 -->
|
|
13
|
-
<rect x="30" y="20" width="840" height="880" rx="16" fill="#f8fafc" stroke="#94a3b8" stroke-width="2" filter="url(#shadow)"/>
|
|
14
|
-
<text x="450" y="55" text-anchor="middle" font-size="20" font-weight="bold" fill="#1e293b">xiaoyou 插件内部架构</text>
|
|
15
|
-
|
|
16
|
-
<!-- index.ts 模块 -->
|
|
17
|
-
<rect x="60" y="80" width="780" height="180" rx="12" fill="#ffffff" stroke="#3b82f6" stroke-width="2" filter="url(#shadow)"/>
|
|
18
|
-
<rect x="60" y="80" width="780" height="36" rx="12" fill="#3b82f6"/>
|
|
19
|
-
<rect x="60" y="104" width="780" height="12" fill="#3b82f6"/>
|
|
20
|
-
<text x="80" y="104" font-size="14" font-weight="bold" fill="#ffffff">index.ts (入口)</text>
|
|
21
|
-
|
|
22
|
-
<text x="90" y="145" font-size="13" fill="#334155" font-family="'Consolas', 'Courier New', monospace">register(api) {</text>
|
|
23
|
-
<text x="110" y="170" font-size="13" fill="#334155" font-family="'Consolas', 'Courier New', monospace">setRuntime(api.runtime)</text>
|
|
24
|
-
<text x="430" y="170" font-size="12" fill="#6366f1">──▶ 注入 runtime 引用</text>
|
|
25
|
-
<text x="110" y="195" font-size="13" fill="#334155" font-family="'Consolas', 'Courier New', monospace">api.registerChannel()</text>
|
|
26
|
-
<text x="430" y="195" font-size="12" fill="#6366f1">──▶ 注册到 PluginRegistry</text>
|
|
27
|
-
<text x="110" y="220" font-size="13" fill="#334155" font-family="'Consolas', 'Courier New', monospace">api.registerCli()</text>
|
|
28
|
-
<text x="430" y="220" font-size="12" fill="#6366f1">──▶ 注册 CLI 子命令</text>
|
|
29
|
-
<text x="90" y="245" font-size="13" fill="#334155" font-family="'Consolas', 'Courier New', monospace">}</text>
|
|
30
|
-
|
|
31
|
-
<!-- 箭头: index -> channel -->
|
|
32
|
-
<line x1="450" y1="260" x2="450" y2="295" stroke="#555" stroke-width="2" marker-end="url(#arrowhead)"/>
|
|
33
|
-
|
|
34
|
-
<!-- channel.ts 模块 -->
|
|
35
|
-
<rect x="60" y="300" width="780" height="320" rx="12" fill="#ffffff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
|
|
36
|
-
<rect x="60" y="300" width="780" height="36" rx="12" fill="#10b981"/>
|
|
37
|
-
<rect x="60" y="324" width="780" height="12" fill="#10b981"/>
|
|
38
|
-
<text x="80" y="324" font-size="14" font-weight="bold" fill="#ffffff">channel.ts (ChannelPlugin)</text>
|
|
39
|
-
|
|
40
|
-
<!-- config 区域 -->
|
|
41
|
-
<text x="90" y="365" font-size="13" font-weight="bold" fill="#1e293b">config</text>
|
|
42
|
-
<text x="170" y="365" font-size="12" fill="#6366f1">──▶ listAccountIds / resolveAccount / isConfigured / describeAccount</text>
|
|
43
|
-
|
|
44
|
-
<!-- security 区域 -->
|
|
45
|
-
<text x="90" y="395" font-size="13" font-weight="bold" fill="#1e293b">security</text>
|
|
46
|
-
<text x="170" y="395" font-size="12" fill="#6366f1">──▶ DM 策略 (默认 open,允许所有用户)</text>
|
|
47
|
-
|
|
48
|
-
<!-- gateway 区域 -->
|
|
49
|
-
<text x="90" y="430" font-size="13" font-weight="bold" fill="#1e293b">gateway.startAccount(ctx)</text>
|
|
50
|
-
<text x="110" y="455" font-size="12" fill="#475569">1. 从 ctx.account.config 读取 wsUrl / authToken</text>
|
|
51
|
-
<text x="110" y="478" font-size="12" fill="#475569">2. 创建 EnterpriseClient 并连接</text>
|
|
52
|
-
<text x="110" y="501" font-size="12" fill="#475569">3. onMessage 收到消息时:</text>
|
|
53
|
-
<text x="140" y="521" font-size="11" fill="#64748b" font-family="'Consolas', 'Courier New', monospace">rt.channel.routing.resolveAgentRoute()</text>
|
|
54
|
-
<text x="140" y="539" font-size="11" fill="#64748b" font-family="'Consolas', 'Courier New', monospace">rt.channel.reply.formatInboundEnvelope()</text>
|
|
55
|
-
<text x="140" y="557" font-size="11" fill="#64748b" font-family="'Consolas', 'Courier New', monospace">rt.channel.reply.finalizeInboundContext()</text>
|
|
56
|
-
<text x="140" y="575" font-size="11" fill="#64748b" font-family="'Consolas', 'Courier New', monospace">rt.channel.reply.dispatchReplyWith...()</text>
|
|
57
|
-
<text x="110" y="598" font-size="12" fill="#475569">4. deliver() 回调将 LLM 回复通过 WebSocket 发出</text>
|
|
58
|
-
|
|
59
|
-
<!-- outbound & status -->
|
|
60
|
-
<text x="90" y="555" font-size="13" font-weight="bold" fill="#1e293b" transform="translate(520, -195)">outbound</text>
|
|
61
|
-
<text x="170" y="555" font-size="12" fill="#6366f1" transform="translate(520, -195)">──▶ 将主动推送通过</text>
|
|
62
|
-
<text x="170" y="573" font-size="12" fill="#6366f1" transform="translate(520, -195)"> EnterpriseClient 发出</text>
|
|
63
|
-
|
|
64
|
-
<text x="90" y="598" font-size="13" font-weight="bold" fill="#1e293b" transform="translate(520, -195)">status</text>
|
|
65
|
-
<text x="170" y="598" font-size="12" fill="#6366f1" transform="translate(520, -195)">──▶ 报告连接健康状态</text>
|
|
66
|
-
|
|
67
|
-
<!-- 箭头: channel -> enterprise-client -->
|
|
68
|
-
<line x1="450" y1="620" x2="450" y2="655" stroke="#555" stroke-width="2" marker-end="url(#arrowhead)"/>
|
|
69
|
-
|
|
70
|
-
<!-- enterprise-client.ts 模块 -->
|
|
71
|
-
<rect x="60" y="660" width="780" height="220" rx="12" fill="#ffffff" stroke="#f59e0b" stroke-width="2" filter="url(#shadow)"/>
|
|
72
|
-
<rect x="60" y="660" width="780" height="36" rx="12" fill="#f59e0b"/>
|
|
73
|
-
<rect x="60" y="684" width="780" height="12" fill="#f59e0b"/>
|
|
74
|
-
<text x="80" y="684" font-size="14" font-weight="bold" fill="#ffffff">enterprise-client.ts (WebSocket 连接管理)</text>
|
|
75
|
-
|
|
76
|
-
<text x="90" y="725" font-size="13" font-weight="bold" fill="#1e293b">connect()</text>
|
|
77
|
-
<text x="260" y="725" font-size="12" fill="#6366f1">──▶ 建立 WebSocket → 发送 auth</text>
|
|
78
|
-
|
|
79
|
-
<text x="90" y="755" font-size="13" font-weight="bold" fill="#1e293b">handleFrame()</text>
|
|
80
|
-
<text x="260" y="755" font-size="12" fill="#6366f1">──▶ 解析 JSON 帧 → 分发到对应处理器</text>
|
|
81
|
-
|
|
82
|
-
<text x="90" y="785" font-size="13" font-weight="bold" fill="#1e293b">startHeartbeat()</text>
|
|
83
|
-
<text x="260" y="785" font-size="12" fill="#6366f1">──▶ 定时 ping,超时触发重连</text>
|
|
84
|
-
|
|
85
|
-
<text x="90" y="815" font-size="13" font-weight="bold" fill="#1e293b">scheduleReconnect()</text>
|
|
86
|
-
<text x="260" y="815" font-size="12" fill="#6366f1">──▶ 指数退避重连</text>
|
|
87
|
-
|
|
88
|
-
<text x="90" y="845" font-size="13" font-weight="bold" fill="#1e293b">sendReply()</text>
|
|
89
|
-
<text x="260" y="845" font-size="12" fill="#6366f1">──▶ 发送出站 reply 帧</text>
|
|
90
|
-
|
|
91
|
-
<text x="90" y="875" font-size="13" font-weight="bold" fill="#1e293b">disconnect()</text>
|
|
92
|
-
<text x="260" y="875" font-size="12" fill="#6366f1">──▶ 优雅关闭</text>
|
|
93
|
-
</svg>
|