Adventists - API文档
  1. TCP语音对话
Adventists - API文档
  • 总览
  • 更新日志
  • 基本配置
  • 2.5 更新
  • Quick Start
  • TCP语音对话
    • TCP接入
    • MCP协议接入
    • WebSocket快速接入
    • VAD 模式接入指南
    • Emoji 模式对接指南
    • TCP获取Token
      POST
  • 配置管理
    • 模版 与 角色
    • 名词和参数说明
    • 模板
      • 查询公开模板列表
      • 查询当前组织模板(包含公开和私有模板)
      • 创建角色模板
      • 查询角色模板信息
      • 修改角色模板
      • 删除角色模板
      • 提交角色模板记忆
    • 角色
      • 创建角色(通过模板创建)
      • 创建角色
      • 查询角色
      • 修改角色
      • 删除角色
      • 发送角色记忆
      • 读取角色记忆
      • 创建角色(通过现有角色创建)
    • 技能书
      • 查询技能书列表
      • 预创建任务书
      • 上传任务书内容
      • 完成上传任务书
      • 查询技能书创建进度
    • 音色
      • 获取音色列表
      • 试听音色
      • 提交音色
      • 查询音色状态
  • 聊天辅助
    • 上传画面(Base64)
      POST
    • 上传画面(File)
      POST
  1. TCP语音对话

VAD 模式接入指南

适用版本:tcpv2.3+

概述#

VAD(Voice Activity Detection)模式是一种智能语音检测模式,服务端自动检测语音结束点,客户端控制监听时机。
核心特点:
客户端主导:task_id 由客户端生成,监听时机由客户端控制
服务端检测:VAD 检测到语音结束后通知客户端
自动打断:新的 VAD 触发会自动打断当前处理
灵活适配:支持有/无 AEC 的不同实现方式

快速开始#

1. 认证连接#

在认证消息中指定 mode:vad:
{JWT_TOKEN}##mode:vad##input_audio_format:pcm

2. 开始监听#

客户端生成 task_id 并发送 LISTEN start 消息:
{
  "taskid": "abc12345",
  "type": "listen",
  "state": "start"
}

3. 上传音频#

使用相同的 task_id 发送音频帧(MessageType.AUDIO_FRAME = 2)

4. 接收通知#

服务端 VAD 会发送两种通知消息:
4.1 语音开始通知(可选处理):
{
  "taskid": "abc12345",
  "type": "listen",
  "state": "detecting",
  "mode": "vad"
}
检测到用户开始说话(连续有人声 200ms)
有 AEC 的客户端可立即停止播放旧音频,实现快速打断
免提模式可忽略此消息
4.2 语音结束通知(必须处理):
{
  "taskid": "abc12345",
  "type": "listen",
  "state": "stop",
  "mode": "vad"
}
检测到语音结束,服务端开始处理

5. 接收响应#

接收音频回复流(MessageType.AUDIO_FRAME = 2)

6. 继续对话#

根据需要发送新的 LISTEN start 开始下一轮对话

LISTEN 消息协议#

消息格式#

