聊天模式管理
名词解释
| 名词 | 解释 |
|---|---|
| AI 聊天模式 | AI 设备的交互模式,定义了用户如何与设备进行语音交互。包括按住模式、按键模式、唤醒词模式和自由对话模式。 |
| VAD | 语音活动检测(Voice Activity Detection),用于检测是否有语音输入。 |
功能简述
ai_manage_mode 是 TuyaOpen AI 应用框架中的聊天模式管理组件,负责管理不同的 AI 聊天模式,提供模式注册、初始化、切换和事件处理等功能。
模式管理
- 模式注册:支持注册多种聊天模式,每个模式提供独立的初始化和事件处理逻辑
- 模式切换:支持在不同模式之间切换,自动处理当前模式的去初始化和新模式的初始化
- 模式查询:支持查询当前模式、模式状态、模式是否已注册等信息
事件处理
- 用户事件处理:将用户事件转发给当前模式的处理器
- VAD 状态处理:将语音活动检测状态变化转发给当前模式(需启用音频组件)
- 按键事件处理:将按键事件转发给当前模式(需启用按键组件)
工作流程
模块架构图
模式注册流程
应用层注册聊天模式时,模块会检查模式是否已注册,创建模式控制结构并添加到模式列表中。
模式初始化流程
初始化指定模式时,模块会查找模式、创建互斥锁、调用模式的初始化函数,并设置当前模式。
模式切换流程
切换模式时,模块会去初始化当前模式,初始化目标模式,并发布模式切换事件。
事件处理流程
当有用户事件、VAD 状态变化或按键事件时,模块会将事件转发给当前模式的处理器。
模式状态机流程
每个模式都有自己的状态机,通过 task() 函数运行,状态包括:初始化、空闲、监听、上传、思考、说话。
开发流程
数据结构
聊天模式枚举
typedef enum {
AI_CHAT_MODE_HOLD, // 按住模式
AI_CHAT_MODE_ONE_SHOT, // 按键模式
AI_CHAT_MODE_WAKEUP, // 唤醒词模式
AI_CHAT_MODE_FREE, // 自由对话模式
AI_CHAT_MODE_CUSTOM_START = 0x100, // 自定义模式起始值
} AI_CHAT_MODE_E;
模式状态枚举
typedef enum {
AI_MODE_STATE_INIT, // 初始化状态
AI_MODE_STATE_IDLE, // 空闲状态
AI_MODE_STATE_LISTEN, // 监听状态
AI_MODE_STATE_UPLOAD, // 上传状态
AI_MODE_STATE_THINK, // 思考状态
AI_MODE_STATE_SPEAK, // 说话状态
AI_MODE_STATE_INVALID, // 无效状态
} AI_MODE_STATE_E;
模式抽象接口
typedef struct {
const char *name; // 模式名称
OPERATE_RET (*init) (void); // 初始化函数
OPERATE_RET (*deinit) (void); // 去初始化函数
OPERATE_RET (*task) (void *args); // 任务函数
OPERATE_RET (*handle_event) (AI_NOTIFY_EVENT_T *event); // 事件处理函数
AI_MODE_STATE_E (*get_state) (void); // 获取状态函数
OPERATE_RET (*client_run) (void *data); // 客户端回调函数
#if defined(ENABLE_COMP_AI_AUDIO) && (ENABLE_COMP_AI_AUDIO == 1)
OPERATE_RET (*vad_change) (AI_AUDIO_VAD_STATE_E vad_state); // VAD 状态变化处理
#endif
#if defined(ENABLE_BUTTON) && (ENABLE_BUTTON == 1)
OPERATE_RET (*handle_key) (TDL_BUTTON_TOUCH_EVENT_E event, void *arg); // 按键处理函数
#endif
} AI_MODE_HANDLE_T;
接口说明
注册聊天模式
注册一个聊天模式到模式管理器中。
- 模式注册的顺序决定了切换到下一个模式的切换顺序。
- 自定义模式的枚举值应从
AI_CHAT_MODE_CUSTOM_START开始,避免与内置模式冲突。
/**
* @brief Register an AI chat mode
* @param mode Chat mode to register
* @param handle Pointer to mode handle structure
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_register(AI_CHAT_MODE_E mode, AI_MODE_HANDLE_T *handle);
初始化聊天模式
初始化指定的聊天模式,使其成为当前活动模式。在切换模式前,确保目标模式已注册。
/**
* @brief Initialize a chat mode
* @param mode Chat mode to initialize
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_init(AI_CHAT_MODE_E mode);
去初始化当前模式
去初始化当前活动的聊天模式。
/**
* @brief Deinitialize current chat mode
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_deinit(void);
运行模式任务
执行当前模式的任务函数,通常用于状态机运行。
/**
* @brief Run current mode task
* @param args Task arguments
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_task_running(void *args);
处理用户事件
将用户事件转发给当前模式的处理器。
/**
* @brief Handle AI user event
* @param event Pointer to event structure
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_handle_event(AI_NOTIFY_EVENT_T *event);
获取模式状态
获取当前模式的状态。在模式未初始化时查询状态会返回 AI_MODE_STATE_INVALID。
/**
* @brief Get current mode state
* @return AI_MODE_STATE_E Current mode state
*/
AI_MODE_STATE_E ai_mode_get_state(void);
运行客户端回调
执行当前模式的客户端回调函数。
/**
* @brief Run client callback for current mode
* @param data Client data pointer
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_client_run(void *data);
处理 VAD 状态变化
将 VAD 状态变化转发给当前模式(需启用音频组件)。
#if defined(ENABLE_COMP_AI_AUDIO) && (ENABLE_COMP_AI_AUDIO == 1)
/**
* @brief Handle VAD (Voice Activity Detection) state change for current mode
* @param vad_state VAD state value
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_vad_change(AI_AUDIO_VAD_STATE_E vad_state);
#endif
处理按键事件
将按键事件转发给当前模式(需启用按键组件)。
#if defined(ENABLE_BUTTON) && (ENABLE_BUTTON == 1)
/**
* @brief Handle button key event
* @param event Button touch event
* @param arg Callback argument
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_handle_key(TDL_BUTTON_TOUCH_EVENT_E event, void *arg);
#endif
获取当前模式
获取当前活动的聊天模式。
/**
* @brief Get current chat mode
* @param mode Pointer to store current mode
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_get_curr_mode(AI_CHAT_MODE_E *mode);
切换模式
切换到指定的聊天模式。
/**
* @brief Switch to a different chat mode
* @param mode Target chat mode
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_mode_switch(AI_CHAT_MODE_E mode);
切换到下一个模式
切换到模式列表中的下一个模式。
/**
* @brief Switch to next chat mode in the list
* @return AI_CHAT_MODE_E Next mode value
*/
AI_CHAT_MODE_E ai_mode_switch_next(void);
获取模式状态字符串
将模式状态枚举转换为字符串。
/**
* @brief Get mode state string
* @param state Mode state
* @return char* State string
*/
char *ai_get_mode_state_str(AI_MODE_STATE_E state);
获取模式名称字符串
获取指定模式的名称字符串。
/**
* @brief Get mode name string
* @param mode Mode
* @return char* name string
*/
char *ai_get_mode_name_str(AI_CHAT_MODE_E mode);
检查模式是否已注册
检查指定的聊天模式是否已注册。
/**
* @brief Check if a chat mode is registered
* @param mode Chat mode to check
* @return bool Returns TRUE if registered, FALSE otherwise
*/
bool ai_mode_is_in_register_list(AI_CHAT_MODE_E mode);
获取第一个模式
获取模式列表中的第一个模式。
/**
* @brief Get the first chat mode in the list
* @param out_mode Pointer to store the first mode
* @return OPERATE_RET Operation result
*/
OPERATE_RET ai_get_first_mode(AI_CHAT_MODE_E *out_mode);
开发步骤
- 注册模式:在应用启动时,调用
ai_mode_register()注册所有需要的聊天模式 - 初始化模式:调用
ai_mode_init()初始化默认模式 - 运行模式任务:在任务循环中调用
ai_mode_task_running()运行当前模式的状态机 - 处理事件:将用户事件、VAD 状态变化、按键事件等转发给模式管理器
- 切换模式:根据需求调用
ai_mode_switch()或ai_mode_switch_next()切换模式
参考示例
注册和初始化模式
#include "ai_manage_mode.h"
// 注册所有模式
OPERATE_RET register_all_modes(void)
{
OPERATE_RET rt = OPRT_OK;
// 注册按住模式
TUYA_CALL_ERR_RETURN(ai_mode_hold_register());
// 注册按键模式
TUYA_CALL_ERR_RETURN(ai_mode_oneshot_register());
// 注册唤醒词模式
TUYA_CALL_ERR_RETURN(ai_mode_wakeup_register());
// 注册自由对话模式
TUYA_CALL_ERR_RETURN(ai_mode_free_register());
return rt;
}
// 初始化默认模式
OPERATE_RET init_default_mode(void)
{
OPERATE_RET rt = OPRT_OK;
AI_CHAT_MODE_E default_mode = AI_CHAT_MODE_HOLD;
// 初始化默认模式
TUYA_CALL_ERR_RETURN(ai_mode_init(default_mode));
return rt;
}
模式切换
// 切换到指定模式
void switch_to_mode(AI_CHAT_MODE_E mode)
{
OPERATE_RET rt = ai_mode_switch(mode);
if (OPRT_OK == rt) {
PR_NOTICE("切换到模式: %s", ai_get_mode_name_str(mode));
} else {
PR_ERR("切换模式失败: %d", rt);
}
}
// 切换到下一个模式
void switch_to_next_mode(void)
{
AI_CHAT_MODE_E next_mode = ai_mode_switch_next();
PR_NOTICE("切换到下一个模式: %s", ai_get_mode_name_str(next_mode));
}
查询模式信息
void query_mode_info(void)
{
AI_CHAT_MODE_E current_mode;
AI_MODE_STATE_E current_state;
// 获取当前模式
if (OPRT_OK == ai_mode_get_curr_mode(¤t_mode)) {
PR_NOTICE("当前模式: %s", ai_get_mode_name_str(current_mode));
}
// 获取当前状态
current_state = ai_mode_get_state();
PR_NOTICE("当前状态: %s", ai_get_mode_state_str(current_state));
// 检查模式是否已注册
if (ai_mode_is_in_register_list(AI_CHAT_MODE_WAKEUP)) {
PR_NOTICE("唤醒词模式已注册");
}
}
事件处理
// 处理用户事件
void handle_user_event(AI_NOTIFY_EVENT_T *event)
{
ai_mode_handle_event(event);
}
#if defined(ENABLE_COMP_AI_AUDIO) && (ENABLE_COMP_AI_AUDIO == 1)
// 处理 VAD 状态变化
void handle_vad_change(AI_AUDIO_VAD_STATE_E vad_state)
{
ai_mode_vad_change(vad_state);
}
#endif
#if defined(ENABLE_BUTTON) && (ENABLE_BUTTON == 1)
// 处理按键事件
void handle_key_event(TDL_BUTTON_TOUCH_EVENT_E event, void *arg)
{
ai_mode_handle_key(event, arg);
}
#endif
运行模式任务
// 在任务循环中运行模式任务
void mode_task_loop(void *args)
{
while (1) {
// 运行当前模式的任务
ai_mode_task_running(args);
// 延时
tal_system_sleep(10);
}
}
自定义模式实现
// 自定义模式的状态变量
static AI_MODE_STATE_E sg_custom_mode_state = AI_MODE_STATE_IDLE;
// 自定义模式初始化
static OPERATE_RET custom_mode_init(void)
{
PR_NOTICE("自定义模式初始化");
sg_custom_mode_state = AI_MODE_STATE_IDLE;
return OPRT_OK;
}
// 自定义模式去初始化
static OPERATE_RET custom_mode_deinit(void)
{
PR_NOTICE("自定义模式去初始化");
return OPRT_OK;
}
// 自定义模式任务
static OPERATE_RET custom_mode_task(void *args)
{
// 实现模式的状态机逻辑
switch (sg_custom_mode_state) {
case AI_MODE_STATE_IDLE:
// 空闲状态处理
break;
case AI_MODE_STATE_LISTEN:
// 监听状态处理
break;
// ... 其他状态
default:
break;
}
return OPRT_OK;
}
// 自定义模式事件处理
static OPERATE_RET custom_mode_handle_event(AI_NOTIFY_EVENT_T *event)
{
// 处理用户事件
return OPRT_OK;
}
// 获取自定义模式状态
static AI_MODE_STATE_E custom_mode_get_state(void)
{
return sg_custom_mode_state;
}
// 注册自定义模式
OPERATE_RET register_custom_mode(void)
{
AI_MODE_HANDLE_T handle = {
.name = "Custom Mode",
.init = custom_mode_init,
.deinit = custom_mode_deinit,
.task = custom_mode_task,
.handle_event = custom_mode_handle_event,
.get_state = custom_mode_get_state,
};
return ai_mode_register(AI_CHAT_MODE_CUSTOM_START, &handle);
}