用户说“找一只有翅膀、和水有关的神兽”,需要的可能是一组候选;说“打开应龙的原典”,需要的则是一个准确页面。把两种请求都转换为搜索框输入,会丢失用户动作的差异,也让结果难以被系统智能入口继续使用。
应用 Skill 化可以从已有业务服务开始:先定义可查询的对象、可执行的动作和可回读的结果,再接入实际智能体框架。HMAF 2.0 是整体能力方向,各接口的版本、申请条件和调用方式仍需逐项核对,不能用框架名称替代接入合同。
端侧 AI 从自然语言落到确定数据
“空间 × AI”场景需要把自然语言结果连接到真实展品。查询执行器先在端侧完成参数校验、实体匹配和候选排序,再把稳定 ID 交给页面路由。图鉴原文、收藏记录与最近浏览历史留在应用数据层;系统智能入口只获得完成本次任务所需的字段。这样既能支持“找一只有翅膀的水系神兽”一类开放表达,也能避免模型生成数据库中不存在的条目。
端侧处理还带来可重复验收条件:相同数据版本与相同输入应返回相同候选;歧义输入进入ambiguous,缺少上下文进入needs_context,未知实体进入empty。页面只消费通过校验的结构化结果,模型输出不会直接变成收藏、跳转或文件操作。
一个 Skill 先处理一种确定任务
首个 Skill 适合选择只读图鉴查询。输入包含用户描述与候选数量,输出包含稳定条目 ID、名称和匹配依据。查询与收藏分开,打开页面与后台返回列表也分开,避免一次意图隐含多种写入行为。
interfaceBeastQuery{text:string;limit:number;}interfaceBeastCandidate{beastId:string;name:string;reason:string;}interfaceQueryResult{state:'matched'|'ambiguous'|'empty'|'needs_context';candidates:BeastCandidate[];}这是应用查询合同,不是系统 Skill 配置。平台适配层负责参数反序列化、系统结果封装和生命周期回调,业务仓库不应直接依赖系统对话对象。
语义理解之后仍要做实体消歧
同名、别名和描述性称呼可能指向多个条目。服务应返回候选,让调用方选择,而不是默认拿第一项执行打开或收藏。匹配依据来自应用数据和实际查询结果,不能临时编造原典说明。
如果用户只说“打开上一个提到的条目”,需要可靠的会话上下文。上下文缺失时返回需要补充信息的状态,不从其他账号的最近浏览记录猜测。会话数据的可见范围应与当前用户一致。
页面动作使用稳定标识
打开原典可以接收 beastId 和 sourceId,再由应用路由解析。不要直接接受任意 URL、任意页面名或文件路径作为动作参数。对已删除条目返回资源不存在,对无权访问的内容返回不可访问,不泄露不必要的内部结构。
系统意图 → 参数校验 → 实体解析 → 业务查询 → 结构化结果/明确页面目标 → 系统结果适配这个拆分使同一查询服务可以同时服务搜索页、智能入口和未来 A2A 流程。三种入口共享真实数据,减少结果不一致。
重复调用不能变成重复副作用
只读查询天然容易重试。将来加入收藏或加入导览时,需要 requestId 与幂等处理,不能因为系统重发就插入多条记录。执行成功后返回实际记录 ID,而不是只返回一句“已完成”。
耗时操作还要区分已接收、执行中和完成。先回复“收到请求”不表示业务完成;系统允许怎样返回任务状态,应在适配层遵循官方协议,不自行拼接看起来相似的消息。
用同一数据集检查多种表达
验收集可以包含准确名称、别名、视觉描述、歧义描述、空输入和不存在条目。记录解析结果、候选顺序与最终目标页面。一个表达成功只能说明该案例通过,不能推广为任意自然语言都能准确理解。
实际 Demo 还需从获准的系统入口发起调用,确认请求到达应用服务并能返回结果。仅在应用内部直接调用查询函数,验证的是业务逻辑,不是系统 Skill 接入。
开发顺序建议先完成查询合同与固定数据测试,再完成系统配置和真实回调,最后扩展页面动作。对写入动作另设验收集,保持查询与执行的责任清晰。
查询出口按信息充分程度选择
| 输入 | 服务结果 | 后续动作 |
|---|---|---|
| 唯一条目的准确名称 | matched 与稳定 ID | 可打开该条目 |
| 能命中多项的描述 | ambiguous 与候选依据 | 请求用户选择 |
| 缺少上下文的“上一个条目” | 需要补充信息 | 不读取无关会话历史 |
第三类与数据确实不存在不同,QueryResult 用 needs_context 状态表达,避免将其塞进 empty。直接生成答案适合展示摘要,结构化候选则更便于继续打开原典;首个 Skill 选择后者。实施先确认查询的真实字段和匹配规则,再定义系统映射,最后做自然语言表达集。无法取得框架入口时,业务查询测试仍可运行,但接口适配保持未完成。
参考:HarmonyOS 7 Skill 能力入口、DevEco Studio AI 开发工具。
意图执行器如何接收参数并返回结果
图鉴查询采用自定义意图:名称为空返回 400,未知名称返回 404,玄龟返回 0 和说明。参数在执行器入口再次校验,不假设系统入口一定传入合法文本。
意图执行器绑定后台执行模式,资源配置显式声明执行文件。构建使用规范化模块地址,确保装饰器生成元数据;另一个入口通过 AgentController 探测登记的 agentId,支持后显示 FunctionComponent。
@InsightIntentEntry({intentName:'ArticleLabLookup',domain:'ArticleLab',intentVersion:'1.0.0',displayName:'查询山海图鉴',displayDescription:'按名称查询随包图鉴说明',llmDescription:'查询玄龟的图鉴文字资料,返回对应说明',keywords:['玄龟','图鉴'],abilityName:'EntryAbility',executeMode:[insightIntent.ExecuteMode.UI_ABILITY_BACKGROUND],parameters:{type:'object',properties:{name:{type:'string',description:'图鉴名称',minLength:1}},required:['name']}})exportdefaultclassGuideIntentextendsInsightIntentEntryExecutor<string>{name:string='';asynconExecute():Promise<insightIntent.IntentResult<string>>{if(typeofthis.name!=='string'||this.name.trim().length===0){return{code:400,result:'图鉴名称不能为空'};}if(this.name.trim()!=='玄龟'){return{code:404,result:'未找到该图鉴'};}return{code:0,result:'玄龟:水系图鉴实验条目。'};}}用三种参数分支核对执行器
意图系统入口实际触发、平台登记的 agentId、生成的能力描述与平台审核。
| 检查层 | 当前处理 | 不能替代的结果 |
|---|---|---|
| 编译 | API 26 工程进行类型检查与打包 | 目标设备支持 |
| 逻辑 | 校验输入、状态或资源释放边界 | 平台服务真实返回 |
| 联调 | 按上面的输入和条件逐步执行 | 不能以按钮文案代替核心结果 |
签名包与真机验证边界
2026-09-23 已使用 API 26 构建出的签名 HAP 安装到 HBN-AL80(API 26),并直接检查包内resources/base/profile/insight_intent.json。生成文件声明了ArticleLabLookup、ArticleLab域、后台执行模式、EntryAbility与必填name参数,说明装饰器元数据进入了实际安装包。
这只验证了构建产物和真机安装,未验证系统意图入口真正调用onExecute。当前没有平台登记的agentId,因此也没有调用AgentController.isAgentSupport或展示FunctionComponent。不能把包内元数据、直接宿主测试或应用内按钮视作系统智能体入口已联调;完整记录见 配套验证记录。
InsightIntent、AgentController 与 FunctionComponent 的版本约束,以 HarmonyOS 官方文档中心和当前 SDK 声明为准。
意图参数的三个返回分支
空名称、未知名称和有效名称分别返回不同代码,不能将未知条目转换为成功空结果。执行器输入来自系统入口,仍需在边界再次校验。宿主测试直接载入 GuideIntent,仅以替身代替平台装饰器和基类;load 与 test 属于测试框架。
test('intent: missing and unknown names have distinct codes',async()=>{constC=load('intents/GuideIntent',{'@kit.AbilityKit':{InsightIntentEntry:()=>cls=>cls,InsightIntentEntryExecutor:class{},insightIntent:{ExecuteMode:{UI_ABILITY_BACKGROUND:1}}}}).default;constx=newC();assert.equal((awaitx.onExecute()).code,400);x.name='unknown';assert.equal((awaitx.onExecute()).code,404);x.name='玄龟';assert.equal((awaitx.onExecute()).code,0);});| 操作或边界 | 应检查的结果 |
|---|---|
| 输入不满足前提 | 不调用后续平台操作 |
| 平台拒绝或抛错 | 保留错误,不生成成功状态 |
| 再次进入或重试 | 检查旧状态不会污染新任务 |