[##START][MessageType:8][task_id:8字节][seq:0000][JSON消息体][##END]

客户端发送 LISTEN start#

启动新的监听会话:
{
  "taskid": "abc12345",
  "type": "listen",
  "state": "start"
}
作用:
通知服务端开始接收该 task_id 的音频
服务端重置 VAD 状态,清空缓冲区

客户端发送 LISTEN stop#

主动取消当前监听:
{
  "taskid": "abc12345",
  "type": "listen",
  "state": "stop"
}
作用:
立即丢弃该 task_id 的所有音频数据
重置 VAD 状态

服务端发送 LISTEN detecting#

VAD 检测到语音开始(用户开始说话):
{
  "taskid": "abc12345",
  "type": "listen",
  "state": "detecting",
  "mode": "vad"
}
作用:
通知客户端检测到用户开始说话(连续有人声 200ms)
客户端可根据场景决定是否打断旧音频播放
有 AEC 的客户端:立即停止播放之前 task_id 的音频,实现干脆打断
免提模式的客户端:可以忽略此消息,不做任何处理
服务端继续接收音频,等待语音结束

服务端发送 LISTEN stop#

VAD 检测到语音结束:
{
  "taskid": "abc12345",
  "type": "listen",
  "state": "stop",
  "mode": "vad"
}
作用:
通知客户端语音已结束
服务端开始处理 STT + LLM + TTS

两种实现模式#

模式 1:有 AEC(边播边听)#

适用场景:Unity、Unreal Engine 等支持回声消除的客户端
流程:
1. 发送 LISTEN start (taskid: A)
2. 上传音频帧
3. 收到 LISTEN stop (taskid: A)
   ↓
4. 立即发送 LISTEN start (taskid: B)  ← 关键:立即开始
5. 继续上传音频帧(taskid: B)
6. 同时接收并播放 taskid A 的音频回复
特点:实现真正的实时对话,边播放边监听

模式 2:无 AEC(等待播放)#

适用场景:Web、移动 App 等简单客户端
流程:
1. 发送 LISTEN start (taskid: A)
2. 上传音频帧
3. 收到 LISTEN stop (taskid: A)
   ↓
4. 停止上传音频
5. 接收并播放 taskid A 的音频回复
6. 播放完成
   ↓
7. 发送 LISTEN start (taskid: B)  ← 关键:播放完成后
8. 开始上传音频帧(taskid: B)
特点:实现简单,避免回声问题

详细时序图#

场景 1:无 AEC 模式(等待播放完成)#

适用于简单客户端(Web、移动 App)
时间轴                客户端                                服务端
  │
  │                 生成 taskid: A
  │                 │
  ├─────────────────┼──── LISTEN start (A) ──────────────>│ 重置 VAD
  │                 │                                      │ 清空缓冲区
  │                 │<──── STATUS: 开始监听 ────────────────│
  │                 │                                      │
  │                 开始录音                               │
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:1) ───────────>│
  ├─────────────────┼──── AUDIO_FRAME (seq:2) ───────────>│ VAD 检测中
  ├─────────────────┼──── AUDIO_FRAME (seq:3) ───────────>│ 检测到语音
  │                 │                                      │
  │                 用户说完,停止说话                       │ VAD 触发
  │                 继续录音(背景音)                       │
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:4) ───────────>│
  │                 │<──── LISTEN stop (A) ─────────────────│ 语音结束
  │                 │                                      │
  │                 停止录音                                │ 模型处理中
  │                 等待播放                                │ (生成回复)
  │                 │                                      │
  │                 │<──── AUDIO (seq:1) ────────────────────│
  │                 │<──── AUDIO (seq:2) ────────────────────│
  │                 播放中...                               │
  │                 │<──── AUDIO (seq:3) ────────────────────│
  │                 │<──── END_FRAME ────────────────────────│
  │                 │                                      │
  │                 播放完成                                │
  │                 生成新 taskid: B                        │
  │                 │                                      │
  ├─────────────────┼──── LISTEN start (B) ──────────────>│ 重置 VAD
  │                 │<──── STATUS: 开始监听 ────────────────│
  │                 │                                      │
  │                 开始新一轮录音                           │
  ▼                 ▼                                      ▼

场景 2:有 AEC 模式(边播边听)#

适用于游戏引擎(Unity、Unreal)等支持回声消除的客户端
时间轴                客户端                                服务端
  │
  │                 生成 taskid: A
  │                 │
  ├─────────────────┼──── LISTEN start (A) ──────────────>│ 重置 VAD
  │                 │                                      │ 清空缓冲区
  │                 │<──── STATUS: 开始监听 ────────────────│
  │                 │                                      │
  │                 开始录音                               │
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:1) ───────────>│
  ├─────────────────┼──── AUDIO_FRAME (seq:2) ───────────>│ VAD 检测中
  ├─────────────────┼──── AUDIO_FRAME (seq:3) ───────────>│ 检测到语音
  │                 │                                      │
  │                 用户说完,停止说话                       │ VAD 触发
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:4) ───────────>│
  │                 │<──── LISTEN stop (A) ─────────────────│ 语音结束
  │                 │                                      │
  │                 生成新 taskid: B(立即!)              │ 模型处理 A
  │                 │                                      │ (生成回复)
  ├─────────────────┼──── LISTEN start (B) ──────────────>│ 重置 VAD
  │                 │<──── STATUS: 开始监听 ────────────────│
  │                 │                                      │
  │                 继续录音(taskid: B)                   │
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:1, tid:B) ────>│
  │                 │<──── AUDIO (seq:1, tid:A) ─────────────│ 返回 A 的回复
  │                 │                                      │
  │                 同时:                                  │
  │                 - 播放 A 的回复                         │
  │                 - 录音并上传 B 的音频                   │
  │                 (AEC 消除回声)                        │
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:2, tid:B) ────>│
  │                 │<──── AUDIO (seq:2, tid:A) ─────────────│
  ├─────────────────┼──── AUDIO_FRAME (seq:3, tid:B) ────>│
  │                 │<──── AUDIO (seq:3, tid:A) ─────────────│
  │                 │<──── END_FRAME (tid:A) ────────────────│
  │                 │                                      │
  │                 A 播放完成,B 继续录音                   │
  ▼                 ▼                                      ▼

