You need to enable JavaScript to run this app.
导航
接收状态变化消息
最近更新时间:2025.06.12 17:48:16首次发布时间:2025.01.09 17:23:24
我的收藏
有用
有用
无用
无用

在实时对话式 AI 场景下,你可以通过回调机制接收智能体任务状态和智能体状态,以保证业务的稳定性。

  • 智能体任务状态:智能体任务的整体生命周期事件,如任务开始、结束、各处理阶段(ASR, LLM, TTS)以及任务执行过程中的错误。
  • 智能体状态:智能体在对话交互中的实时状态,如聆听中、思考中、说话中、被打断等。
接收智能体任务状态

当智能体任务的整体状态发生变化或任务执行出现错误时,可以通过配置回调 URL 来接收服务端推送事件回调消息(JSON 格式)。

  • 配置回调
    1. 前往 RTC 控制台-功能配置,选择回调设置。
    2. 单击 添加配置,按页面指引填写相关参数,其中回调事件选择 VoiceChat
      配置完成后,当智能体任务状态变更或发生错误时,配置的 URL 将会收到推送事件回调消息(JSON 格式)。

    说明

    如无特殊需求则无需配置业务标识,否则可能导致无法正常收到回调消息。

  • 回调消息:具体内容参看 VoiceChat
接收智能体状态

实时获取 AI 智能体在对话过程中的关键状态变化,如“聆听中”、“思考中“、“说话中“、“被打断“等。

通过服务端接收

配置回调

调用 StartVoiceChat 接口,配置如下参数:

// 调用 StartVoiceChat 接口时 AgentConfig 部分示例
"AgentConfig": {
    "EnableConversationStateCallback": true,
    // ... 其他 AgentConfig 参数
    "ServerMessageURLForRTS": "YOUR_SERVER_CALLBACK_URL", // 必填,接收回调的 URL
    "ServerMessageSignatureForRTS": "YOUR_SIGNATURE" // 必填,签名密钥,用于验证回调来源的安全性
}

当智能体任务状态发生变化时,你配置的回调 URL 会收到回调消息。

说明

消息格式为 Base 64 编码的二进制,使用前需先解析。

消息格式和内容

收到的回调格式如下:

参数名类型描述
messageStringBase 64 编码的二进制消息内容。长度不超过 48 KB。格式参看二进制消息格式
signatureString鉴权签名。可与StartVoiceChat接口中传入的ServerMessageSignatureForRTS字段值进行对比以进行鉴权验证。

二进制消息格式如下:

alt

参数名类型描述
magic numberbinary消息格式,固定为 conv
lengthbinary消息长度,单位为 bytes。存放方式为大端序。
conversation_status_messagebinary消息详细信息。格式参看 conversation_status_message 格式

conversation_status_message:

参数名类型描述
TaskIdString智能体任务 ID。
UserIDString说话人 UserId。
RoundIDInt64对话轮次。从 0 开始计数。
EventTimeInt64该事件在 RTC 服务器上发生的 Unix 时间戳 (ms)。
StageStage任务状态详细描述。

Stage

参数名类型描述
CodeInt任务状态码。
  • 1:智能体聆听中。
  • 2:智能体思考中。
  • 3:智能体说话中。
  • 4:智能体被打断。
  • 5:智能体说话完成。
DescriptionString任务状态描述。

解析消息

你可以参考以下示例代码对回调信息中的 message 内容进行解析。

package main

import (
   "encoding/base64"
   "encoding/binary"
   "encoding/json"
   "fmt"
)

const (
   conversationStageHeader = "conv"
   exampleSignature        = "example_signature"
)

type RtsMessage struct {
   Message   string `json:"message"`
   Signature string `json:"signature"`
}

type Conv struct {
   TaskID    string    `json:"TaskId"`
   UserID    string    `json:"UserID"`
   RoundID   int64     `json:"RoundID"`   
   EventTime int64     `json:"EventTime"`
   Stage     StageInfo `json:"Stage"`
}

type StageInfo struct {
   Code        stageCode `json:"Code"`
   Description string    `json:"Description"`
}
type stageCode int

const (
   _ stageCode = iota
   listening
   thinking
   answering
   interrupted
   answerFinish
)

