事件系统 (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: stringtype: 'client' | 'aiWorker'id: stringcompleted: 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: 包含以下字段的对象:
| 字段 | 类型 | 说明 |
|---|---|---|
available | boolean | 设备是否可用 |
error? | AudioDeviceError | 错误类型(仅当 available=false 时) 可能值: BROWSER_NOT_SUPPORTED、HTTPS_REQUIRED、DEVICE_ENUMERATION_FAILED、PERMISSION_DENIED、DEVICE_NOT_READABLE、DEVICE_CHECK_TIMEOUT |
userMessage? | string | 用户友好的错误提示文案(可直接用于 UI 展示) |
devices? | MediaDeviceInfo[] | 检测到的音频设备列表 |
permissionState? | PermissionState | 权限状态('granted'|'denied'|'prompt') |
⚠️ 注意事项:
userMessage是经过优化的用户提示,可直接用于 UI 展示- 如果
available=true,error和userMessage为undefined 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: 包含 text、sessionId、timestamp 的对象
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 包含可选的 requestId、queryId、sessionId
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);
}
});
事件监听最佳实践
- 必须监听的事件:
ready、error - 推荐监听的事件:
conversation_add、is_rendering_change - 按需监听的事件: 其他事件根据具体功能需求选择
- 记得移除监听器: 组件销毁时
dispose()会自动移除事件监听,避免内存泄漏
// 移除所有事件监听器
controller.emitter.clearListeners();
// 或移除特定事件监听器
controller.emitter.off('ready', readyHandler);