查看: 165|回复: 3

鸿蒙意图框架实战:小艺出行从声明到双端运行全记录

[复制链接]
发表于 昨天 23:00 | 显示全部楼层 |阅读模式
在 Agent 时代的应用开发中,一个根本性的变化是用户不再关心由哪个应用完成任务,只关心任务本身是否被正确完成。鸿蒙意图框架(Intents Kit)正是实现这种“以意图为中心”分发的关键基础设施。本文以一个真实可编译的 Demo——「小艺出行」为主线,完整复盘其从零构建到在手机与鸿蒙 PC 双端运行的全过程,重点解析意图声明、执行器实现、UI 回流机制、导航拉起以及常见问题排查等核心技术细节。

一、开发范式的认知升级
传统 App 开发以“应用为中心”,用户需解锁、点击图标、打开应用、再通过多步操作完成任务。而 Agent 模式以“意图为中心”,用户只需说一句“帮我加个明天九点去机场的行程”,系统即完成意图理解、参数抽取并路由给最合适的能力提供方。开发者的核心工作从设计吸引用户的界面,转变为向系统清晰声明:可处理哪些意图、需要哪些参数、执行后返回何种结果。这一转变可归纳为三个层面:从流量获取到意图接入、从界面开发到契约定义、从单点体验到全场景触达。理解这些,再看意图框架 API 的设计逻辑就清晰了。

二、场景选取与 Demo 整体架构
选定出行助手作为演示场景,因为其意图表达天然口语化、参数可清晰结构化、可演示多意图协同(添加、查询、导航),且天然具备跨设备特性。Demo 名为「小艺出行」,纯端侧无后端依赖,包含三个页面(概览、行程、我的)和三个意图(AddTrip、QueryTrips、NavigateTo),采用底部 Tab 切换深色科技风格。工程目录关键结构如下(仅列核心文件):
  1. entry/src/main/
  2. ├── ets/
  3. │   ├── entryability/EntryAbility.ets
  4. │   ├── intents/TripIntentExecutor.ets
  5. │   ├── model/Trip.ets
  6. │   ├── model/IntentHandler.ets
  7. │   ├── model/NavHelper.ets
  8. │   └── pages/
  9. │       ├── Index.ets
  10. │       ├── OverviewTab.ets / TripsTab.ets / ProfileTab.ets
  11. ├── resources/base/profile/insight_intents.json
  12. └── module.json5
复制代码

三、核心实现:向系统声明应用能力
这是从 App 到 Agent 转变中最关键的一步。首先编写意图声明文件 `insight_intents.json`,向系统明确三件事:应用具备哪些意图、每个意图的定义、执行所需的参数。以“添加行程”为例:
  1. {
  2.   "intentName": "AddTrip",
  3.   "domain": "TravelManagement",
  4.   "displayName": "添加行程",
  5.   "llmDescription": "当用户想安排出行、添加行程、记录一次出发时使用。可从表达中提取目的地 destination、出行方式 type、时间 when、备注 note。例如「帮我加个明天9点去首都机场的行程」。",
  6.   "keywords": ["加行程", "安排出行", "添加行程", "去", "出发"],
  7.   "parameters": {
  8.     "type": "object",
  9.     "properties": {
  10.       "destination": { "type": "string", "description": "目的地,必填" },
  11.       "type": { "type": "string", "enum": ["飞机", "高铁", "驾车", "打车", "地铁", "步行"] },
  12.       "when": { "type": "string", "description": "出发时间的自然语言描述" },
  13.       "note": { "type": "string", "description": "备注,可选" }
  14.     },
  15.     "required": ["destination"]
  16.   },
  17.   "executor": "ets/intents/TripIntentExecutor.ets"
  18. }
复制代码
值得关注的是 `llmDescription` 字段——它以自然语言撰写、供大模型理解能力说明,相当于面向 AI 的 prompt。`keywords` 辅助召回,`parameters` 定义结构化契约,由系统大模型将口语表达解析为结构化字段,开发者无需实现 NLP 逻辑。

声明文件需在 `module.json5` 中通过 metadata 注册:
  1. "metadata": [
  2.   { "name": "ohos.ability.intent.metadata", "resource": "$profile:insight_intents" }
  3. ]
复制代码