var (
   stageListening    = StageInfo{Code: listening, Description: "listening"}
   stageThinking     = StageInfo{Code: thinking, Description: "thinking"}
   stageAnswering    = StageInfo{Code: answering, Description: "answering"}
   stageInterrupted  = StageInfo{Code: interrupted, Description: "interrupted"}
   stageAnswerFinish = StageInfo{Code: answerFinish, Description: "answerFinish"}
)

func HandleConversationStageMsg(c *gin.Context) {
   msg := &RtsMessage{}
   if err := c.BindJSON(&msg); err != nil {
      fmt.Printf("BindJson failed,err:%v\n", err)
      return
   }
   if msg.Signature != exampleSignature {
      fmt.Printf("Signature not match\n")
      return
   }

   conv, err := Unpack(msg.Message)
   if err != nil {
      fmt.Printf("Unpack failed,err:%v\n", err)
      return
   }

   fmt.Println(conv)

   //业务逻辑

   c.String(200, "ok")
}

func Unpack(msg string) (*Conv, error) {
   data, err := base64.StdEncoding.DecodeString(msg)
   if err != nil {
      return nil, fmt.Errorf("DecodeString failed,err:%v", err)
   }
   if len(data) < 8 {
      return nil, fmt.Errorf("Data invalid")
   }
   dataHeader := string(data[:4])
   if dataHeader != conversationStageHeader {
      return nil, fmt.Errorf("Header not match")
   }
   dataSize := binary.BigEndian.Uint32(data[4:8])
   if dataSize+8 != uint32(len(data)) {
      return nil, fmt.Errorf("Size not match")
   }

   subData := data[8:]
   conv := &Conv{}
   err = json.Unmarshal(subData, conv)
   if err != nil {
      return nil, fmt.Errorf("Unmarshal failed,err:%v\n", err)
   }
   return conv, nil
}

func main() {

   r := gin.Default()
   r.POST("/example_domain/vertc/cstage", HandleConversationStageMsg)
   r.Run()
}

通过客户端接收

配置回调

  1. 调用 StartVoiceChat 接口将 AgentConfig.EnableConversationStateCallback 设置为 true
  2. 在客户端监听回调 onRoomBinaryMessageReceived
    当智能体任务状态发生变化时,客户端也会通过该回调中收到智能体状态。消息格式为二进制,使用前需先解析。

消息格式和内容

参数名类型描述
uidString消息发送者 ID。
messageString二进制消息内容。长度不超过 64 KB。与服务端返回二进制消息格式相同,详细参看二进制消息格式

解析消息

你可以参考以下示例代码对回调信息中的 message 内容进行解析。

import VERTC from '@volcengine/rtc';

/**
 * @brief TLV 数据格式转换成字符串
 * @note TLV 数据格式
 * | magic number | length(big-endian) | value |
 * @param {ArrayBufferLike} tlvBuffer
 * @returns 
 */
function tlv2String(tlvBuffer: ArrayBufferLike) {
  const typeBuffer = new Uint8Array(tlvBuffer, 0, 4);
  const lengthBuffer = new Uint8Array(tlvBuffer, 4, 4);
  const valueBuffer = new Uint8Array(tlvBuffer, 8);

  let type = '';
  for (let i = 0; i < typeBuffer.length; i++) {
    type += String.fromCharCode(typeBuffer[i]);
  }

  const length =
    (lengthBuffer[0] << 24) | (lengthBuffer[1] << 16) | (lengthBuffer[2] << 8) | lengthBuffer[3];

  const value = new TextDecoder().decode(valueBuffer.subarray(0, length));

  return { type, value };
};

/**
 * @brief Room Message Handler
 */
function handleRoomBinaryMessageReceived(
  event: {
    userId: string;
    message: ArrayBuffer;
  },
) {
  const { message } = event;
  const { type, value } = tlv2String(message);
  const data = JSON.parse(value);
  const { EventTime, RoundID, Stage, TaskId, UserID } = data || {};
  const { Code, Description } = Stage || {};
  // 对 type、data 进行业务处理
  console.log(type, EventTime, RoundID, TaskId, UserID, Code, Description);
  // ...
}

/**
 * @brief 监听房间内二进制消息
 */
this.engine.on(VERTC.events.onRoomBinaryMessageReceived, handleRoomBinaryMessageReceived);