场景 3:打断机制(仅限有 AEC 模式)#

基础打断流程#

时间轴                客户端                                服务端
  │
  │                 [taskid: A 正在处理中]                 │
  │                 │                                      │
  │                 录音中(taskid: B)                     │ 生成 A 的回复
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:5, tid:B) ────>│
  ├─────────────────┼──── AUDIO_FRAME (seq:6, tid:B) ────>│
  │                 │<──── AUDIO (seq:1, tid:A) ─────────────│ 返回 A
  │                 │                                      │
  │                 播放 A + 录音 B                         │ VAD 检测 B
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:7, tid:B) ────>│
  │                 │                                      │ B 触发 VAD!
  │                 │<──── LISTEN stop (B) ─────────────────│
  │                 │                                      │
  │                 生成新 taskid: C(立即!)              │ 打断 A
  │                 │                                      │ 开始处理 B
  ├─────────────────┼──── LISTEN start (C) ──────────────>│
  │                 │<──── STATUS: 开始监听 ────────────────│
  │                 │                                      │
  │                 停止播放 A 的回复                       │ A 停止发送
  │                 继续录音(taskid: C)                   │ 模型处理 B
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:1, tid:C) ────>│
  │                 │                                      │ 生成 B 的回复
  │                 │<──── AUDIO (seq:1, tid:B) ─────────────│ 返回 B
  │                 │                                      │
  │                 播放 B + 录音 C                         │
  ▼                 ▼                                      ▼

增强打断流程(使用语音开始检测)#

支持在用户开始说话(无需等待停顿)时立即打断:
时间轴                客户端                                服务端
  │
  │                 [taskid: A 正在处理中]                 │
  │                 │                                      │
  │                 录音中(taskid: B)                     │ 生成 A 的回复
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:5, tid:B) ────>│
  ├─────────────────┼──── AUDIO_FRAME (seq:6, tid:B) ────>│
  │                 │<──── AUDIO (seq:1, tid:A) ─────────────│ 返回 A
  │                 │                                      │
  │                 播放 A + 录音 B                         │ VAD 检测 B
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:7, tid:B) ────>│ 检测到人声!
  │                 │<──── LISTEN detecting (B) ─────────────│ 200ms后触发
  │                 │                                      │
  │                 立即停止播放 A                          │ 继续接收 B
  │                 继续录音(taskid: B)                   │
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:8, tid:B) ────>│
  ├─────────────────┼──── AUDIO_FRAME (seq:9, tid:B) ────>│
  │                 │                                      │ B 语音结束
  │                 │<──── LISTEN stop (B) ─────────────────│
  │                 │                                      │
  │                 生成新 taskid: C                        │ 开始处理 B
  │                 │                                      │
  ├─────────────────┼──── LISTEN start (C) ──────────────>│
  │                 │<──── STATUS: 开始监听 ────────────────│
  │                 │                                      │
  │                 继续录音(taskid: C)                   │ 模型处理 B
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:1, tid:C) ────>│
  │                 │                                      │ 生成 B 的回复
  │                 │<──── AUDIO (seq:1, tid:B) ─────────────│ 返回 B
  │                 │                                      │
  │                 播放 B + 录音 C                         │
  ▼                 ▼                                      ▼
关键差异:
传统打断:需要等用户说完(stop)才能打断
语音开始检测:用户刚开口(detecting)就立即打断,体验更流畅
客户端可根据场景选择是否使用 detecting 消息

场景 4:客户端主动取消#

时间轴                客户端                                服务端
  │
  │                 录音中(taskid: A)                     │
  │                 │                                      │
  ├─────────────────┼──── AUDIO_FRAME (seq:1, tid:A) ────>│
  ├─────────────────┼──── AUDIO_FRAME (seq:2, tid:A) ────>│
  │                 │                                      │
  │                 用户点击"取消"按钮                       │
  │                 │                                      │
  ├─────────────────┼──── LISTEN stop (A) ───────────────>│ 抛弃所有音频
  │                 │<──── STATUS: 已停止并清空 ───────────│ 重置 VAD
  │                 │                                      │
  │                 生成新 taskid: B                        │
  │                 │                                      │
  ├─────────────────┼──── LISTEN start (B) ──────────────>│ 开始新会话
  │                 │<──── STATUS: 开始监听 ────────────────│
  │                 │                                      │
  │                 开始新录音                              │
  ▼                 ▼                                      ▼