四、意图执行器的实现
三个意图共用一个执行器 TripIntentExecutor,按意图名称分派。执行器的关键方法 `onExecuteInUIAbilityForegroundMode` 接收意图名称和参数,执行后返回 `ExecuteResult`,其中 `result.message` 不仅用于 UI 展示,还可被小艺语音播报,因此文案需兼顾“可视”与“可听”。示例核心逻辑:
  1. export default class TripIntentExecutor extends InsightIntentExecutor {
  2.   async onExecuteInUIAbilityForegroundMode(
  3.     name: string,
  4.     param: Record<string, Object>,
  5.     pageLoader: object
  6.   ): Promise<insightIntent.ExecuteResult> {
  7.     let message = '';
  8.     if (name === 'QueryTrips') {
  9.       message = await this.doQuery(str(param['when']));
  10.     } else if (name === 'NavigateTo') {
  11.       const dest = str(param['destination']);
  12.       await openNavigation(this.context as common.UIAbilityContext, dest);
  13.       message = `正在为你拉起地图,规划前往「${dest}」的路线…`;
  14.     } else {
  15.       message = await this.doAdd(param);
  16.     }
  17.     AppStorage.setOrCreate('lastIntentMsg', message);
  18.     AppStorage.setOrCreate('intentTick', Date.now());
  19.     return { code: 0, result: { message } } as insightIntent.ExecuteResult;
  20.   }
  21. }
复制代码

五、意图结果的 UI 回流机制
意图执行后数据变更,若应用已在前台,`onPageShow` 不会触发。本 Demo 采用全局状态 + `@Watch` 响应式机制:执行器将结果写入 `AppStorage`,页面用 `@StorageLink` 监听。使用递增的 `intentTick` 时间戳作为触发信号,避免重复监听。关键代码:
  1. @StorageLink('intentTick') @Watch('onIntentResult') intentTick: number = 0;
  2. @StorageLink('lastIntentMsg') lastIntentMsg: string = '';
  3. @StorageLink('intentJumpTab') intentJumpTab: number = -1;
  4. onIntentResult(): void {
  5.   promptAction.showToast({ message: '🎙 小艺:' + this.lastIntentMsg, duration: 2800 });
  6.   if (this.intentJumpTab >= 0 && this.intentJumpTab <= 2) {
  7.     this.current = this.intentJumpTab;
  8.   }
  9. }
复制代码
这种“时间戳驱动响应式刷新”的方法,是调试中总结的实用技巧。

六、导航能力:真实拉起地图应用
“导航”意图需要真实拉起地图应用。鸿蒙中通过 `startAbility` + Want 实现。由于不同设备预装地图不同,设计了一条降级调用链:依次尝试 Petal Maps、高德、百度,任一成功即返回,全部失败时使用系统隐式 Want 兜底。
  1. export async function openNavigation(context: common.UIAbilityContext, dest: string): Promise<boolean> {
  2.   const schemes = [
  3.     { name: 'PetalMaps', uri: `maps://routes?daddr=${encodeURIComponent(dest)}&type=drive` },
  4.     { name: 'Amap', uri: `amapuri://route/plan/?dname=${encodeURIComponent(dest)}&t=0` },
  5.     { name: 'BaiduMap', uri: `baidumap://map/direction?destination=${encodeURIComponent(dest)}&mode=driving` }
  6.   ];
  7.   for (const s of schemes) {
  8.     try {
  9.       await context.startAbility({ uri: s.uri });
  10.       return true;
  11.     } catch (e) { /* 未安装,继续尝试 */ }
  12.   }
  13.   try {
  14.     await context.startAbility({ action: 'ohos.want.action.viewData', uri: `geo:0,0?q=${encodeURIComponent(dest)}` });
  15.     return true;
  16.   } catch (e) { return false; }
  17. }
复制代码
这一设计体现了 Agent 化思路:应用将“导航”子任务委托给系统中最适合的提供方,形成能力协作网络。

七、双端适配:手机与鸿蒙 PC 一致运行
本 Demo 未为 PC 单独编写布局代码,即可在手机和鸿蒙 PC 上直接运行。实现原则:弹性布局(`layoutWeight`、百分比、`Flex`)、安全区适配(`getWindowAvoidArea` 获取状态栏高度存入全局)、组件化复用(`TripCard` 独立组件)。更重要的是,意图能力天然跨端——同一份 `insight_intents.json` 在手机和 PC 上均可通过小艺触发,因为意图注册于系统层面,而非某一屏幕。从模拟器到真机手机再到鸿蒙 PC,全程无需修改代码。

