rtc-voice-chat/backend/API.md

13 KiB
Raw Blame History

Python 后端 API 文档

Base URL: http://localhost:3001/v1

所有路径均挂载在 /v1 前缀下,完整路径如 POST http://localhost:3001/v1/getScenes

来自 java-mock 的请求须附加 内部签名 Header

/chat_callback火山引擎 RTC 平台直接回调,走独立 API Key 鉴权。


接口总览

方法 路径 调用方 鉴权 说明
POST /getScenes java-mock 内部签名 获取场景列表 & RTC 入房参数
POST /proxy?Action=xxx java-mock 内部签名 开始/停止语音对话
POST /session/history java-mock 内部签名 写入房间历史上下文
POST /chat_callback 火山引擎 RTC Bearer Token 自定义 LLM 回调SSE 流式)
POST /debug/chat 开发调试 调试 LLM 对话
GET /debug/rag 开发调试 调试 RAG 检索

调用时序

前端 → java-mock → Python 后端

1. POST /getScenes                          ← 获取场景 + RTC 入房参数
2. POST /session/history                    ← (可选) 注入历史上下文
3. POST /proxy?Action=StartVoiceChat        ← 启动语音对话
4.   火山引擎 RTC → POST /chat_callback     ← 平台回调(自动,非 java-mock
5. POST /proxy?Action=StopVoiceChat         ← 结束语音对话

一、获取场景列表

POST /v1/getScenes

返回所有已配置场景的信息和对应的 RTC 入房参数。每次调用会重新生成 RoomId/UserId/Token/TaskId 并写入服务端 Session供后续 StartVoiceChat 自动取用。

请求

Headers内部签名

Header 必填 说明
X-Internal-Service 固定 java-gateway
X-Internal-User-Id 当前登录用户 ID
X-Internal-Timestamp 毫秒时间戳(字符串)
X-Internal-Signature HMAC-SHA256 签名hex算法见内部鉴权协议

Body 无({} 或空均可)

成功响应 200

{
  "ResponseMetadata": {
    "Action": "getScenes"
  },
  "Result": {
    "scenes": [
      {
        "scene": {
          "id": "Custom",
          "name": "小块",
          "icon": "https://lf3-rtc-demo.volccdn.com/obj/rtc-aigc-assets/DoubaoAvatar.png",
          "botName": "agent-user-001",
          "isInterruptMode": true,
          "isVision": false,
          "isScreenMode": false,
          "isAvatarScene": false,
          "avatarBgUrl": ""
        },
        "rtc": {
          "AppId": "6xxxxxxx",
          "RoomId": "550e8400-e29b-41d4-a716-446655440000",
          "UserId": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
          "Token": "001xxxxxxAQBhMGI3Zm...",
          "TaskId": "e5f6a7b8-1234-5678-9abc-def012345678"
        }
      }
    ]
  }
}

Result.scenes[*].scene 字段说明:

字段 类型 说明
id string 场景唯一标识,后续 /proxy 接口的 SceneID 取此值
name string 场景显示名称
icon string 场景头像 URL
botName string AI Bot 在 RTC 房间中的 UserId
isInterruptMode boolean 是否开启用户打断模式
isVision boolean 是否开启视觉理解能力
isScreenMode boolean 是否为屏幕共享模式(视觉输入来自屏幕流)
isAvatarScene boolean 是否为数字人场景
avatarBgUrl string|null 数字人背景图 URL非数字人场景为空

Result.scenes[*].rtc 字段说明:

字段 类型 说明
AppId string 火山引擎 RTC AppId
RoomId string 本次生成的房间 IDUUID前端入房和后续接口都需要
UserId string 本次生成的用户 IDUUID前端入房使用
Token string RTC 入房 Token24 小时有效
TaskId string 语音任务 IDStopVoiceChat 时需要

注意: 每次调用 getScenes 都会重新生成 RoomId/UserId/Token/TaskId。必须在 getScenes 之后、StartVoiceChat 之前使用同一套参数。

失败响应

鉴权失败 401

{
  "code": 401,
  "message": "鉴权失败"
}

配置错误(缺少环境变量等):

{
  "ResponseMetadata": {
    "Action": "getScenes",
    "Error": {
      "Code": -1,
      "Message": "Custom 场景缺少以下环境变量: CUSTOM_ACCESS_KEY_ID, CUSTOM_SECRET_KEY"
    }
  }
}

二、开始/停止语音对话

POST /v1/proxy?Action={Action}&Version={Version}

带 SigV4 签名转发到火山引擎 RTC OpenAPI。内部会自动从 Session 取回 getScenes 分配的房间参数。

请求

Headers内部签名getScenes

Query 参数:

参数 类型 必填 默认值 说明
Action string StartVoiceChatStopVoiceChat
Version string 环境变量 RTC_OPENAPI_VERSION,兜底 2025-06-01 火山引擎 OpenAPI 版本

BodyJSON

字段 类型 必填 说明
SceneID string 场景 IDgetScenes 返回的 scene.id(当前固定为 "Custom"

请求示例:

{
  "SceneID": "Custom"
}

StartVoiceChat 处理逻辑

  1. 从 Session 取回 getScenes 时生成的 RoomIdUserIdTaskId
  2. room_id 追加到 LLM 回调 URL?room_id={RoomId}),使 /chat_callback 能关联历史上下文
  3. 使用 AK/SK 对请求做 SigV4 签名
  4. 转发到 https://rtc.volcengineapi.com?Action=StartVoiceChat

StartVoiceChat 成功响应 200

{
  "ResponseMetadata": {
    "RequestId": "2025070100000000000000000000abcd",
    "Action": "StartVoiceChat",
    "Version": "2025-06-01",
    "Service": "rtc"
  },
  "Result": {
    "Message": "success"
  }
}

StopVoiceChat 处理逻辑

  1. 从 Session 取回 RoomIdTaskId
  2. 清除该房间的历史上下文缓存session/history 写入的数据)
  3. 使用 AK/SK 签名后转发到火山引擎

