SDM(Smart Device Model) 开发,更多 SDM 相关可以 查看 SDM 文档。详见 面板小程序 > 搭建环境。
产品名称:端智能体
准备好 智能生活 OEM App 和智能产品(可选)后,需要在 智能体平台 上创建智能体。








由于产品定义了面板和设备所拥有的功能点与智能体,所以在开发一个智能设备面板之前,首先需要创建一个 AI 毛绒玩具产品,定义产品有哪些功能点、携带哪些智能体,然后再在面板中一一实现这些功能点。
首先,注册登录 涂鸦开发者平台,并在平台创建产品:










面板小程序的开发在 小程序开发者 平台上进行操作,首先请前往 小程序开发者平台 完成平台的注册登录。
详细操作步骤可以参考 创建面板小程序。
打开 IDE 创建一个基于 AI 智能体小程序模板 的面板小程序项目,需要在 Tuya MiniApp IDE 上操作。
详细操作步骤可以参考 初始化项目工程。
完成以上步骤后,一个面板小程序的开发模板初始化完成。以下为工程目录的介绍:
├── src
│  	├── api // 面板所有云端 API 请求聚合文件
│  	├── components
│  	│   ├── AICard // 智能体卡片组件
│  	│   ├── DialogConfirm // 确认弹窗组件
│  	│   ├── DialogInput // 文本输入弹窗组件
│  	│   ├── DialogPicker // DP 选择弹窗组件
│  	│   ├── GridBattery // 电池电量组件
│  	│   ├── icon-font // svg 图标容器组件
│  	│   ├── Modal // 弹窗通用组件
│  	│   ├── NoData // 无数据兜底组件
│  	│   ├── PickerItem // 选择按钮通用组件
│  	│   ├── SearchBar // 搜索组件
│  	│   ├── Tag // 分类标签子组件
│  	│   ├── TagBar // 分类标签组件
│  	│   ├── Text // 通用文本组件
│  	│   ├── TopBar // 通用 TopBar 组件
│  	│   ├── TouchableOpacity // 通用按钮组件
│  	├── constant
│  	│   ├── dpCodes.ts // dpCode 常量
│  	│   ├── index.ts // 存放所有的常量配置
│  	├── devices // 设备模型
│  	├── hooks // hooks
│  	├── i18n // 多语言
│  	├── pages
│   │   ├── AIDialogue // 智能体列表页面
│   │   ├── AvatarSelect // 头像选择
│   │   ├── CustomAgentEdit // 智能体角色新增和编辑
│   │   ├── DialogHistory // 单个智能体会话历史记录页面
│   │   ├── home // 首页
│   │   ├── VoiceSetting // 单个音色编辑页面
│   │   ├── VoiceSquare // 音色广场页面
│  	├── redux // Redux
│   ├── res // 图片资源 & SVG 相关
│   ├── styles // 全局样式
│   ├── types // 全局类型定义
│   ├── utils // 业务常用工具方法
│   ├── app.config.ts
│   ├── app.less
│   ├── app.tsx
│   ├── composeLayout.tsx // 处理监听子设备添加、解绑、DP 变化等
│   ├── global.config.ts
│   ├── mixins.less // less mixins
│   ├── routes.config.ts // 配置路由
│   ├── variables.less // less variables
 
 
使用 getAIAgentRoles API 方法获取智能体角色列表,目前角色数量上限为 10 个。
// 角色列表请求函数
export const getAIAgentRoles = async () => {
  try {
    // getAgentRoles
    const response = await getAI2AgentRoles({
      devId: getDevInfo().devId,
      pageNo: 1,
      pageSize: 10,
      panelCode: 'ai_platform',
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const getBoundAgentsFunc = async () => {
  getAIAgentRoles()
    .then((res: any) => {
      const { list: boundAgentList } = res;
      console.log('==55boundAgentList', boundAgentList);
      dispatch(updateRoleList([...boundAgentList]));
      setRefresherTriggeredState(false);
    })
    .catch(err => {
      setInitLoading(false);
      console.log('getBoundAgentsFunc::err::', err);
      setRefresherTriggeredState(false);
    });
};
点击智能体会话卡片右上角的 绑定到设备 按钮后,智能体角色将和设备进行绑定,面板顶部的默认智能体信息展示区域将会自动切换为该智能体角色的对应内容,同时该智能体会话卡片将自动排序为第一个。完成上述绑定操作后,C 端用户在与设备交互时,将感知到最新的智能体角色相关内容。
export const bindAIAgentRoles = async (roleId: string) => {
  try {
    // bindAgentRoles
    const response = await bindAI2AgentRoles({
      devId: getDevInfo().devId,
      roleId,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const bindAIRole = (roleId: string, inBind: boolean) => {
  if (inBind) return;
  showLoading({
    title: '',
  });
  bindAIAgentRoles(roleId)
    .then(() => {
      hideLoading();
      showToast({
        title: Strings.getLang('dsc_binding_success'),
        icon: 'success',
      });
      emitter.emit('refreshDialogData', '');
    })
    .catch(() => {
      hideLoading();
      showToast({
        title: Strings.getLang('dsc_binding_fail'),
        icon: 'error',
      });
    });
};
export const deleteAIAgentRoles = async (roleId: string) => {
  try {
    // deleteAgentRoles 
    const response = await deleteAI2AgentRoles({
      devId: getDevInfo().devId,
      roleId,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const deleteAIRole = (roleId: string) => {
  showLoading({
    title: '',
  });
  deleteAIAgentRoles(roleId)
    .then(() => {
      hideLoading();
      showToast({
        title: Strings.getLang('dsc_delete_success'),
        icon: 'success',
      });
      emitter.emit('refreshDialogData', '');
    })
    .catch((err) => {
      hideLoading();
      console.log('deleteAIAgentRoles::err::', err);
      showToast({
        title: Strings.getLang('dsc_delete_fail'),
        icon: 'error',
      });
    });
  setUnbindId('-1');
  setIsShowUnbindConfirm(false);
};

角色会话记录展示页主要用于展示 C 端用户与智能体角色的对话记录,当 C 端用户暂未与智能体产生对话内容时,模板将会展示空内容提示图文。用户点击智能体会话记录的顶部的卡片后,便可跳转至角色编辑页面。
// 获取角色会话记录
export const getDialogHistoryList = async (params: GetHistoryParams) => {
  try {
    // getAIAgentHistory 
    const response = await getAI2AgentEndpointHistory({
      devId: getDevInfo().devId,
      roleId: params.roleId,
      pageNo: params.pageNo,
      pageSize: params.pageSize,
      startTime: dayjs().subtract(3, 'months').format('YYYY-MM-DD HH:mm:ss'),
      endTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const [historyList, setHistoryList] = useState([]);
const getDialogHistoryListFunc = useCallback((params: GetListParams) => {
    return new Promise((resolve, reject) => {
      getDialogHistoryList({
        pageNo: params.current,
        pageSize: params.pageSize,
        roleId,
        startTime: params.startTime,
        endTime: params.endTime,
      })
        .then((res: DialogHistoryRes) => {
          const { totalPage, list } = res;
          resolve({
            total: totalPage,
            list,
          });
        })
        .catch(error => {
          reject(error);
        });
    });
  }, []);
  const { pagination, data, run } = usePagination(
    ({ current, pageSize, startTime, endTime }) =>
      getDialogHistoryListFunc({
        current,
        pageSize,
        startTime,
        endTime,
      }) as Promise<GetDialogHistory>,
    {
      manual: true,
    }
  );
该功能主要用于删除一组或多组角色的聊天信息。
// 清除上下文接口 clearAgentHistoryMessage
export const clearAgentHistoryMessage = async (params: any) => {
  try {
    // deleteAIAgentHistoryMessage 
    const response = await deleteAI2AgentEndpointHistory({
      devId: getDevInfo().devId,
      ...params,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const handleDeleteMessage = async () => {
  try {
    showLoading({ title: '' });
    await clearAgentHistoryMessage({
      roleId,
      requestIds: selectedItems?.join(','), // requestId 就是 docId
    });
    const newList = historyList.filter(item => !selectedItems.includes(item.requestId));
    setHistoryList(newList);
    hideLoading();
    showToast({
      title: Strings.getLang('dsc_delete_chat_history_tip_new'),
      icon: 'success',
    });
  } catch (err) {
    hideLoading();
    showToast({
      title: Strings.getLang('dsc_delete_chat_history_failed'),
      icon: 'error',
    });
  }
};
该功能主要用于清除 C 端用户与智能体角色之间的聊天记录,并初始化该智能体角色的聊天内容记录。
// 清除历史聊天记录接口
export const clearingHistoryRecord = async (roleId: string) => {
  try {
    // deleteAIAgentHistory 
    const response = await clearAI2AgentEndpointHistory({
      devId: getDevInfo().devId,
      roleId,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const handleClearingHistoryRecord = () => {
  showModal({
    title: '',
    content: Strings.getLang('dsc_delete_chat_history'),
    confirmText: Strings.getLang('delete'),
    cancelText: Strings.getLang('dsc_cancel'),
    success: ({ confirm }) => {
      if (confirm) {
        showLoading({
          title: '',
        });
        clearingHistoryRecord(roleId)
          .then(() => {
            hideLoading();
            showToast({
              title: Strings.getLang('dsc_delete_chat_history_tip'),
              icon: 'success',
            });
            navigateBack({});
            setTimeout(() => {
              emitter.emit('refreshHistoryData', '');
            }, 2000);
          })
          .catch(() => {
            hideLoading();
            showToast({
              title: Strings.getLang('dsc_delete_chat_history_failed'),
              icon: 'error',
            });
          });
      }
    },
  });
};
 
 
角色新增/编辑页面主要用于修改角色信息(头像、名称,角色介绍、记忆体),以及选择音色、语种、和大模型。
B 端可以预设一些模板,提供给用户选择,简化用户操作流程。
//获取角色模板列表
export const getAIAgentRolesTemplateList = async () => {
  try {
    // getAgentRolesTemplates 
    const response = await getAI2AgentRolesTemplates({ devId: getDevInfo().devId, panelCode: 'ai_platform' });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const getTemplateList = async () => {
  getAIAgentRolesTemplateList()
    .then((res: any) => {
      console.log('==getTemplateList', res);
      setTemplateList(res);
      if (res?.length > 0) {
        setTemplateRoleId(res[0].roleId);
      }
    })
    .catch(err => {
      console.log('getTemplateList::err::', err);
    });
};
//获取角色模板详情
export const getAIAgentRolesTemplatesDetail = async (roleId: string) => {
  try {
    // getAgentRolesTemplatesDetail 
    const response = await getAI2AgentRolesTemplatesDetail({ devId: getDevInfo().devId, roleId });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const getTemplateListDetail = async (roleId: string) => {
  getAIAgentRolesTemplatesDetail(roleId)
    .then((res: any) => {
      console.log('==getTemplateListDetail', res);
      initRoleData(res);
      initRoleInfoData();
    })
    .catch(err => {
      console.log('getTemplateListDetail::err::', err, roleId);
    });
};
编辑角色时,获取当前角色详情。
export const getAIAgentRoleDetail = async (roleId: string) => {
  try {
    // getAgentRolesDetail 
    const response = await getAI2AgentRolesDetail({
      devId: getDevInfo().devId,
      roleId,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const getAgentRoleDetail = async (roleId: string) => {
  getAIAgentRoleDetail(roleId)
    .then((res: any) => {
      console.log('==getAIAgentRoleDetail', res);
      initRoleData(res);
      initRoleInfoData();
    })
    .catch(err => {
      console.log('getAIAgentRoleDetail::err::', err);
    });
};
创建/编辑角色。
// 创建角色
export const createRole = async (params: any) => {
  try {
    // postCreateAgentRole 
    const response = await addAI2AgentRoles({
      devId: getDevInfo().devId,
      ...params,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
// 编辑角色
export const updateRole = async (params: any) => {
  try {
    // postUpdateAgentRole 
    const response = await updateAI2AgentRoles({
      devId: getDevInfo().devId,
      ...params,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const handleSave = async () => {
  if (!name || nameErrorMessage) {
    showToast({
      title: `${Strings.getLang('please_complete')}${Strings.getLang('role_name')}`,
      icon: 'error',
    });
    return;
  }
  if (!introduction) {
    showToast({
      title: `${Strings.getLang('please_complete')}${Strings.getLang('introduction')}`,
      icon: 'error',
    });
    return;
  }
  if (!roleInfo?.voiceId && !selectedVoiceId) {
    showToast({
      title: `${Strings.getLang('please_complete')}${Strings.getLang('role_voice')}`,
      icon: 'error',
    });
    return;
  }
  if (!selectedModel) {
    showToast({
      title: `${Strings.getLang('please_complete')}${Strings.getLang('model')}`,
      icon: 'error',
    });
    return;
  }
  const params = {
    roleName: name,
    roleIntroduce: introduction,
    roleImgUrl: roleInfo?.roleImgUrl || roleImgUrl,
    useLangCode: roleInfo?.useLangId || selectedLanguageCode,
    useTimbreId: roleInfo?.voiceId || selectedVoiceId,
    useLlmId: selectedModel,
    memoryInfo: memoryInfo,
  }
  try {
    if (roleId) {
      params.roleId = roleId
      await updateRole(params);
    } else {
      await createRole(params);
    }
    hideLoading();
    showToast({
      title: Strings.getLang('save_success'),
      icon: 'success',
    });
    emitter.emit('refreshDialogData', '');
    navigateBack({ delta: 9999 });
  } catch (error) {
    console.log('createRole/updateRole:::', error);
    hideLoading();
    showToast({
      title: Strings.getLang('save_failed'),
      icon: 'error',
    });
  }
};
B 端可以预设一些头像,提供给用户选择。
// 头像
export const getAIAvatars = async () => {
  try {
    // getAgentAvatars
    const response = await getAI2AgentAvatars({
      devId: getDevInfo().devId,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const getBoundAgentsFunc = async () => {
  getAIAvatars()
    .then(res => {
      console.log('getAIAvatars::', res);
      setAvatarList(res);
    })
    .catch(err => {
      console.log('getAIAvatars::err::', err);
    });
};
获取支持的语言语种。
// 获取支持的语言语种
export const getAgentLanguages = async () => {
  try {
    // getAgentLanguageConfig 
    const response = await getAI2AgentConfigLangList({
      devId: getDevInfo().devId,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const useAgentLanguages = (id: number) => {
  const [langRangeList, setSupportLangs] = useState<Array<{ key: string; dataString: string }>>([]);
  useEffect(() => {
    const fetchSupportedLangs = async () => {
      try {
        const response = await getAgentLanguages();
        if (response) {
          setSupportLangs(
            response?.map(item => ({
              dataString: item?.langName,
              key: item?.langCode,
            }))
          );
        }
      } catch (error) {
        console.error('Failed to fetch supported languages:', error);
      }
    };
    fetchSupportedLangs();
  }, []);
  return { langRangeList };
};
import useAgentLanguages from '@/hooks/useAgentLanguages';
const { langRangeList } = useAgentLanguages(id);
获取大模型列表。
// 获取大模型列表
export const getAgentModels = async () => {
  try {
    // getAgentModelConfig
    const response = await getAI2AgentConfigLlmList({
      devId: getDevInfo().devId,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const fetchSupportedModels = async () => {
  try {
    const response = await getAgentModels();
    console.log('getAgentModels:::', response);
    if (response) {
      const models = response?.map(model => ({
        key: model.llmId,
        dataString: model.llmName,
      }));
      setModelList(models);
    }
  } catch (error) {
    console.error('Failed to fetch supported models:', error);
  }
};

音色管理页面主要用于切换智能体角色所携带的音色信息,主要包含系统默认音色管理。

音色广场列表主要用于展示系统默认音色列表,C 端用户可在广场内挑选适合自己的音色内容赋能给智能体角色。在筛选音色方面,模板支持分类筛选、搜索筛选功能。
// 获取标准音色列表
export const getStandardVoiceList = async (params: any) => {
  try {
    // getMarketList 
    const response = await getAI2TimbreMarketList({
      devId: getDevInfo().devId,
      pageNo: params.pageNo,
      pageSize: params.pageSize,
      tag: '',
      keyWord: params.keyWord,
      lang: params.lang,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
// 音色列表请求函数
const getVoiceListFunc = (params: GetListParams) => {
  return new Promise((resolve, reject) => {
    getStandardVoiceList({
      pageNo: params.current,
      pageSize: params.pageSize,
      tag,
      agentId,
      keyWord: params.searchText,
      lang: selectedLang,
    })
      .then((res: VoiceRes) => {
        const { totalPage, list = [] } = res;
        hideLoading();
        setLoading(false);
        resolve({
          total: totalPage,
          list,
        });
      })
      .catch(error => {
        hideLoading();
        setLoading(false);
        reject(error);
      });
  });
};
 // 音色列表请求管理
const { pagination, data, run } = usePagination(
  ({ current, pageSize, searchText }) =>
    getVoiceListFunc({ current, pageSize, searchText }) as Promise<GetStandardVoice>,
  {
    manual: true,
  }
);
// 响应懒加载
useEffect(() => {
  if (data?.list) {
    pagination.current > 1
      ? dispatch(updateVoiceList([...data?.list]))
      : dispatch(initVoiceList([...data?.list]));
  }
}, [data]);
// 搜索与查询智能体列表接口请求流程相同,区别在于【searchText】字段的不同,拉取整体列表使【searchText】字段为空字符串
const { pagination, data, run } = usePagination(
  ({ current, pageSize, searchText }) =>
    getVoiceListFunc({ current, pageSize, searchText }) as Promise<GetStandardVoice>,
  {
    manual: true,
  }
);
// 切换音色(编辑智能体详情)
export const updateRole = async (params: any) => {
  try {
    // postUpdateAgentRole 
    const response = await updateAI2AgentRoles({
      devId: getDevInfo().devId,
      ...params,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const handleItemChecked = async (idKey: string, item: any) => {
  try {
    playVoiceAudio(item?.demoUrl);
    if (squareEntry === 'role') {
      dispatch(
        updateRoleInfo({
          voiceId: idKey,
          voiceName: item?.voiceName,
          supportLangs: item?.supportLangs,
        })
      );
      return;
    }
    showLoading({
      title: '',
    });
    const params = { ...roleInfo, useTimbreId: idKey, roleId };
    if (squareEntry === 'role') {
      delete params.roleId;
    }
    updateRole(params)
      .then(async res => {
        if (res) {
          dispatch(updateRoleInfo({ voiceId: idKey, voiceName: item?.voiceName }));
          setTimeout(async () => {
            const agentCloudInfo = (await getAgentInfo(roleId)) as AgentInfo;
            dispatch(updateAgentInfo({ ...agentCloudInfo, roleId }));
            hideLoading();
          }, 500);
        }
        hideLoading();
      })
      .catch(error => {
        if (platform === 'android' && error?.innerError?.errorCode === '13890100') {
          showToast({
            title: error?.innerError?.errorMsg,
            icon: 'error',
          });
        } else if (platform === 'ios') {
          showToast({
            title: iOSExtractErrorMessage(error?.innerError?.errorMsg),
            icon: 'error',
          });
        } else {
          showToast({
            title: Strings.getLang('dsc_choose_fail'),
            icon: 'error',
          });
        }
        hideLoading();
      });
  } catch (err) {
    console.log('handleItemChecked:::err', err);
    hideLoading();
  }
};

该页面用于编辑音色具体内容,音色广场中的音色在此页面支持修改音色的语速等功能。
// 编辑智能体详情(语调、语速为智能体详情信息)
export const updateRole = async (params: any) => {
  try {
    // postUpdateAgentRole 
    const response = await updateAI2AgentRoles({
      devId: getDevInfo().devId,
      ...params,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const onChangeToneOrSpeed = (sv: number, tv: number) => {
  showLoading({
    title: '',
  });
  updateRole({ roleId, [speedKey]: `${sv}`, [toneKey]: `${tv}` })
    .then(async res => {
      sv >= 0 && setSpeedValue(sv);
      tv >= 0 && setToneValue(tv);
      hideLoading();
      showToast({
        title: Strings.getLang('dsc_edit_success'),
        icon: 'success',
      });
    })
    .catch(() => {
      hideLoading();
      showToast({
        title: Strings.getLang('dsc_edit_fail'),
        icon: 'error',
      });
    });
};
// 恢复默认设置(语调、语速)
export const localResetToDefaultVoice = async (params: any) => {
  try {
    // resetToDefaultVoice
    const response = await restoreAI2AgentRolesSpeed({
      devId: getDevInfo().devId,
      ...params,
    });
    return response;
  } catch (err) {
    return Promise.reject(err);
  }
};
const onChangeToneOrSpeedReset = () => {
  showLoading({
    title: '',
  });
  localResetToDefaultVoice({ roleId })
    .then(async res => {
      const sv = res?.speed;
      const tv = res?.tone;
      sv >= 0 && setSpeedValue(sv);
      tv >= 0 && setToneValue(tv);
      hideLoading();
      showToast({
        title: Strings.getLang('dsc_edit_success'),
        icon: 'success',
      });
    })
    .catch(() => {
      hideLoading();
      showToast({
        title: Strings.getLang('dsc_edit_fail'),
        icon: 'error',
      });
    });
};