Skip to main content

事件系统 (Events)

AaaS Pilot Kit 基于事件驱动架构,通过 controller.emitter 提供丰富的事件监听能力。

生命周期事件

ready

🚀 【一次性】数字员工初始化完成,可安全调用其他 API - 在数字员工形象加载完成、ASR初始化完成后触发。

最佳实践: 在 'ready' 后再调用 playXXX / input 等方法,避免竞态。

Payload: 无参数

controller.emitter.on('ready', () => {
console.log('数字人已就绪');
controller.input('你好!');
});

error

【必须监听】系统发生错误 - 覆盖 ASR / Agent / TTS / 网络 / 渲染等全链路异常。

🚨 生产环境务必监听并处理!建议上报监控系统 + 用户友好提示

Payload: IErrorEventPayload 包含:

  • code: 错误唯一标识,如 'ASR_TIMEOUT'、'AGENT_UNAUTHORIZED'
  • message: 人类可读错误描述,可用于日志或用户提示
  • stack?: 错误堆栈(开发环境建议保留,生产可选上报)
  • actionRequired: 用户或开发者需采取的操作,如 "请检查麦克风权限"、"稍后重试"
  • severity: 'low' | 'medium' | 'high' | 'critical' —— 用于告警分级
  • metadata: 错误上下文(时间、会话ID、用户ID、服务模块等,便于追踪)
  • originalError?: 原始 Error 对象(内部调试用,生产环境可忽略)

响应建议:

  • low/medium → 记录日志 + 局部提示
  • high/critical → 中断流程 + 弹窗提示 + 上报运维
controller.emitter.on('error', (error) => {
console.error('系统错误:', error);

if (error.severity === 'critical') {
showErrorDialog(error.message);
reportToMonitoring(error);
}
});

对话相关事件

conversation_add

💬 【高频】新增对话消息(用户 or AI) - 每当有新对话内容产生时触发(ASR识别结果 / Agent回复)。

适用场景: 聊天记录展示、日志记录、数据分析

Payload: AnyConversation - 包含角色(client/aiWorker)、内容、时间戳等

controller.emitter.on('conversation_add', (conversation) => {
console.log('新对话:', conversation);
addToChatHistory(conversation);
});

conversation_change

📝 【高频】当前对话片段状态更新(流式播报中) - 用于实时更新"正在输入…"动画或打字机效果。

适用场景: UI 实时渲染流式回复、进度提示

Payload: 包含以下字段的对象:

  • text: string
  • type: 'client' | 'aiWorker'
  • id: string
  • completed: boolean - true 表示该片段播报结束(气泡完成渲染)
controller.emitter.on('conversation_change', (update) => {
if (update.completed) {
console.log('片段完成:', update.text);
}
else {
console.log('流式更新:', update.text);
}
});

reply_start

Agent 开始回复(数字人服务处理完毕) - 在数字人服务收到流式文本并完成预处理(TTS/动作合成)准备开始播报前触发。

适用场景: 统计首字延迟(Latency)、调试性能

Payload: IReplyStartEventPayload 包含:

  • agentResponse: Agent 的响应对象
  • latency?: 数字人服务处理耗时(毫秒),从收到文本流到准备播报的时间(不含 LLM 耗时)
  • requestId: 本次请求的追踪 ID
controller.emitter.on('reply_start', (payload) => {
console.log('准备播报, Latency:', payload.latency, 'ms');

// 配合 ttft 事件可分析全链路耗时:
// LLM耗时 ≈ ttft.totalLatency - payload.latency
});

ttft

⏱️ Agent 首字延迟性能指标 - 首次收到 Agent 响应内容时触发,包含 Agent 生成第一个字符的性能数据。

适用场景: 性能监控、首字延迟分析。

Payload: ITtftEventPayload 包含:

  • firstToken: Agent 返回的首个字符。
  • timestamp: 首字返回的时间戳。
  • sessionId: 当前会话 ID。
  • queryId?: 当前轮的查询 ID。
  • totalLatency?: 从用户输入到收到首字的总延迟(毫秒),由 Controller 层计算并填充。
controller.emitter.on('ttft', (payload) => {
console.log('Agent 首字:', payload.firstToken);
console.log('首字时间戳:', new Date(payload.timestamp));
if (payload.totalLatency) {
console.log('总延迟:', payload.totalLatency, 'ms');
}
});