StopVoiceChat 成功响应 200

{
  "ResponseMetadata": {
    "RequestId": "2025070100000000000000000000efgh",
    "Action": "StopVoiceChat",
    "Version": "2025-06-01",
    "Service": "rtc"
  },
  "Result": {
    "Message": "success"
  }
}

失败响应

鉴权失败 401

{
  "code": 401,
  "message": "鉴权失败"
}

参数/配置错误HTTP 200但包含 Error

{
  "ResponseMetadata": {
    "Action": "StartVoiceChat",
    "Error": {
      "Code": -1,
      "Message": "SceneID 不能为空SceneID 用于指定场景配置"
    }
  }
}
错误场景 Error.Message
Action 为空 Action 不能为空
SceneID 为空 SceneID 不能为空SceneID 用于指定场景配置
场景不存在 {SceneID} 不存在,请先配置对应场景。
AK/SK 缺失 Custom 场景的 AccountConfig.accessKeyId 不能为空
火山引擎接口报错 透传火山引擎原始错误信息

三、写入历史上下文

POST /v1/session/history

StartVoiceChat 之前调用,将上一次的对话历史注入该房间的上下文缓存。后续火山引擎 RTC 回调 /chat_callback 时,会自动将这些历史消息 prepend 到每次 LLM 请求的 messages 前面,实现跨会话的上下文延续。

请求

Headers内部签名getScenes

BodyJSON

字段 类型 必填 说明
room_id string RTC 房间 IDgetScenes 返回的 rtc.RoomId
messages array 历史消息列表,按时间正序排列

messages[*] 字段说明:

字段 类型 必填 可选值 说明
role string "user" | "assistant" 消息发送方角色
content string 消息文本内容

请求示例:

{
  "room_id": "550e8400-e29b-41d4-a716-446655440000",
  "messages": [
    { "role": "assistant", "content": "你好,我是小块,有什么需要帮忙的吗?" },
    { "role": "user", "content": "今天出勤情况咋样" },
    { "role": "assistant", "content": "今天全部门出勤率百分之九十五,共二十三人到岗。" }
  ]
}

