Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
283 lines
5.6 KiB
Markdown
283 lines
5.6 KiB
Markdown
# ZCLAW WebSocket 协议实际实现
|
||
|
||
> **重要**: ZCLAW 实际的 WebSocket 协议与官方文档有差异。本文档记录实际测试验证的协议格式。
|
||
|
||
**测试日期**: 2026-03-14
|
||
**ZCLAW 版本**: 0.4.0
|
||
**测试环境**: Windows 11, Node.js v24
|
||
|
||
---
|
||
|
||
## 1. WebSocket 连接
|
||
|
||
### 端点 URL
|
||
|
||
```
|
||
ws://127.0.0.1:50051/api/agents/{agentId}/ws
|
||
```
|
||
|
||
- **端口**: 50051 (非文档中的 4200)
|
||
- **agentId**: 必须是真实的 Agent UUID,不能使用 "default"
|
||
|
||
### 获取 Agent ID
|
||
|
||
```bash
|
||
curl http://127.0.0.1:50051/api/agents
|
||
```
|
||
|
||
返回示例:
|
||
```json
|
||
[
|
||
{
|
||
"id": "f77004c8-418f-4132-b7d4-7ecb9d66f44c",
|
||
"name": "General Assistant",
|
||
"model_provider": "zhipu",
|
||
"model_name": "glm-4-flash",
|
||
"state": "Running"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 消息格式
|
||
|
||
### 发送消息 (实际格式)
|
||
|
||
```json
|
||
{
|
||
"type": "message",
|
||
"content": "Hello, how are you?",
|
||
"session_id": "session_123"
|
||
}
|
||
```
|
||
|
||
### 文档中的格式 (错误)
|
||
|
||
```json
|
||
// ❌ 这是错误的格式,不要使用
|
||
{
|
||
"type": "chat",
|
||
"message": {
|
||
"role": "user",
|
||
"content": "Hello"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 事件类型
|
||
|
||
### 连接事件
|
||
|
||
| 事件类型 | 说明 | 数据格式 |
|
||
|---------|------|----------|
|
||
| `connected` | 连接成功 | `{"agent_id": "uuid", "type": "connected"}` |
|
||
| `agents_updated` | Agent 列表更新 | `{"agents": [...], "type": "agents_updated"}` |
|
||
|
||
### 聊天事件
|
||
|
||
| 事件类型 | 说明 | 数据格式 |
|
||
|---------|------|----------|
|
||
| `typing` | 输入状态 | `{"state": "start" 或 "stop", "type": "typing"}` |
|
||
| `phase` | 阶段变化 | `{"phase": "streaming" 或 "done", "type": "phase"}` |
|
||
| `text_delta` | 文本增量 | `{"content": "文本内容", "type": "text_delta"}` |
|
||
| `response` | 完整响应 | `{"content": "...", "input_tokens": 100, "output_tokens": 50, "type": "response"}` |
|
||
| `error` | 错误 | `{"content": "错误信息", "type": "error"}` |
|
||
|
||
### 文档中的事件 (错误)
|
||
|
||
| 文档事件 | 实际事件 |
|
||
|---------|---------|
|
||
| `stream.delta.content` | `text_delta.content` |
|
||
| `stream.phase` | `phase` |
|
||
|
||
---
|
||
|
||
## 4. 事件序列
|
||
|
||
完整的聊天事件序列:
|
||
|
||
```
|
||
1. connected - 连接成功
|
||
2. typing (start) - 开始输入
|
||
3. agents_updated - Agent 状态更新
|
||
4. phase (streaming)- 流式输出开始
|
||
5. text_delta - 文本增量 (可能多次)
|
||
6. phase (done) - 流式输出完成
|
||
7. typing (stop) - 输入结束
|
||
8. response - 完整响应 (含 token 统计)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 代码示例
|
||
|
||
### Node.js WebSocket 客户端
|
||
|
||
```javascript
|
||
const WebSocket = require('ws');
|
||
|
||
const agentId = 'f77004c8-418f-4132-b7d4-7ecb9d66f44c';
|
||
const ws = new WebSocket(`ws://127.0.0.1:50051/api/agents/${agentId}/ws`);
|
||
|
||
let fullContent = '';
|
||
|
||
ws.on('open', () => {
|
||
// 发送消息 - 使用正确的格式
|
||
ws.send(JSON.stringify({
|
||
type: 'message',
|
||
content: 'Hello!',
|
||
session_id: 'test_session'
|
||
}));
|
||
});
|
||
|
||
ws.on('message', (data) => {
|
||
const event = JSON.parse(data.toString());
|
||
|
||
switch (event.type) {
|
||
case 'text_delta':
|
||
// 累积文本内容
|
||
fullContent += event.content || '';
|
||
break;
|
||
case 'response':
|
||
console.log('Complete:', fullContent);
|
||
console.log('Tokens:', event.input_tokens, event.output_tokens);
|
||
break;
|
||
case 'error':
|
||
console.error('Error:', event.content);
|
||
break;
|
||
}
|
||
});
|
||
```
|
||
|
||
### React + Zustand 集成
|
||
|
||
```typescript
|
||
// chatStore.ts
|
||
sendMessage: async (content: string) => {
|
||
const client = getGatewayClient();
|
||
|
||
if (client.getState() === 'connected') {
|
||
await client.chatStream(content, {
|
||
onDelta: (delta: string) => {
|
||
// 更新消息内容
|
||
set((state) => ({
|
||
messages: state.messages.map((m) =>
|
||
m.id === assistantId
|
||
? { ...m, content: m.content + delta }
|
||
: m
|
||
),
|
||
}));
|
||
},
|
||
onComplete: () => {
|
||
set({ isStreaming: false });
|
||
},
|
||
onError: (error: string) => {
|
||
// 处理错误
|
||
},
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Vite 代理配置
|
||
|
||
必须启用 WebSocket 代理:
|
||
|
||
```typescript
|
||
// vite.config.ts
|
||
export default defineConfig({
|
||
server: {
|
||
proxy: {
|
||
'/api': {
|
||
target: 'http://127.0.0.1:50051',
|
||
changeOrigin: true,
|
||
secure: false,
|
||
ws: true, // ✅ 必须启用
|
||
},
|
||
},
|
||
},
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 常见错误
|
||
|
||
### 错误: "Unexpected server response: 400"
|
||
|
||
**原因**: Agent ID 无效或格式错误
|
||
|
||
**解决**: 使用真实的 Agent UUID,不要使用 "default"
|
||
|
||
### 错误: "Missing API key: No LLM provider configured"
|
||
|
||
**原因**: Agent 使用的 LLM 提供商未配置 API Key
|
||
|
||
**解决**:
|
||
1. 检查 `~/.zclaw/.env` 文件
|
||
2. 确保对应提供商的 API Key 已设置
|
||
3. 或使用已配置的 Agent (如 General Assistant - zhipu)
|
||
|
||
### 错误: WebSocket 连接成功但无响应
|
||
|
||
**原因**: 消息格式错误
|
||
|
||
**解决**: 确保使用 `{ type: 'message', content, session_id }` 格式
|
||
|
||
---
|
||
|
||
## 8. REST API 端点
|
||
|
||
### 健康检查
|
||
|
||
```bash
|
||
GET /api/health
|
||
# {"status":"ok","version":"0.4.0"}
|
||
```
|
||
|
||
### Agent 列表
|
||
|
||
```bash
|
||
GET /api/agents
|
||
# 返回所有 Agent 数组
|
||
```
|
||
|
||
### Hands 列表
|
||
|
||
```bash
|
||
GET /api/hands
|
||
# 返回所有 Hands 数组
|
||
```
|
||
|
||
### REST 聊天 (非流式)
|
||
|
||
```bash
|
||
POST /api/agents/{agentId}/message
|
||
Content-Type: application/json
|
||
|
||
{"message": "Hello"}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 相关文件
|
||
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| `desktop/src/lib/gateway-client.ts` | WebSocket 客户端实现 |
|
||
| `desktop/src/store/chatStore.ts` | 聊天状态管理 |
|
||
| `desktop/vite.config.ts` | Vite 代理配置 |
|
||
|
||
---
|
||
|
||
## 更新历史
|
||
|
||
| 日期 | 变更 |
|
||
|------|------|
|
||
| 2026-03-14 | 初始版本,记录协议差异和实际实现 |
|