HarmonyOS 开发实例—蜜蜂 AI 助手

开发实例—蜜蜂 AI 助手 1. 前言
自华为宣布NEXT 全面启动 , 近期新浪、B 站、小红书、支付宝等各领域头部企业纷纷启动鸿蒙原生应用开发 。据媒体统计,如今 Top20 的应用里,已经有近一半开始了鸿蒙原生应用开发 。虽然目前NEXT 还未面向个人开发者开放,但我们可以体验并使用最新的 API9 和开发工具,尝试开发元服务,这个鸿蒙新的应用形态 。体验未来在NEXT 上实现的应用开发 。但需要注意的是,基于 API9 开发的应用或元服务是不可以适配NEXT 版本的,大家也可以期待一下明年推出的适配NEXT 新版本 。
本文主要是基于蜜蜂 AI 元服务的开发案例,主要的功能有
元服务内部功能:
1、提供两个 Tabs , 首页和我的;
2、用户只有登录之后才可以去使用蜜蜂 AI 的功能;
3、目前现有的知识库包括知识百科小助手,节日小助手,文本翻译小助手 , 产品名称小助手,以及道歉信小助手等;
4、用户使用小助手之后,我们可以保存对话到列表内,下次快速的进行访问 。
元服务卡片:
1、提供 2-4 的卡片,卡片界面展示每日妙语,点击即可刷新;
2、提供 1-2 的卡片,实现快速访问首页;
3、提供 2-2 卡片,可以快速使用包括知识百科小助手,节日小助手,文本翻译小助手,产品名称小助手;
4、提供 4-4 卡片,可以快速到达登陆页面,访问小助手等 。
开发实例—蜜蜂 AI 助手演示视频
1.1
是华为公司开发的操作系统,它的设计理念是面向未来的全场景智慧体验,可在各种设备上运行,包括手机、平板电脑、智能手表、智能音箱等 。采用分布式技术,可以将不同设备之间的计算资源连接起来,实现设备间的协同工作,提高系统的性能和稳定性 。此外,还拥有高度自适应的界面、多屏协同等特性,使用户能够在不同设备上实现无缝的体验 。
1.2 元服务
在万物互联时代,人均持有设备量不断攀升 , 设备和场景的多样性 , 使应用开发变得更加复杂、应用入口更加多样 。在此背景下,应用提供方和用户迫切需要一种新的服务提供方式,使应用开发更简单、服务(如听音乐、打车等)的获取和使用更便捷 。为此,除支持传统方式的需要安装的应用(以下简称传统应用)外,还支持更加方便快捷的免安装的应用(即元服务) 。
1.3 介绍(AGC)
(简称 AGC)致力于为应用的创意、开发、分发、运营、经营各环节提供一站式服务,构建全场景智慧化的应用生态体验 。
1.4 蜜蜂 AI 元服务助手背景
目前 AI 正火,而我自己也有辛参与到大模型的训练中来,于是有了蜜蜂这个作品 。
元服务与传统应用对比 项目元服务传统应用
软件包形态
App Pack(.app)
App Pack(.app)
分发平台
由应用市?。ǎ┕芾砗头址?
由应用市?。ǎ┕芾砗头址?
安装后有无桌面 icon
无桌面 icon,但可手动添加到桌面,显示形式为服务卡片
有桌面 icon
HAP 免安装要求
所有HAP(包括 Entry HAP 和HAP)均需满足免安装要求
所有 HAP(包括 Entry HAP 和HAP)均为非免安装的
新建元服务应用
开通
AI 平台
登陆账号
如何运行
2. 准备工作 2.1应用开发环境
工欲善其事,必先利其器,我们首先要做的就是搭建开发环境
这里面我们分为三步走
2.1.1 环境安装
首先在这边安装最新的 IDE:
下载链接:#
我的是 M1,所以我们下载这一个就可以
2.1.2 环境配置
下载完成之后,我们就开始配置开发环境 。下载 SDK 及工具链 , 首次使用,工具的配置向导会引导您下载 SDK 及工具链 。配置向导默认下载 API9 的 SDK 及工具链,我们选择默认就好
下载和 ohpm , 记得最好SDK 路径中不能包含中文字符 。
下载完成之后,我们下载SDK
在弹出的 SDK 下载信息页面,单击Next,并在弹出的 窗口,阅读协议 , 需同意协议后,单击Next 。
目前最新的应该是 3.2.13.5 。
确认设置项的信息,点击Next开始安装 。
等待 Node.js、ohpm 和 SDK 下载完成后,单击,界面会进入到欢迎页 。
2.1.3 创建
1.在的欢迎页 , 选择开始创建一个新工程 。
2.根据工程创建向导,在页签,选择“Empty ”模板,单击 Next 。
3.单击 Next,各个参数保持默认值即可 , 单击 ,
2.1.4 运行
1.将搭载手机与电脑连接 。
2.单击 File>> >界面勾选“支持,以及”,等待自动签名完成即可,单击 OK 。如右所示: 。
3.在编辑窗口右上角的工具栏,单击运行,等待编译完成即可便运行在设备上 。
这个时候真机就可以看到。接下来我们就创建蜜蜂 AI 元服务 。
2.2 创建蜜蜂 AI 元服务
这里我们的模版就不再选空模板了,而是直接选择最后一个端云一体化模版
然后其他的就按照上面的配置就可以 。完成项目的配置 。
这里有个区别就是我们需要关联云资源 。所以我们创建的应用包名要牢记,这个要在后面我们云端配置的时候使用 。
为工程关联云开发所需的资源,即在中选择您的华为开发者账号加入的开发者团队,将该团队在 AGC 的同包名应用关联到当前工程,具体操作如下:
单击“Team”下拉框 , 选择开发团队 。选中团队后,系统根据工程包名自动查询团队下的同包名应用 。若为首次创建且团队下未创建同包名的应用 , 则提示需要在 AGC 平台创建应用 。
单击“ ”打开 AGC 应用创建向导,填写应用信息 , 单击“确认”按钮创建应用 。
完成以上操作后 , 即可获取到同包名应用对应的项目信息 。
2.3 AGC 配置
我们登陆云侧,创建元服务
然后我们开通手机登陆和邮箱登录服务 。
3. 实现登录
当前 AGC 认证服务为应用/服务提供的登录认证方式有手机、邮箱两种方式 。本工程使用“手机号码+验证码”的方式作为应用的登录入口 。而且我们在前面已经开通 。
在登陆这一块,用户首次登陆的时候,我们会首先利用首选项检查他的登陆状态 。
首选项工具类
/*** 首选项操作类*/import { PreferenceDBUtil } from '../utils/PreferencesDBUtil';const preDbService = new PreferenceDBUtil();preDbService.getPreStorage();export const getDBPre = async (key: string) => {const value = http://www.kingceram.com/post/await preDbService.getPreVal(key);return value;};export const putDBPre = async (key: string, value: string) => {await preDbService.putPreData(key, value);};
然后跳用调用.de申请验证码,在entry/src/main/ets//Auth.ts认证工具类中添加邮箱验证码获取方法 。
import { MainPage } from "@hw-agconnect/auth-component-ohos"import router from '@ohos.router'import { LogUtil } from '../common/utils/LogUtil';import { Constants } from '../common/Constants';import { putPre } from '../common/service/PreService';import { UserInfo } from '../common/UserInfo';@Entry@Componentstruct Index {@State icon: Resource = router.getParams()['icon'];@State isAgreement:boolean = router.getParams()['isAgreement'];@State agreementContent:string = router.getParams()['agreementContent'];@State onSuccess: Function = router.getParams()['onSuccess'];@State onError: Function = router.getParams()['onError'];build() {Column() {MainPage({icon: this.icon,agreement: {isAgreement: this.isAgreement,agreementContent: this.agreementContent,},onSuccess: async (user) => {LogUtil.info(`登录用户信息:${JSON.stringify(user)}`);const loginUser = user['user'];const userInfo: UserInfo = {uid: loginUser['uid'],email: loginUser['email'],phone: loginUser['phone'] === undefined ? "" : loginUser['phone'].split('-')[1],displayName: loginUser['displayName'] === undefined ? "" : loginUser['displayName'],photoUrl: loginUser['photoUrl'] === undefined ? "/common/imgs/ic_user.svg" : loginUser['photoUrl']}await putPre(Constants.LOGIN_USER_KEY, JSON.stringify(userInfo));router.back();},onError: (err) => {LogUtil.error(`登录用户信息:${JSON.stringify(err)}`);}})}}aboutToAppear() {}}
未登录弹窗
/*** 未登录弹窗*/import common from '@ohos.app.ability.common';import router from '@ohos.router';import { GlobalConstant } from '../common/constants/GlobalConstant';@CustomDialogexport struct LoginTipDialogView {loginTipCtrl: CustomDialogController;build() {Column({ space: GlobalConstant.SIZE_8 }) {Row({ space: GlobalConstant.SIZE_4 }) {Image($r('app.media.ic_tip')).width(GlobalConstant.SIZE_32).height(GlobalConstant.SIZE_32)Text('温馨提示').fontSize($r('app.float.font_size_24')).fontColor($r('app.color.tip_color')).fontWeight(FontWeight.Bolder)}.width(GlobalConstant.PAGE_FULL).height(GlobalConstant.SIZE_64).padding({ left: GlobalConstant.SIZE_16 })Text('您还未登录,请登录后体验功能!').height(GlobalConstant.SIZE_48).fontSize(Color.Black).fontSize($r('app.float.font_size_18')).fontWeight(FontWeight.Normal)Row({ space: GlobalConstant.SIZE_8 }) {Button('退出', { type: ButtonType.Normal }).borderRadius(GlobalConstant.SIZE_4).backgroundColor($r('app.color.embellishment_color')).fontColor($r('app.color.text_color_9')).onClick(() => {const ctx = getContext(this) as common.UIAbilityContext;ctx.terminateSelf();})Button('去登录', { type: ButtonType.Normal }).borderRadius(GlobalConstant.SIZE_4).backgroundColor($r('app.color.embellishment_color')).fontColor($r('app.color.auxiliary_color')).onClick(() => {this.loginTipCtrl.close();router.pushUrl({params:{isAgreement: true,agreementContent: "",icon: "",type: ["HWID_VERIFY_CODE","PHONE"]},url: '@bundle:com.jianguo.ai/common/ets/LoginComponent/LoginPage',})})}.width(GlobalConstant.PAGE_FULL).justifyContent(FlexAlign.Center)}.width(GlobalConstant.PAGE_96).padding({ bottom: GlobalConstant.SIZE_20 }).borderRadius(GlobalConstant.SIZE_16).backgroundColor(Color.White)}}
4.实现蜜蜂 AI 助手页面
我们这个应用主要的一个功能就是 AI 助手,所以这一块我们分为三块 。
4.1 蜜蜂 AI 列表页
关于列表页,我们使用一个列表就可以
/*** 首页*/import { ConfigConstant } from '../common/constants/ConfigConstant'import { GlobalConstant } from '../common/constants/GlobalConstant'import { AiAppConfig } from '../common/dto/AiAppConfig';import router from '@ohos.router'import { getDBPre } from '../common/api/PreDbService';@Componentexport struct HomeView {@State aiAppList: Array = ConfigConstant.DEFAULT_AI_APP_LIST;}build() {Column() {List() {ForEach(this.aiAppList, (item: AiAppConfig) => {ListItem() {Row({ space: GlobalConstant.SIZE_8 }) {Row() {Image(item.avatar).width(GlobalConstant.SIZE_64).height(GlobalConstant.SIZE_64).borderRadius(GlobalConstant.SIZE_32)}.height(GlobalConstant.PAGE_FULL).layoutWeight(1)Column({ space: GlobalConstant.SIZE_16 }) {Text(item.name).fontSize($r('app.float.font_size_18'))Text(item.intro).fontSize($r('app.float.font_size_14')).fontColor($r('app.color.text_color_9'))}.height(GlobalConstant.PAGE_FULL).layoutWeight(3).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Start)}.width(GlobalConstant.PAGE_96).height(GlobalConstant.SIZE_100).paddingStyle().borderRadius(GlobalConstant.SIZE_16).shadow({radius: GlobalConstant.SIZE_16,color: $r('app.color.main_color')}).onClick(() => {router.pushUrl({url: "pages/detail/index",params: {"AiAppConfig": item}})})}.width(GlobalConstant.PAGE_FULL).paddingStyle().borderRadius(GlobalConstant.SIZE_16)})}.listDirection(Axis.Vertical)}.width(GlobalConstant.PAGE_FULL).height(GlobalConstant.PAGE_FULL).padding(GlobalConstant.SIZE_8)}}
效果图
4.2 对话页 关键代码
build() {Column({ space: GlobalConstant.SIZE_8 }) {Stack({ alignContent: Alignment.Bottom }) {Column() {Column({ space: GlobalConstant.SIZE_4 }) {Text("蜜蜂AI助手").fontSize($r('app.float.font_size_16')).fontColor(Color.Black).fontWeight(FontWeight.Bolder)Text("介绍").fontSize($r('app.float.font_size_12')).fontColor($r('app.color.text_color_9')).fontWeight(FontWeight.Lighter)}.width(GlobalConstant.PAGE_FULL).justifyContent(FlexAlign.Center).padding({top: GlobalConstant.SIZE_4,bottom: GlobalConstant.SIZE_8})Scroll() {Column({ space: GlobalConstant.SIZE_8 }) {ForEach(this.chatContentArr, (chat: ChatInfo) => {if (chat.role === "assistant") {Row() {Row({ space: GlobalConstant.SIZE_8 }) {Image(chat.avatar).width(GlobalConstant.SIZE_24).height(GlobalConstant.SIZE_24)Row() {Text(chat.content).fontSize($r('app.float.font_size_14')).fontColor(Color.Black)}.width(chat.content.length > 15 ? GlobalConstant.PAGE_76 : 'auto').backgroundColor($r('app.color.embellishment_color')).padding({left: GlobalConstant.SIZE_16,right: GlobalConstant.SIZE_16,top: GlobalConstant.SIZE_8,bottom: GlobalConstant.SIZE_8}).borderRadius({topRight: GlobalConstant.SIZE_4,bottomLeft: GlobalConstant.SIZE_8,bottomRight: GlobalConstant.SIZE_4})}.justifyContent(FlexAlign.Start).alignItems(VerticalAlign.Top)}.width(GlobalConstant.PAGE_FULL).justifyContent(FlexAlign.Start)}if (chat.role === "user") {Row() {Row({ space: GlobalConstant.SIZE_8 }) {Row() {Text(chat.content).fontSize($r('app.float.font_size_14')).fontColor(Color.Black)}.width(chat.content.length > 15 ? GlobalConstant.PAGE_76 : 'auto').backgroundColor($r('app.color.tab_default_color')).padding({left: GlobalConstant.SIZE_16,right: GlobalConstant.SIZE_16,top: GlobalConstant.SIZE_8,bottom: GlobalConstant.SIZE_8}).borderRadius({topLeft: GlobalConstant.SIZE_4,bottomLeft: GlobalConstant.SIZE_4,bottomRight: GlobalConstant.SIZE_8})Image(chat.avatar).width(GlobalConstant.SIZE_24).height(GlobalConstant.SIZE_24)}.justifyContent(FlexAlign.End).alignItems(VerticalAlign.Top)}.width(GlobalConstant.PAGE_FULL).justifyContent(FlexAlign.End)}})}.width(GlobalConstant.PAGE_FULL)}.width(GlobalConstant.PAGE_96).scrollable(ScrollDirection.Vertical).flexShrink(1)}.width(GlobalConstant.PAGE_FULL).height(GlobalConstant.PAGE_FULL).padding({ bottom: GlobalConstant.SIZE_50 })Row({ space: GlobalConstant.SIZE_8 }) {TextInput({ placeholder: "请输入提示词...", text: this.inputValue }).height(GlobalConstant.SIZE_48).fontSize($r('app.float.font_size_16')).placeholderFont({ size: $r('app.float.font_size_16') }).placeholderColor($r('app.color.text_color_9')).borderRadius($r('app.float.size_8')).backgroundColor($r('app.color.card_bg_color')).flexShrink(1).onChange((value: string) => {this.inputValue = http://www.kingceram.com/post/value;})Image($r('app.media.ic_send')).width(GlobalConstant.SIZE_32).height(GlobalConstant.SIZE_32).onClick(async () => {this.loadingCtrl.open();if (this.inputValue =http://www.kingceram.com/post/=="") {promptAction.showToast({message: "发送内容不能为空!"})return;}await this.getAiResult();})}.width(GlobalConstant.PAGE_FULL).padding({left: GlobalConstant.SIZE_8,right: GlobalConstant.SIZE_8}).backgroundColor($r('app.color.card_bg_color'))}.width(GlobalConstant.PAGE_FULL).height(GlobalConstant.PAGE_FULL)}.width(GlobalConstant.PAGE_FULL).height(GlobalConstant.PAGE_FULL)}
【HarmonyOS 开发实例—蜜蜂 AI 助手】效果图