客户端状态机#

无 AEC 模式状态机#

                    ┌─────────────┐
                    │   初始化     │
                    └──────┬──────┘
                           │
                           ▼
                    ┌─────────────┐
              ┌────>│    空闲      │<────┐
              │     └──────┬──────┘     │
              │            │发送 start   │
              │            ▼            │
              │     ┌─────────────┐     │
              │     │   录音中     │     │
              │     └──────┬──────┘     │
              │            │收到 stop    │
              │            ▼            │
              │     ┌─────────────┐     │
              │     │   等待播放    │     │
              │     └──────┬──────┘     │
              │            │            │
              │            ▼            │
              │     ┌─────────────┐     │
              │     │   播放中     │     │
              │     └──────┬──────┘     │
              │            │播放完成     │
              └────────────┘            │
                                        │
              用户取消 ───────────────────┘
状态说明:
状态说明可执行操作
空闲等待用户开始对话发送 LISTEN start,生成新 task_id
录音中持续发送音频帧发送 AUDIO_FRAME;收到 stop 后转到等待播放
等待播放等待服务端返回音频接收音频帧
播放中播放音频回复播放音频;完成后回到空闲

有 AEC 模式状态机#

                    ┌─────────────┐
                    │   初始化     │
                    └──────┬──────┘
                           │
                           ▼
                    ┌─────────────┐
              ┌────>│    空闲      │
              │     └──────┬──────┘
              │            │发送 start
              │            ▼
              │     ┌─────────────┐
              │     │   录音中     │<──────┐
              │     └──────┬──────┘       │
              │            │收到 stop      │
              │            ▼              │
              │     ┌─────────────┐       │
              │     │ 录音+播放中  │       │
              │     └──────┬──────┘       │
              │            │              │
              │            ├──────────────┘
              │            │收到新 stop(打断)
              │            │立即发送新 start
              │            │
              │            │播放完成且无新对话
              └────────────┘

              用户取消可在任意状态发生
状态说明:
状态说明可执行操作
空闲等待用户开始对话发送 LISTEN start,生成新 task_id
录音中持续发送音频帧发送 AUDIO_FRAME;收到 stop 后立即发送新 start
录音+播放中同时播放和录音继续发送音频帧;播放音频;收到新 stop 则打断
关键差异:
无 AEC:线性状态转换,录音和播放不重叠
有 AEC:可以同时录音和播放,支持打断

打断机制详解#

什么是打断?#

在有 AEC 的实时对话中,当 AI 正在说话时,用户可以随时开口打断,系统会:
1.
立即停止播放当前 AI 的语音
2.
处理用户的新输入
3.
生成新的回复

打断的实现原理#

服务端机制#

task_id 识别机制:
每个 task_id 对应一个独立的对话轮次
服务端在发送每一帧音频时,都会检查 current_task_id
当 current_task_id 改变时,旧 task_id 的音频会自动停止发送
打断流程:
1. 用户正在说话(taskid: B)
2. 服务端正在返回 A 的音频回复
3. B 的 VAD 触发 → 发送 LISTEN stop (B)
4. 服务端更新 current_task_id = B
5. A 的音频检测到 task_id 不匹配 → 停止发送
6. 开始处理 B 的音频,生成新回复

客户端机制#

task_id 管理:
打断处理:
增强型打断处理(使用语音开始检测):

打断的时机判断#

服务端判断:
VAD 检测到新的语音结束
与当前 current_task_id 比较
如果不同,说明发生了打断
客户端判断:
收到新的音频帧
检查 task_id 是否与当前播放的不同
如果不同,停止旧的播放,开始新的播放

打断的边界情况#

情况 1:快速连续打断#

用户说话 A → 服务端处理 A → 用户立即说话 B → 用户又立即说话 C
处理方式:
服务端会依次收到 A、B、C 的音频
每次 VAD 触发都会更新 current_task_id
只有最后一个(C)的音频回复会被发送

情况 2:网络延迟导致的"假打断"#

客户端收到旧的音频包(网络延迟)→ 客户端误以为是新对话
解决方案:
客户端必须检查 task_id
只播放当前或更新的 task_id
忽略旧的 task_id

情况 3:用户在 AI 说完前一刻打断#

AI 的回复只剩最后一个字 → 用户开口打断
体验优化:
可以让客户端判断剩余播放时长
如果 < 500ms,可以播放完再切换
否则立即打断