语音识别事件

asr_start

🎙️ ASR 音频采集开始(用户开始说话) - 通常在 VAD 检测到语音活动后触发。

适用场景: 显示"正在听…"动画、启动打断检测,等等...

Payload: 无参数

controller.emitter.on('asr_start', () => {
showListeningAnimation();
});

asr_message

🎙️ 【高频流式】ASR 实时识别结果(逐句/逐词推送) - 用户说话过程中持续触发,用于实现实时字幕、语音预览、热词高亮等。

触发时机:

  • 每当语音识别引擎输出一个"中间片段"或"最终句子"时触发
  • 由 VAD(语音活动检测)和语义断句策略共同驱动

使用场景:

  • 实时显示"用户正在说:xxx..."
  • 热词替换/敏感词标记(配合 opts.hotWordReplacementRules
  • 打断检测(当 completed=false 但停顿过长,可预判打断)

Payload: IAsrMessageEventPayload 包含:

  • text: 当前识别出的文本片段(可能不完整)
  • completed: true=本句识别结束(可送入 Agent),false=仍在说话(中间结果)
  • id: 本条语音片段唯一标识(可用于去重或追踪)
  • sessionId: 当前会话 ID(用于多路对话隔离)

⚠️ 注意:

  • 中间结果(completed=false)可能被后续结果覆盖,勿直接用于业务逻辑
  • 只有 completed=true 的文本才应提交给 Agent 或记录到对话历史
controller.emitter.on('asr_message', (payload) => {
if (payload.completed) {
console.log('最终识别:', payload.text);
sendToAgent(payload.text);
}
else {
console.log('中间结果:', payload.text);
showPreview(payload.text);
}
});

microphone_available

🎙️ 【设备检测】麦克风设备可用性检测结果 - 执行音频设备检测后触发,提供用户友好的错误提示。

触发时机:

  • 手动调用 controller.checkAudioDevice()
  • ASR 启动前自动检测(需配置 checkAudioDeviceBeforeStart: true

使用场景:

  • 实时显示设备状态提示:"麦克风不可用,请检查设备"
  • 根据不同错误类型展示针对性解决方案
  • 采集设备统计数据(设备数量、权限状态等)

Payload: 包含以下字段的对象:

字段类型说明
availableboolean设备是否可用
error?AudioDeviceError错误类型(仅当 available=false 时)
可能值:BROWSER_NOT_SUPPORTEDHTTPS_REQUIREDDEVICE_ENUMERATION_FAILEDPERMISSION_DENIEDDEVICE_NOT_READABLEDEVICE_CHECK_TIMEOUT
userMessage?string用户友好的错误提示文案(可直接用于 UI 展示)
devices?MediaDeviceInfo[]检测到的音频设备列表
permissionState?PermissionState权限状态('granted'|'denied'|'prompt')

⚠️ 注意事项:

  • userMessage 是经过优化的用户提示,可直接用于 UI 展示
  • 如果 available=trueerroruserMessageundefined
  • permissionState 可能为 undefined(Safari 不支持 Permissions API)

示例代码:

// 监听设备检测结果
controller.emitter.on('microphone_available', (result) => {
if (!result.available) {
// 直接显示用户友好的错误消息
alert(result.userMessage);

// 或使用 Toast/Modal 等 UI 组件
showToast({
type: 'error',
message: result.userMessage
});

// 根据错误类型做特殊处理
if (result.error === 'PERMISSION_DENIED') {
showPermissionGuide(); // 引导用户开启权限
}
else if (result.error === 'HTTPS_REQUIRED') {
showHTTPSWarning(); // 提示需要 HTTPS
}
}
else {
// 设备可用
console.log('麦克风可用,找到', result.devices?.length, '个音频设备');
console.log('权限状态:', result.permissionState);
}
});

// 手动触发设备检测
await controller.checkAudioDevice();

错误类型详细说明: 参见 FAQ - 错误代码表 中的 3100-3105 错误码

相关配置: checkAudioDeviceBeforeStart - ASR 启动前自动检测设备

相关方法: checkAudioDevice() - 手动触发设备检测

渲染播报事件

render_start

🎬 【高频】数字员工开始播报某段文本 - 每次开始播放一个句子/片段时触发。

适用场景: 同步播报字幕、埋点统计播报内容

Payload: 包含 textsessionIdtimestamp 的对象

controller.emitter.on('render_start', (payload) => {
showSubtitle(payload.text);
logPlaybackEvent(payload);
});

is_rendering_change

🖼️ 【状态同步】数字员工渲染/播报状态变更 - 实时反映是否"正在说话中"。

核心用途:

  • 控制 UI:显示"正在播报…"加载态、禁用发送按钮
  • 体验优化:避免用户在播报中途误操作导致体验割裂

Payload: boolean

  • true → 数字人正在渲染口型 + 播放语音(播报中)
  • false播报完成,空闲可交互(此时可安全输入或执行后续动作)

⚠️ 注意: 此状态仅反映"数字员工是否在播报",不反映 ASR 或 Agent 是否在工作

controller.emitter.on('is_rendering_change', (isRendering) => {
if (isRendering) {
showPlayingState();
disableSendButton();
}
else {
hidePlayingState();
enableSendButton();
}
});

控制事件

mute

🔇 静音状态变更 - 当调用 .mute(true/false) 时触发。

适用场景: 同步 UI 静音按钮状态

Payload: boolean - true=已静音,false=取消静音

controller.emitter.on('mute', (isMuted) => {
console.log('静音状态:', isMuted);
updateMuteButton(isMuted);
});

interrupt

🛑 对话被打断(用户主动 or 系统触发) - 调用 .interrupt() 或语音打断时触发。

适用场景: 停止动画、清除输入框、重置状态

Payload: IInterruptEventPayload 包含可选的 requestIdqueryIdsessionId

controller.emitter.on('interrupt', (payload) => {
stopAllAnimations();
clearInputBox();
resetConversationState();
});

会话管理事件

inactivity

⏸️ 【低频】用户长时间无交互(触发 opts.inactivityPrompt - 在配置的不活跃时间后触发,数字人将播报提醒语。

适用场景: 挽留用户、自动结束会话前提示

Payload: 包含 requestId 的对象

controller.emitter.on('inactivity', (payload) => {
console.log('用户无活动,播报提示');
showInactivityWarning();
});

busy

🚧 【低频】系统繁忙(资源达到上限) - 数字人并发路数 / Agent QPS / RTC 带宽超限。

⚠️ 需引导用户"稍后再试"或"联系客服"

载荷: IBusyEventPayload

controller.emitter.on('busy', (payload) => {
showBusyMessage('系统当前繁忙,请稍后再试');
});

完整的错误处理示例

在生产环境中始终监听 error 事件:

controller.emitter.on('error', (error) => {
// 记录错误
console.error('AaaS Pilot Kit 错误:', {
code: error.code,
message: error.message,
severity: error.severity,
metadata: error.metadata
});

// 根据严重程度处理
switch (error.severity) {
case 'low':
case 'medium':
// 显示轻微通知
showToast(error.message);
break;

case 'high':
case 'critical':
// 显示明显的错误对话框
showErrorDialog({
title: '服务错误',
message: error.actionRequired || error.message,
action: '重试'
});

// 上报监控
reportError(error);
break;
}
});

实时字幕实现示例

结合多个事件实现完整的实时字幕功能:

// 用于最终字幕
controller.emitter.on('conversation_add', (payload) => {
// 创建新的回复气泡
createNewResponseBubble(payload.text);
});

// 用于数字员工播报字幕的实时打字效果
controller.emitter.on('conversation_change', (update) => {
updateResponseBubbleTypingText(update.text, update.completed);
});

// 用户语音实时显示
controller.emitter.on('asr_message', (payload) => {
if (payload.completed) {
showUserSpeech(payload.text);
}
else {
showUserSpeechPreview(payload.text);
}
});

事件监听最佳实践

  1. 必须监听的事件: readyerror
  2. 推荐监听的事件: conversation_addis_rendering_change
  3. 按需监听的事件: 其他事件根据具体功能需求选择
  4. 记得移除监听器: 组件销毁时 dispose() 会自动移除事件监听,避免内存泄漏
// 移除所有事件监听器
controller.emitter.clearListeners();

// 或移除特定事件监听器
controller.emitter.off('ready', readyHandler);