成功响应 200

{
  "code": 200
}

失败响应

鉴权失败 401

{
  "code": 401,
  "message": "鉴权失败"
}

Body 校验失败 422FastAPI 自动校验):

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "room_id"],
      "msg": "Field required"
    }
  ]
}

注意:

  • 每次调用会覆盖room_id 下已有的历史,不是追加
  • StopVoiceChat 时会自动清除该房间的历史缓存
  • 如果不需要上下文延续(新对话),可以跳过此接口

四、自定义 LLM 回调SSE

此接口由火山引擎 RTC 平台自动回调,不经过 java-mock。

POST /v1/chat_callback?room_id={room_id}

鉴权: Authorization: Bearer <CUSTOM_LLM_API_KEY>

Query 参数:

参数 必填 说明
room_id 房间 IDStartVoiceChat 自动追加到回调 URL 中

BodyJSON

字段 类型 必填 说明
messages array 对话消息列表,最后一条必须是 user 角色
temperature float 采样温度
max_tokens int 最大生成 token 数
top_p float Top-P 采样

成功响应 200text/event-stream

data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"今"}}]}

data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"天"}}]}

data: [DONE]

失败响应:

HTTP 状态码 Error.Code 触发场景
401 AuthenticationError API Key 无效
400 BadRequest messages 为空 / 最后一条不是 user
500 InternalError LLM 初始化失败 / 请求解析失败

五、调试接口

仅供本地开发,无鉴权。

5.1 调试聊天

POST /v1/debug/chat
字段 类型 必填 说明
history array 历史消息列表(role + content),默认空
question string 本次用户提问

响应: 200 text/plain 流式文本

5.2 调试 RAG 检索

GET /v1/debug/rag?query={query}
参数 必填 说明
query 检索问题

响应 200

{
  "query": "今天出勤情况",
  "retrieved_context": "检索到的知识文本...",
  "length": 128,
  "status": "success"
}

内部鉴权协议

/getScenes/proxy/session/history 均启用此鉴权。

java-mock 发送方在请求头附加:

Header 必填 说明
X-Internal-Service 固定值 java-gateway
X-Internal-User-Id 当前登录用户 ID
X-Internal-Timestamp 毫秒级 Unix 时间戳(字符串)
X-Internal-Signature HMAC-SHA256 签名hex
X-User-Name URL 编码的用户显示名透传Python 侧暂未使用)
X-User-Sex 性别
X-User-Dept-Name URL 编码的部门名称

签名算法:

message   = "java-gateway:{userId}:{毫秒时间戳}"
signature = HMAC-SHA256(INTERNAL_SERVICE_SECRET, message)  → hex 编码

Python 接收方验证逻辑:

  1. INTERNAL_SERVICE_SECRET 未配置 → 直接放行(开发环境兼容)
  2. 校验 X-Internal-Service 必须为 java-gateway,且 4 个必要 Header 非空
  3. 时间窗口校验:|当前时间 - timestamp| ≤ 5 分钟(防重放)
  4. 重新计算 HMAChmac.compare_digest 常量时间比较(防时序攻击)

通用错误结构

内部接口getScenes / proxy / session/history 返回火山引擎风格:

{
  "ResponseMetadata": {
    "Action": "操作名",
    "Error": {
      "Code": -1,
      "Message": "错误描述"
    }
  }
}

chat_callback 返回 JSON 格式错误(非 SSE

{
  "Error": {
    "Code": "BadRequest",
    "Message": "messages 不能为空"
  }
}

环境变量

变量名 必填 说明
INTERNAL_SERVICE_SECRET 内部服务签名密钥,需与 java-mock 侧一致
CUSTOM_LLM_API_KEY /chat_callback 的 Bearer Token留空则跳过鉴权
LOCAL_LLM_API_KEY 方舟 LLM API Key
LOCAL_LLM_MODEL 方舟端点 ID
LOCAL_LLM_BASE_URL 方舟 API 地址

完整变量列表见 .env.example