-
-
[原创]意图框架习惯推荐方案,为用户提供个性化内容分发
-
发表于: 2025-1-15 10:40 3393
-
在快速发展的现代生活中,用户面临着时间紧迫、信息过载和决策困难等痛点。例如新闻资讯过多,寻找感兴趣的内容费时费力;音乐和视频服务推荐内容不符合个人口味等。这些痛点,无疑增加了用户的烦恼,也给应用的开发者带来了挑战:如何更好识别用户意图,实现精准推荐,提高应用打开和使用频率?
基于此,HarmonyOS SDK意图框架服务(Intents Kit)提供习惯推荐方案,是HarmonyOS学习用户的行为习惯后做出的主动预测推荐。开发者将用户在应用/元服务内的使用行为向HarmonyOS共享,使得HarmonyOS可以基于共享的数据学习用户的行为习惯。在HarmonyOS学习到用户的行为习惯后,会给用户推荐相应功能,并且尝试补充详细功能参数,减少用户执行任务的步骤。
这种推荐机制不仅让用户更容易发现并使用他们感兴趣的内容和服务,也显著提高了开发者的点击率和转化率。为了简化开发者的接入流程,意图框架智慧分发提供了多种特性类别,当前已开放习惯推荐、事件推荐、技能调用-语音、本地搜索,后续会陆续开放其他特性类别。
场景体验
当前习惯推荐可在小艺建议入口分发,在不同垂域中,填充功能详细参数或内容的逻辑不同,主要典型场景可分为常用接续、常用复访、常用推新三类。以常看视频续播为例,系统预测当前用户使用华为视频的播放视频功能概率较高,会选择用户最近观看且还没看完的视频内容来补充功能细节,在小艺建议中以模板卡形式推荐展示,用户点击卡片后,实现一步跳转进应用的视频播放页。
卡片展示效果
意图框架提供各垂域习惯推荐在小艺建议中展示使用的标准模板卡片,开发者无需开发展示卡片。在展示模板上,会展示应用/元服务名称与logo和内容必要信息,比如音乐名、音乐图片,这类参数需要开发者共享到系统。
以下为播放歌曲-习惯推荐的卡片示例。
接入方案
方案概述
当用户在应用/元服务内使用功能时,开发者需要按照标准意图Schema向系统共享行为数据,并支持意图调用(空调用与传参调用),以实现用户点击模板卡后跳转回对应页面。
意图注册
以歌曲续听推荐特性为例,首先要注册播放歌曲意图(PlayMusic),其他意图见各垂域意图Schema。开发者需要编辑对应的意图配置
PROJECT_HOME/entry/src/main/resources/base/profile/insight_intent.json文件,实现意图注册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | { / / 应用支持的意图列表 / / 必须声明应用支持插件包含的必选意图,应用上架时会进行校验 "insightIntents" : [ { / / 意图名称 / / 名称应当遵循意图框架规范,当前仅支持预置垂域意图,不允许自定义 / / 应用内意图名称唯一,不允许出现相同的名称定义 "intentName" : "PlayMusic" , / / 意图所属的垂域 "domain" : "MusicDomain" , / / 意图版本号 / / 插件引用意图时会校验该版本号,只有和插件定义的版本号一致才能正常调用 "intentVersion" : "1.0.1" , / / 意图调用逻辑入口 "srcEntry" : "./ets/entryability/InsightIntentExecutorImpl.ets" , "uiAbility" : { / / 意图所在module、ability,以及代码相对路径入口 "ability" : "EntryAbility" , / / UIAbility支持前后台两种执行模式 "executeMode" : [ "background" , "foreground" ] } } ] } |
端侧意图共享
构建意图对象,并且调用shareIntent(),实现意图共享。可同时构建多个PlayMusic或PlayMusicList的意图对象。
1 2 3 4 5 6 7 8 9 10 11 12 | import { insightIntent } from '@kit.IntentsKit' ; import { BusinessError } from '@kit.BasicServicesKit' ; let playMusicIntent1: insightIntent.InsightIntent; let playMusicIntent2: insightIntent.InsightIntent; / / 共享数据接口 意图数组可以是更多的实体 / / 根据实际代码上下文自行传入合适的context insightIntent.shareIntent(context, [playMusicIntent1, playMusicIntent2]).then(() = > { console.info( 'shareIntent succeed' ); }).catch((err: BusinessError) = > { console.error(`error.code: ${err?.code}, failed because ${err?.message}`); }); |
PlayMusic的意图共享字段定义见各垂域意图Schema定义,代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import { insightIntent } from '@kit.IntentsKit' ; let playMusicIntent: insightIntent.InsightIntent = { intentName: "PlayMusic" , intentVersion: "1.0" , identifier: "52dac3b0-6520-4974-81e5-25f0879449b5" , intentActionInfo: { actionMode: "EXECUTED" , executedTimeSlots: { executedStartTime: 1637393212000 , executedEndTime: 1637393112000 , }, currentPercentage: 50 , }, intentEntityInfo: { entityName: "Music" , entityId: "C10194368" , entityGroupId: "C10194321312" , displayName: "测试歌曲1" , description: "NA" , logoURL: "2f3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3c8X3K9h3I4W2i4K6u0W2j5h3u0U0i4K6u0W2j5$3!0E0i4K6u0r3i4K6u0V1i4K6u0r3L8h3g2V1K9h3q4Q4x3V1k6U0L8%4u0H3L8%4u0S2N6r3g2Q4x3V1k6A6L8h3q4Y4k6i4y4Q4x3V1k6Z5L8$3#2W2i4K6u0r3L8r3!0Y4L8#2)9J5c8X3q4T1j5#2)9#2k6X3I4G2k6$3!0Q4x3X3g2H3L8X3M7`." , keywords: [ "华为音乐" , "化妆" ], rankingHint: 99 , expirationTime: 1637393212000 , metadataModificationTime: 1637393212000 , activityType: [ "1" , "2" , "3" ], artist: [ "测试歌手1" , "测试歌手2" ], lyricist: [ "测试词作者1" , "测试词作者2" ], composer: [ "测试曲作者1" , "测试曲作者2" ], albumName: "测试专辑" , duration: 244000 , playCount: 100000 , musicalGenre: [ "流行" , "华语" , "金曲" , "00后" ], isPublicData: false, } } |
完整的意图共享示例如下所示,该示例构建了一个PlayMusic意图,并进行了shareIntent调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | import { insightIntent } from '@kit.IntentsKit' ; import { BusinessError } from '@kit.BasicServicesKit' ; let playMusicIntent: insightIntent.InsightIntent = { intentName: "PlayMusic" , intentVersion: "1.0" , identifier: "52dac3b0-6520-4974-81e5-25f0879449b5" , intentActionInfo: { actionMode: "EXECUTED" , executedTimeSlots: { executedStartTime: 1637393212000 , executedEndTime: 1637393112000 , }, currentPercentage: 50 , }, intentEntityInfo: { entityName: "Music" , entityId: "C10194368" , entityGroupId: "C10194321312" , displayName: "测试歌曲1" , description: "NA" , logoURL: "92dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3c8X3K9h3I4W2i4K6u0W2j5h3u0U0i4K6u0W2j5$3!0E0i4K6u0r3i4K6u0V1i4K6u0r3L8h3g2V1K9h3q4Q4x3V1k6U0L8%4u0H3L8%4u0S2N6r3g2Q4x3V1k6A6L8h3q4Y4k6i4y4Q4x3V1k6Z5L8$3#2W2i4K6u0r3L8r3!0Y4L8#2)9J5c8X3q4T1j5#2)9#2k6X3I4G2k6$3!0Q4x3X3g2H3L8X3M7`." , keywords: [ "华为音乐" , "化妆" ], rankingHint: 99 , expirationTime: 1637393212000 , metadataModificationTime: 1637393212000 , activityType: [ "1" , "2" , "3" ], artist: [ "测试歌手1" , "测试歌手2" ], lyricist: [ "测试词作者1" , "测试词作者2" ], composer: [ "测试曲作者1" , "测试曲作者2" ], albumName: "测试专辑" , duration: 244000 , playCount: 100000 , musicalGenre: [ "流行" , "华语" , "金曲" , "00后" ], isPublicData: false, } } / / 共享数据接口 意图数组可以是更多的实体 / / 根据实际代码上下文自行传入合适的context insightIntent.shareIntent(context, [playMusicIntent]).then(() = > { console.info( 'shareIntent succeed' ); }).catch((err: BusinessError) = > { console.error(`error.code: ${err?.code}, failed because ${err?.message}`); }); |
端侧意图调用
意图执行组件为uiAbility的意图调用
如上文意图注册,当开发者注册的意图承载的运行组件为uiAbility时,开发者需要自己实现InsightIntentExecutor,并在对应回调实现打开落地页(点击推荐卡片跳转的界面)的能力,PlayMusic的意图调用字段定义见各垂域意图Schema。
步骤如下:
1.继承InsightIntentExecutor。
2.重写对应方法,例如目标拉起前台页面,则可重写
1 | onExecuteInUIAbilityForegroundMode方法。 |
3.通过意图名称,识别播放歌曲意图(PlayMusic),在对应的方法中传递意图参数(param),并拉起对应落地页(如歌曲落地页)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | import { insightIntent, InsightIntentExecutor } from '@kit.AbilityKit' ; import { window } from '@kit.ArkUI' ; import { BusinessError } from '@kit.BasicServicesKit' ; / * * * 意图调用样例 * / export default class InsightIntentExecutorImpl extends InsightIntentExecutor { private static readonly PLAY_MUSIC = 'PlayMusic' ; / * * * override 执行前台UIAbility意图 * * @param name 意图名称 * @param param 意图参数 * @param pageLoader 窗口 * @returns 意图调用结果 * / onExecuteInUIAbilityForegroundMode(name: string, param: Record<string, Object >, pageLoader: window.WindowStage): Promise<insightIntent.ExecuteResult> { / / 根据意图名称分发处理逻辑 switch (name) { case InsightIntentExecutorImpl.PLAY_MUSIC: return this.playMusic(param, pageLoader); default: break ; } return Promise.resolve({ code: - 1 , result: { message: 'unknown intent' } } as insightIntent.ExecuteResult) } / * * * 实现调用播放歌曲功能 * * @param param 意图参数 * @param pageLoader 窗口 * / private playMusic(param: Record<string, Object >, pageLoader: window.WindowStage): Promise<insightIntent.ExecuteResult> { return new Promise((resolve, reject) = > { let para: Record<string, string> = { 'result' : JSON.stringify(param) }; let localStorage: LocalStorage = new LocalStorage(para); / / TODO 实现意图调用,loadContent的入参为歌曲落地页路径,例如:pages / Index pageLoader.loadContent( 'pages/Index' , localStorage) .then(() = > { let entityId: string = (param.items as Array< object >)?.[ 0 ]?.[ 'entityId' ]; / / TODO 调用成功的情况,此处可以打印日志 resolve({ code: 0 , result: { message: 'Intent execute succeed' } }); }) .catch((err: BusinessError) = > { / / TODO 调用失败的情况 resolve({ code: - 1 , result: { message: 'Intent execute failed' } }) }); }) } } |
意图执行组件为form的意图调用
如上文意图注册,当开发者注册的意图承载的运行组件为form(运行组件FormExtensionAbility)时,则需要开发者在实现的FormExtensionAbility中从want中获取并解析意图名和执行参数,用于卡片展示。
步骤如下:
1.在意图执行绑定FormExtensionAbility的onAddForm(want: Want)中获取运行态意图框架传入的意图名(预定义keyName为
ohos.insightIntent.executeParam.name)和意图执行参数(预定义keyName为ohos.insightIntent.executeParam.param);
2.通过意图名称,识别播放歌曲意图(PlayMusic),在对应的方法中传递意图参数(param),并加载对应数据用于卡片展示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { Want } from '@kit.AbilityKit' ; import { formBindingData, FormExtensionAbility } from '@kit.FormKit' ; / * * * 卡片意图调用示例 * / export default class LoadCardFormAbility extends FormExtensionAbility { onAddForm(want: Want): formBindingData.FormBindingData { if (want?.parameters?.[ 'ohos.insightIntent.executeParam.name' ] ! = undefined) { const intentName = want.parameters[ 'ohos.insightIntent.executeParam.name' ]; / / 意图名 / / TODO: 根据意图名称分发处理逻辑,若仅一个卡片意图,则可以忽略 } if (want?.parameters?.[ 'ohos.insightIntent.executeParam.param' ] ! = undefined) { const executeParameter = want.parameters[ 'ohos.insightIntent.executeParam.param' ]; / / 意图执行参数 / / TODO: 从 executeParameter 中解析具体意图执行参数,用于卡片内容展示 } let formData = ''; / / TODO: 仅示例,根据具体逻辑封装 return formBindingData.createFormBindingData(formData); } } |
了解更多详情>>