最佳实践#

有 AEC 模式的最佳实践#

1. AEC 配置建议#

Unity 示例:
Web Audio API 示例:

2. 打断的用户体验优化#

渐进式停止:
视觉反馈:

3. 多轮打断的处理#

task_id 队列管理:

4. 性能优化#

并发控制:
音频缓冲优化:

5. 错误处理#

网络异常时的降级:
AEC 失效检测:

通用最佳实践#

1. task_id 生成策略#

推荐方式:

2. 日志记录#

详细的日志帮助调试:

3. 状态监控#

定期检查状态:

常见问题#

Q1: task_id 如何生成?#

A: 推荐生成方式:

Q2: 什么时候需要 AEC?#

A:
需要 AEC:要实现边播边听的实时对话
不需要 AEC:可以等待播放完成后再监听
大多数游戏引擎(Unity、Unreal)内置 AEC,Web/移动端需要额外实现。

Q3: 收到 LISTEN stop 后必须立即响应吗?#

A: 不需要。客户端可以根据自己的情况决定何时发送新的 LISTEN start:
有 AEC:可以立即发送
无 AEC:等播放完成
用户交互:等用户操作

Q4: 客户端主动取消的使用场景?#

A: 以下场景建议使用:
用户点击"取消"按钮
录音时间过长(如超过30秒)
检测到录音异常
用户切换到其他功能
发送 LISTEN stop 后,服务端会立即丢弃该 task_id 的所有音频。

Q5: 如何调试 VAD 模式?#

A: 建议的调试方法:
1.
查看服务端日志:记录了所有 LISTEN 消息和 VAD 触发
2.
打印消息流:在客户端打印所有收发的 LISTEN 消息
3.
使用简单客户端:先用 Python 脚本测试,再集成到正式客户端
4.
模拟 VAD 触发:用固定长度的音频测试流程

Q6: 可以同时有多个 task_id 吗?#

A: 不可以。服务端同一时间只处理一个 task_id:
发送新的 LISTEN start 会覆盖旧的 task_id
旧 task_id 的音频会被丢弃
建议一个对话完成后再开始下一个

Q7: VAD 误触发怎么办?#

A: 可以通过以下方式优化:
1.
客户端过滤:添加音量检测,过滤背景噪音
2.
确认机制:让用户确认是否说完
3.
调整参数:联系管理员调整服务端 VAD 阈值

Q8: 什么是语音开始检测(detecting)?#

A: 语音开始检测是 v2.3+ 版本新增的功能,用于实现更流畅的打断体验:
传统模式:
用户说话 → 等待停顿 → 服务端发送 stop → 客户端处理
问题:用户说话期间,AI 和用户同时在讲话
语音开始检测模式:
用户开始说话(200ms)→ 服务端发送 detecting → 客户端立即停止播放
优势:用户刚开口就打断,无需等待说完
如何使用:
适用场景:
有 AEC 的客户端:推荐使用,实现干脆打断
免提模式:可忽略 detecting 消息,保持原有逻辑

参数配置#

认证参数#

参数值说明
modevadVAD 模式
input_audio_formatpcm / opus输入音频格式
formatpcm / opus / mp3输出音频格式(可选)

服务端 VAD 配置#

环境变量配置:
参数默认值说明
VAD_THRESHOLD0.5VAD 检测阈值(0-1)
VAD_SILENCE_MS700静默判定时长(毫秒)
VAD_SPEECH_START_MS200语音开始检测阈值(毫秒)
STT_VALIDATION_ENABLEDtrue是否启用 STT 验证
配置说明:
VAD_THRESHOLD:语音检测灵敏度,越低越灵敏(容易误触),越高越保守(可能漏检)
VAD_SILENCE_MS:静默多久后认为语音结束,触发 state="stop" 消息
VAD_SPEECH_START_MS:连续有人声多久后触发 state="detecting" 消息,用于实现快速打断
有 AEC 设备:可降低至 150ms,提升响应速度
免提/回声大:提高至 300ms,减少误触发
STT_VALIDATION_ENABLED:是否过滤空白/噪音识别结果

消息类型参考#

类型值说明
AUTH1认证消息
AUDIO_FRAME2音频帧
END_FRAME3结束帧(manual 模式)
TEXT4文本消息
STATUS5状态消息
MCP6MCP 消息
SPEAK7播放指定内容
LISTEN8监听控制消息

修改于 2025-10-20 12:50:21
上一页
WebSocket快速接入
下一页
Emoji 模式对接指南
Built with