八、典型问题与解决方案
8.1 语音指令被分发至系统日历
原因是真实小艺基于华为云端已上架的意图库匹配,本应用的 AddTrip 仅为本地声明,未通过 AppGallery Connect 完成意图框架能力申请与上架。本地声明仅解决“应用内部知晓”,平台上架才使小艺云端感知。本地验证可采用 `aa start` 命令直接投递意图。

8.2 未上架情况下的意图链路验证
使用 hdc shell 命令:
  1. hdc shell "aa start -a EntryAbility -b com.example.smarttrip --ps op add --ps destination '首都机场' --ps type 'flight' --ps when 'tomorrow 9'"
复制代码
注意使用 `--ps` 直接写入 `want.parameters`,避免 URI 中 `&` 被 shell 解析为分隔符。

8.3 命令行中文参数乱码
hdc shell 处理中文参数时编码错乱,但在真实场景中小艺采用结构化参数传递不受影响。应对方案:添加英文 key 到中文的映射,本地测试使用英文参数。

8.4 环境变量污染导致构建失败
报错 `ERR_WORKER_INVALID_EXEC_ARGV: --report-on-fatalerror is not allowed in NODE_OPTIONS`,因 `NODE_OPTIONS` 被外部工具污染。解决方案:彻底退出 DevEco Studio,清除该环境变量后重新启动。

九、意图框架的工程价值
完成本 Demo 后,可以明确感受到意图即服务的价值:
- 降低流量获取压力,竞争维度转为能力可靠程度;
- 垂直应用可通过意图分发被精准调用,对独立开发者友好;
- 促使开发者重新审视应用边界,将子任务委托给专业能力方。
同时,开发者需要掌握面向 AI 的能力说明撰写(`llmDescription`)、返回值语音播报优化、跨端弹性设计等新课题。

十、结语
在 Agent 时代,一个应用真正的起点不是它的首个页面,而是它向系统声明的能力契约。「小艺出行」Demo 虽只有三个页面和三个意图,却完整呈现了从“开发一个供用户主动打开的应用”到“构建一个供系统随时调用的 Agent 能力”的全过程,包括技术难点与解决方案。对于正在开发鸿蒙应用的开发者,建议选取熟悉场景,编写意图声明并接入小艺,真切体验这一范式转变的工程落地。
回复

使用道具 举报

发表于 昨天 23:10 | 显示全部楼层

Re: 鸿蒙意图框架实战:小艺出行从声明到双端运行全记录

写得非常详尽,感谢分享!想请教一下:`llmDescription` 这个字段在测试阶段如何调试?比如大模型解析出的参数跟预期不一致时,有没有什么工具或日志可以查看系统意图理解结果?还是只能通过最终小艺的执行结果反推?
回复 支持 反对

使用道具 举报

发表于 昨天 23:10 | 显示全部楼层

Re: 鸿蒙意图框架实战:小艺出行从声明到双端运行全记录

感谢楼主的详细分享,这篇实战记录非常硬核,从认知升级到代码细节都讲透了。我尤其感兴趣的是 `llmDescription` 字段的设计——把 prompt 直接写进意图声明里,等于让开发者用自然语言跟大模型对话,这个思路确实比传统 NLP 逻辑清爽很多。另外双端运行的体验差异有遇到过吗?比如 PC 端小艺的意图识别准确率或者 UI 回流会不会有不同表现?期待后续能补一篇双端适配的踩坑记录。
回复 支持 反对

使用道具 举报

发表于 昨天 23:10 | 显示全部楼层

Re: 鸿蒙意图框架实战:小艺出行从声明到双端运行全记录

首帖内容非常详实,技术细节清晰,尤其是对意图框架的“认知升级”总结很到位。从声明到双端运行的完整流程记录对正在转型 Agent 模式的开发者很有参考价值。`llmDescription` 字段的设计思路很有意思,体现了面向 AI 的接口思维。期待后续关于 `insight_intents.json` 中 `executeInUIAbilityForegroundMode` 实现和常见问题排查部分的展开。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-11 01:26 , Processed in 0.036235 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部