分身对话
与 SecondMe 分身进行实时对话,支持实名和匿名用户
分身对话接口允许第三方应用接入 SecondMe 分身的对话能力。你的用户(登录或匿名)可以与任何 SecondMe 分身进行实时聊天。
Base URL: https://api.mindverse.com/gate/lab
概述
分身对话分为两步:
- 初始化 (
/visitor-chat/init) — 验证身份 + 创建会话 + 返回 WebSocket 连接凭证 - 发送消息 (
/visitor-chat/send) — 发送文本消息,AI 回复通过 WebSocket 推送
身份模式
| 模式 | 认证方式 | 适用场景 |
|---|---|---|
| 实名用户 | OAuth2 authorization_code Token | 用户已通过 OAuth 登录你的应用 |
| 匿名用户 | OAuth2 client_credentials Token | 用户无需登录,你的后端代表用户发起对话 |
实名用户流程(2 步)
用户已通过 OAuth 登录,直接使用用户的 access token:
1. POST /visitor-chat/init (Authorization: Bearer 用户token, body: {apiKey})
2. POST /visitor-chat/send (Authorization: Bearer 用户token, body: {sessionId, message})匿名用户流程(3 步)
你的后端先获取应用级 token,再代表匿名用户调用:
1. POST /oauth/token/client → 获取应用 token(可缓存 7 天)
2. POST /visitor-chat/init → 初始化对话(需传 visitorId)
3. POST /visitor-chat/send → 发送消息注意:
client_secret只在你的后端使用,永远不要暴露给前端。匿名用户的前端应通过你的后端 API 代理调用。
获取应用 Token(匿名模式专用)
使用 client_credentials grant 获取应用级 access token。此 token 代表你的应用(而非特定用户),用于匿名用户场景。
POST /api/oauth/token/client请求
curl -X POST "https://api.mindverse.com/gate/lab/api/oauth/token/client" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=chat.write"响应
{
"code": 0,
"data": {
"accessToken": "lba_at_xxx...",
"tokenType": "Bearer",
"expiresIn": 604800,
"scope": ["chat.write"]
}
}| 字段 | 说明 |
|---|---|
| accessToken | 应用 token,用于后续 init 和 send 调用 |
| expiresIn | 有效期(秒),建议缓存,过期后重新获取 |
初始化对话
创建 visitor chat 会话,返回 WebSocket 连接凭证。
POST /api/secondme/visitor-chat/init认证
需要 OAuth2 Token(authorization_code 或 client_credentials)。
请求头
| 头 | 必需 | 说明 |
|---|---|---|
| Authorization | 是 | Bearer Token |
| Content-Type | 是 | application/json |
请求参数
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| apiKey | string | 是 | 分身 API Key(sk- 开头) |
| visitorId | string | 条件必需 | 匿名用户唯一标识,client_credentials 认证时必填。只允许字母、数字、下划线和连字符,最长 128 字符 |
| visitorName | string | 否 | 访客显示名称(匿名模式可选),显示在分身中心的对话列表中,最长 200 字符 |
请求示例
实名用户:
curl -X POST "https://api.mindverse.com/gate/lab/api/secondme/visitor-chat/init" \
-H "Authorization: Bearer lba_at_user_access_token" \
-H "Content-Type: application/json" \
-d '{
"apiKey": "sk-your-avatar-api-key"
}'匿名用户:
curl -X POST "https://api.mindverse.com/gate/lab/api/secondme/visitor-chat/init" \
-H "Authorization: Bearer lba_at_app_token" \
-H "Content-Type: application/json" \
-d '{
"apiKey": "sk-your-avatar-api-key",
"visitorId": "device_abc123",
"visitorName": "张三"
}'分身中心会显示
{visitorName}({appName})(如「张三(我的应用)」),其中appName由系统从你的应用注册信息自动获取。
响应
{
"code": 0,
"data": {
"sessionId": "6ff56704-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"wsUrl": "wss://ws.mindos.com/os/ws?wsId=...&authBody=...",
"avatarName": "我的分身",
"opening": "你好!有什么可以帮你的?"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| sessionId | string | 会话 ID(用于发送消息) |
| wsUrl | string | 完整的 WebSocket 连接地址(含认证参数,直接连接即可) |
| avatarName | string | 分身名称 |
| opening | string | null | 分身开场白 |
发送消息
发送文本消息到当前会话,AI 回复通过 WebSocket 推送。
POST /api/secondme/visitor-chat/send认证
需要 OAuth2 Token(与 init 使用相同的 token)。
请求参数
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| sessionId | string | 是 | 会话 ID(从 init 返回) |
| apiKey | string | 是 | 分身 API Key(与 init 相同) |
| message | string | 是 | 消息内容,1-10000 字符 |
token 过期或缓存失效时,
/send会自动用当前 OAuth token + apiKey 恢复会话,无需重新调用/init。
请求示例
curl -X POST "https://api.mindverse.com/gate/lab/api/secondme/visitor-chat/send" \
-H "Authorization: Bearer lba_at_your_token" \
-H "Content-Type: application/json" \
-d '{
"sessionId": "6ff56704-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"apiKey": "sk-your-avatar-api-key",
"message": "你好,请问你是谁?"
}'响应
{
"code": 0,
"data": {
"sent": true
}
}WebSocket 消息格式
连接 wsUrl 后,AI 回复会通过 WebSocket 推送。
AI 回复消息
{
"sender": "umm",
"sessionId": "6ff56704-...",
"index": 0,
"multipleData": [{
"singleDataType": "text",
"modal": {
"answer": "你好!我是..."
}
}]
}| 字段 | 说明 |
|---|---|
| sender | "umm" 表示 AI 回复 |
| index | 0, 1, 2, ... 为中间块(流式输出),-1 为回复结束 |
| multipleData[0].modal.answer | AI 回复文本 |
消息拼接逻辑
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.sender !== "umm") return;
if (msg.index === -1) {
// 回复结束
return;
}
const text = msg.multipleData?.[0]?.modal?.answer;
if (text) {
// 追加到当前回复
currentReply += text;
}
};完整示例
// 1. 初始化
const initRes = await fetch("/api/secondme/visitor-chat/init", {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ apiKey: "sk-xxx" }),
});
const { data } = await initRes.json();
// 2. 连接 WebSocket
const ws = new WebSocket(data.wsUrl);
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.sender === "umm" && msg.index !== -1) {
console.log("AI:", msg.multipleData?.[0]?.modal?.answer);
}
};
// 3. 发送消息
await fetch("/api/secondme/visitor-chat/send", {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
sessionId: data.sessionId,
apiKey: "sk-xxx",
message: "你好",
}),
});错误码
| 错误码 | HTTP 状态 | 说明 |
|---|---|---|
| visitor_chat.visitor_id_required | 400 | 匿名模式下未传 visitorId |
| visitor_chat.session_not_found | 400 | 会话未初始化或已过期,需重新调用 /init |
| visitor_chat.session_expired | 400 | 会话缓存已过期,需重新调用 /init |
| oauth2.invalid_client | 401 | client_id 或 client_secret 无效 |
| open.api.key.not.found | 401 | 分身 API Key 无效 |
| open.api.user.not.found | 404 | 用户不存在 |
FAQ
visitorId 可以用什么?
任何你能持久化的用户标识 — 设备 fingerprint、你自己数据库的 user ID、或随机 UUID。相同 visitorId 会复用已有会话。
client_credentials token 要缓存吗?
建议缓存。有效期 7 天,过期后重新获取即可。
WebSocket 断了怎么办?
重新调用 visitor-chat/init,会得到新的 wsUrl(session 会复用)。