获取智能体状态
如果你的 App 希望感知智能体状态变化,可以选择通过 RTM Message 或 RTM Presence 获取状态。本文介绍如何通过 RTM Message 获取智能体状态,并说明该方案与 RTM Presence 方案的差异。
- 本文重点介绍如何通过 RTM Message 获取智能体状态。
- 如果你希望使用客户端组件监听状态变化,可以参考监听智能体事件。
- 如果你希望通过 RTM Presence 获取状态,可以继续沿用现有 Presence 方案。
实现原理
ConvoAI 支持通过 RTM Message 向客户端推送智能体状态变化事件。与 RTM Presence 的键值同步方式不同,RTM Message 会将每一次状态变化包装成一条独立的事件消息,便于你记录状态变更时间、进行顺序处理或与其他 RTM Message 事件统一消费。
ConvoAI 推送的状态消息通过 RTM 的消息回调(onMessageEvent)下发。消息的内层 message 字段是一个 JSON 字符串,需要再次解析才能得到具体的状态事件。对于状态消息,建议你直接根据内层 message 中的 event_type 字段识别消息类型。
当前通过 RTM Message 推送的智能体状态包括:
state.listeningstate.thinkingstate.speaking
其工作方式如下:
ConvoAI 状态变化
├─ listening -> true / false
├─ thinking -> true / false
└─ speaking -> true / false
↓
RTM Message
↓
客户端解析内层 payload 并根据 event_type 识别状态事件
状态消息类型
ConvoAI 会在智能体状态变化时发送 RTM Message。每一条消息只对应一个状态变化事件。
当前支持以下事件类型:
event_type | 说明 |
|---|---|
state.listening | 智能体开始或结束聆听用户输入。 |
state.thinking | 智能体开始或结束处理用户输入。 |
state.speaking | 智能体开始或结束播报语音。 |
消息结构
智能体状态消息的内层 message 为 JSON 字符串,反序列化后的结构如下:
{
"event_id": "xxxx",
"event_type": "state.listening",
"event_ms": 1611566412672,
"payload": {
"value": true,
"timestamp": 1611566412600
}
}
字段说明如下:
| 字段 | 类型 | 说明 |
|---|---|---|
event_id | string | 事件唯一 ID。 |
event_type | string | 状态事件类型,取值为 state.listening、state.thinking 或 state.speaking。 |
event_ms | integer | 事件发送时间戳,单位为毫秒。 |
payload.value | boolean | 当前状态值。true 表示进入该状态,false 表示退出该状态。 |
payload.timestamp | integer | 状态实际发生时间戳,单位为毫秒。 |
示例
智能体进入聆听状态时,内层 message 反序列化后的示例如下:
{
"event_id": "a1b2c3d4",
"event_type": "state.listening",
"event_ms": 1611566412672,
"payload": {
"value": true,
"timestamp": 1611566412600
}
}
智能体退出说话状态时,内层 message 反序列化后的示例如下:
{
"event_id": "e5f6g7h8",
"event_type": "state.speaking",
"event_ms": 1611566420000,
"payload": {
"value": false,
"timestamp": 1611566419900
}
}
前提条件
开始前,请确保你已经:
- 参考实现对话式智能体完成与智能体交互的基本流程。
- 完成 RTM SDK 的基础集成,并参考实现收发消息实现消息监听逻辑。
- 在创建智能体时启用 RTM 服务,并将
data_channel设置为rtm。
在开始监听智能体状态消息前,你需要先在创建智能体时启用 RTM 服务,并将数据通道设置为 RTM。
调用 POST 创建对话式智能体接口时,至少需要包含以下配置:
{
"properties": {
"advanced_features": {
"enable_rtm": true
},
"parameters": {
"data_channel": "rtm"
}
}
}
实现通过 RTM Message 获取智能体状态
完成 RTM 登录并订阅与智能体同名的频道后,你可以在 RTM 消息回调中解析状态消息。处理逻辑分为三步:
- 将收到的 RTM
message字段反序列化为状态事件对象。 - 检查反序列化结果中是否包含
event_type和payload等字段。 - 根据
event_type分发处理,并使用payload.value更新业务状态。
监听并解析状态消息
- Android
- iOS
- Web
val rtmConfig = RtmConfig()
rtmConfig.eventListener = object : RtmEventListener {
override fun onMessageEvent(event: MessageEvent) {
// 1. 反序列化内层 message(JSON 字符串)
val payload = JSONObject(event.message.data)
if (!payload.has("event_type") || !payload.has("payload")) {
return
}
val eventType = payload.getString("event_type")
val statePayload = payload.getJSONObject("payload")
val value = statePayload.getBoolean("value")
val timestamp = statePayload.getLong("timestamp")
// 2. 根据 event_type 分发处理
when (eventType) {
"state.listening" -> handleListeningChanged(value, timestamp)
"state.thinking" -> handleThinkingChanged(value, timestamp)
"state.speaking" -> handleSpeakingChanged(value, timestamp)
}
}
}
mRtmClient = RtmClient.create(rtmConfig)
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceiveMessageEvent event: AgoraRtmMessageEvent) {
// 1. 反序列化内层 message(JSON 字符串)
guard let jsonString = event.message.stringData,
let data = jsonString.data(using: .utf8),
let payload = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let eventType = payload["event_type"] as? String,
let statePayload = payload["payload"] as? [String: Any] else {
return
}
let value = statePayload["value"] as? Bool ?? false
let timestamp = statePayload["timestamp"] as? Int ?? 0
// 2. 根据 event_type 分发处理
switch eventType {
case "state.listening":
handleListeningChanged(value, timestamp)
case "state.thinking":
handleThinkingChanged(value, timestamp)
case "state.speaking":
handleSpeakingChanged(value, timestamp)
default:
break
}
}
rtm.addEventListener("message", (event) => {
// 1. 反序列化内层 message(JSON 字符串)
const payload = JSON.parse(event.message as string);
if (!payload.event_type || !payload.payload) {
return;
}
const { event_type, payload: statePayload } = payload;
// 2. 根据 event_type 分发处理
switch (event_type) {
case "state.listening":
handleListeningChanged(statePayload.value, statePayload.timestamp);
break;
case "state.thinking":
handleThinkingChanged(statePayload.value, statePayload.timestamp);
break;
case "state.speaking":
handleSpeakingChanged(statePayload.value, statePayload.timestamp);
break;
default:
break;
}
});
在上述示例中:
- 内层
message是 JSON 字符串,需要再次反序列化才能得到event_type和payload。 event_type用于判断具体的状态类型。payload.value表示进入(true)或退出(false)该状态。payload.timestamp可用于精确排序或日志记录。
更新业务状态
你可以根据接收到的事件更新 UI 或内部状态机,例如:
- 收到
state.listening且payload.value = true时,展示“正在听”。 - 收到
state.thinking且payload.value = true时,展示“思考中”。 - 收到
state.speaking且payload.value = true时,展示“说话中”。
RTM Message 与 RTM Presence 方案差异
ConvoAI 同时支持通过 RTM Message 和 RTM Presence 获取智能体状态。两种方案的核心差异如下:
| 对比项 | RTM Presence | RTM Message |
|---|---|---|
| 返回形式 | 状态键值对 | 状态事件消息 |
| 粒度 | 更适合读取当前状态 | 更适合处理每次状态变化 |
| 时间信息 | 一般只依赖消息到达时间 | 显式包含 event_ms 和 payload.timestamp |
| 适用场景 | 简单 UI 状态展示 | 事件驱动、日志记录、统一消息消费 |
可以按以下方式选择:
- 如果你只需要根据智能体当前是否处于
listening、thinking或speaking状态来更新界面,优先使用 RTM Presence,接入更直接。 - 如果你希望将状态变化与字幕、打断、性能指标等其他 RTM Message 一起统一处理,适合使用 RTM Message。
- 如果你需要记录每次状态变化的事件时间,或要对状态切换进行顺序分析,适合使用 RTM Message。
开发注意事项
- RTM 状态消息的内层
message字段是 JSON 字符串,需要再次反序列化后才能读取event_type和payload。 - 如果你已经通过 RTM Presence 驱动界面状态,切换到 RTM Message 时需要避免两套链路同时更新同一份 UI 状态,以免产生重复更新或状态抖动。
state.speaking为false并不一定等于端侧已经停止说话。当前speaking状态的结束时间由服务端根据 TTS 音频长度估算得出,因此在端侧实际播放结束时间与服务端估算时间存在偏差时,可能出现状态已经切换为false,但端侧仍在播放剩余音频的情况。- 旧版状态链路中,智能体状态可能通过 RTC Datastream 或 RTM Presence 的聚合态对外暴露。当前文档中的 RTM Message 状态事件,针对的是新状态链路中的
listening、thinking和speaking三类细粒度状态。
本文中的状态消息仅覆盖智能体状态事件,不包含手动 sos/eos 指令或相关回调。