AI 聊天 WebSocket
建立连接与鉴权
wss://ydzx.panretro.com/external/api/v1/sseStream/ws/{accessToken}
accessToken 必须是带 chat scope 的 OAuth2 access token。认证失败后服务端发送 error 帧并关闭连接。
认证成功:
{ "type": "ready", "focusConversationId": null }
连接建立时没有默认聊天。客户端必须先发送 focus 帧,再发送消息、读取历史或修改设置。
状态机
ready ── focus ──> focused ── send ──> streaming
│ │
├── setting ─────────┤
├── fetch ───────────┤
└── focus ───────────┘
focus 是每个 WebSocket 连接私有的状态。切换 focus 会取消该连接对旧会话的流订阅;服务端仍会继续生成旧会话的回复,但不会继续推送给这个连接。
所有帧都是 JSON 文本帧。服务端会校验 focus 会话归属,不能借由猜测会话 ID 访问其他用户的聊天。
客户端帧
focus:选择或新建聊天
选择已有聊天:
{ "type": "focus", "conversationId": 1950000000000000000 }
创建并自动 focus:
{ "type": "focus" }
conversationId: null 或空字符串的行为与省略相同,都会创建新聊天。可选 model 字段会成为新会话的默认模型。
成功响应:
{
"type": "focus",
"conversationId": 1950000000000000000,
"conversation": {
"id": 1950000000000000000,
"title": "新对话",
"defaultModel": "deepseek-chat",
"defaultModel": "deepseek-chat"
}
}
setting:设置当前聊天
{
"type": "setting",
"SYSTEM_PROMPT": "You are a concise assistant.",
"maxResponseLength": 1024
}
SYSTEM_PROMPT 也可写为 systemPrompt,最大长度为 8000 字符。null 或空字符串表示恢复使用全局系统提示词。
maxResponseLength(也兼容 maxTokens)范围为 1–32768;它会传给上游模型的 max_tokens 参数,单位是 token 而不是字符。传 null 表示不传该上游参数,使用无限制的上游默认行为。未出现的字段保持原值。
这些设置仅保存在当前 WebSocket 连接的内存中,并按会话 ID 隔离:设置会影响该连接当前 focus 会话后续发送的消息,不写入数据库,也不会影响其他连接。新连接或新会话默认使用全局系统提示词和无限输出长度。
send:发送消息
{
"type": "send",
"message": "解释一下 B+ 树",
"model": "deepseek-chat"
}
message 不能为空,最大 500 字符;model 可选。服务端先返回确认帧,再持续发送增量内容:
{ "type": "message", "messageId": 1950000000000000002, "conversationId": 1950000000000000000 }
fetch:分页读取当前聊天记录
{ "type": "fetch", "page": 1, "pageSize": 50 }
默认 page=1、pageSize=50,pageSize 最大 100。服务端返回:
{
"type": "history",
"conversationId": 1950000000000000000,
"page": 1,
"pageSize": 50,
"total": 2,
"list": []
}
subscribe / good:订阅正在生成的回复
{ "type": "subscribe" }
{ "type": "good" } 是兼容别名。当前 focus 没有进行中的生成时,服务端返回 idle。
服务端推送帧
| 类型 | 说明 |
|---|---|
ready | 认证成功,尚未 focus 会话 |
focus | focus 成功,包含会话信息 |
setting | 设置已保存 |
message | 消息已受理,包含助手消息 ID |
history | fetch 的分页结果 |
delta | 回复增量;包含 conversationId、messageId、content |
done | 本次流式回复完成 |
idle | 当前会话没有进行中的回复 |
error | 请求未执行,见下方错误说明 |
delta 示例:
{
"type": "delta",
"conversationId": 1950000000000000000,
"messageId": 1950000000000000002,
"content": "B+ 树是一种"
}
完成示例:
{
"type": "done",
"conversationId": 1950000000000000000,
"messageId": 1950000000000000002
}
错误帧与心跳
错误统一使用以下结构:
{ "type": "error", "code": "BAD_REQUEST", "message": "尚未 focus 对话,请先发送 focus 帧" }
常见 code:
| code | 含义 |
|---|---|
UNAUTHORIZED | access token 无效、过期,或缺少 chat scope |
BAD_FRAME | 帧不是合法 JSON 对象 |
BAD_REQUEST | 参数无效、没有 focus,或会话不存在 |
UNKNOWN_FRAME | 不支持的 type |
服务端每 25 秒发送 WebSocket Ping。客户端应使用 WebSocket 库自动回复 Pong;如库未自动处理,应回复 Pong 并在重连后重新发送 focus 帧。
与旧端点的关系
旧客户端继续使用:
/external/api/v1/sseStream/ws/{conversationId}/{accessToken}
该路径保留用于兼容既有网页客户端,但新接入方应使用本文的 focus 协议。旧路径同样执行会话归属校验。