
本文基于 HarmonyOS 7API 26的《基于ArkTS脚本的应用Skill开发指导》与官方新能力一览整理。文中代码是为说明问题自写的完整示例不是官方示例的搬运API 名称、标签与版本号等事实性信息均标注官方出处涉及真机表现的部分已明确标注未做任何实测数据编造。引子图标是一扇门但门不会自己开V哥认识一个做本地餐饮应用的朋友功能做得很全扫码点单、排队取餐、会员储值一个不落。有天V哥问他用户找你们’排队取餐’这个功能要点几步他掰指头数解锁、找图标、进首页、过广告位、点 tabBar、再进二级页——五步。而用户的表达其实只有四个字“帮我取餐。”这事在 HarmonyOS 7API 26之前无解应用只能在图标里等用户来点。7.0 把这个前提改了应用内业务能力可以以 Skill 的形式开放给系统智能体调用用户说一句帮我取餐系统自己找到你的能力、自己调官方新能力一览。V哥的原话概括就是标题那半句——应用从被打开变成了被调用。别小看这一个词的差别。被打开流量入口是图标和应用市场被调用流量入口变成了系统级智能分发。这期V哥把 Skill 化的完整路径拆开讲机制、目录、契约、代码最后给一份上线前自检清单。一、先想明白Skill 到底开放了什么先看官方的关键表述基于ArkTS脚本的应用Skill开发指导从 API 版本 26.0.0 开始Ability Kit 支持将应用内业务能力以 Skill 形式开放给系统智能体调用。Skill 提供一种声明式的能力外化机制……运行时系统智能体依据描述文件完成意图—能力的语义匹配并将结果转化为面向用户的自然语言回复。V哥从这段话里读出三个重点比 API 本身重要重点意思对开发者的直接影响声明式你不写怎么被调用只声明能做什么、什么时候调、参数什么样主体工作量在写契约不在写代码薄封装不改造既有业务实现入口脚本只是参数适配器老业务一行不动加个壳就能上语义匹配系统靠你的描述文件猜用户意图匹配上了才调你契约写得糙 永远匹配不上第三条是很多人会漏的。图标时代你的应用名称写错顶多搜索排名靠后Skill 时代描述文件就是你在意图分发池里的全部简历——系统智能体不进你的代码只看这份简历决定调不调你。还有一个硬边界先说在前这套机制仅支持 Stage 模型FA 模型不可用官方原文。存量 FA 模型工程得先迁移这不是 Skill 的坑是前置条件。二、Skill 的解剖图一个目录、两份文件、一处注册官方把一个 Skill 的物理形态规定得很死V哥画成一张链路图落到工程里就是在模块entry下建一个skills/目录里面每个 Skill 一个文件夹官方开发指导entry/ ├── skills/ - 固定值本模块所有 Skill 的根目录 │ └── vge-org-milktea-assistant/ - Skill 名须与 SKILL.md 的 name 一致 │ ├── scripts/ │ │ └── MilkteaSkill.ets - 入口脚本薄适配层 │ └── SKILL.md - 描述文件意图匹配的唯一依据 └── src/main/ ├── ets/service/MilkTeaService.ets - 应用既有业务被脚本调用零改动 └── module.json5 - 在这里注册 skillProfiles三处名字必须完全一致目录名 SKILL.md 的name module.json5 里skillProfiles[].name。官方还专门提醒为防命名冲突Skill 名推荐用公司或组织名做前缀——V哥示例里的vge-org-就是这个用途。注册写进module.json5的skillProfiles标签官方新增标签{ module: { skillProfiles: [ { name: vge-org-milktea-assistant, // 与目录名、SKILL.md 的 name 三处一致 abilityName: EntryAbility, // Skill 绑定到哪个 Ability 的运行上下文 srcEntries: [ // 入口脚本路径 ../../skills/vge-org-milktea-assistant/scripts/MilkteaSkill.ets ], version: 1.0.0 } ], requestPermissions: [ { name: ohos.permission.INTERNET } // Skill 运行需要的权限照常声明 ] } }注意abilityName这一项Skill 不是悬浮的它绑定在指定 Ability 的运行上下文里执行。权限也是老规矩——Skill 要联网就声明联网该最小化就最小化。三、动手把点奶茶递给小艺V哥的示例应用是奶茶点单既有业务里已经有MilkTeaService下单、查单。现在要开放两个能力给系统智能体点单orderMilkTea和查取餐进度queryQueue。入口脚本一个只做翻译的类入口脚本以export default导出一个类类里每个public async方法对应 SKILL.md 声明的一项能力方法名必须与契约里的functionName严格一致第一个参数固定是ArkTSScriptInfo官方约定。V哥的写法// MilkteaSkill.ets —— 薄适配层只翻译参数不装业务import{scriptManager}fromkit.AbilityKit;import{BusinessError}fromkit.BasicServicesKit;// 应用既有业务模块Skill 化前后零改动import{MilkTeaService,OrderResult}from../../../src/main/ets/service/MilkTeaService;exportdefaultclassMilkteaSkill{// 点单品名必填糖度、冰量可选与 SKILL.md 的 args Schema 对应publicasyncorderMilkTea(info:scriptManager.ArkTSScriptInfo,...argv:string[]):Promisevoid{constdrink:stringargv.length0?argv[0].trim():;constsugar:stringargv.length1?argv[1].trim():五分甜;constice:stringargv.length2?argv[2].trim():少冰;// ① 前置校验品名为空直接走参数错误分支不进业务if(drink.length0){awaitthis.report(info,{code:-1,result:{type:result,status:failed,errCode:ERR_INVALID_PARAMS,errMsg:drink name is empty,suggestion:V哥没听清品名你想喝哪杯}});return;}// ② 调既有业务入口脚本不写业务逻辑try{constorder:OrderResultMilkTeaService.createOrder(drink,sugar,ice);// ③ 按契约把业务结果装进回包awaitthis.report(info,{code:0,result:{type:result,status:success,data:{orderNo:order.orderNo,pickupCode:order.pickupCode,etaMinutes:order.etaMinutes}}});}catch(e){consterreasBusinessError;// 业务异常统一映射到内部错误分支awaitthis.report(info,{code:-1,result:{type:result,status:failed,errCode:ERR_INTERNAL,errMsg:err.message,suggestion:下单失败了稍后再试试}});}}// 查询取餐进度契约结构同理略// ④ 唯一回包出口只管上报不参与结果构造privateasyncreport(info:scriptManager.ArkTSScriptInfo,result:scriptManager.ExecuteResult):Promisevoid{try{awaitscriptManager.completeArkTSScriptInApp(info.context,info.requestCode,result);}catch(e){consterreasBusinessError;console.error(completeArkTSScriptInApp failed, code:${err.code}, message:${err.message});}}}四个环节对应官方规定的四步解析校验入参 → 调既有业务 → 按契约构造ExecuteResult→ 经completeArkTSScriptInApp回传。核心接口就三个ExecuteResult脚本执行结果、ArkTSScriptInfo系统传进来的脚本上下文、completeArkTSScriptInApp上报结果都挂在ohos.app.ability.scriptManager下接口参考。V哥最想强调的是那句注释——唯一的回包出口。官方示例把completeArkTSScriptInApp收进一个report私有方法V哥照做了理由很实际回包点一散某个分支忘了报、报错了没人知道系统智能体只会觉得这个 Skill 失联了下次直接不调你。SKILL.md系统智能体的招聘简历SKILL.md 分三段元数据、触发场景、能力契约官方开发指导 · 第 4 节。V哥按自己的示例写了一份--- name: vge-org-milktea-assistant description: 提供奶茶点单与取餐进度查询能力响应点一杯奶茶、帮我点单、 还有多久能取餐等指令 --- ## 触发场景 当用户明确表达**点奶茶**或**查询取餐进度**时调用。典型话术 - 点一杯芝士葡萄五分甜少冰 - 帮我点单要三分甜的珍珠奶茶 - 我的奶茶做到哪了、还要等多久 不调用的情况 - 用户说附近的奶茶店有哪些——意图是搜索店铺本 Skill 只管下单和进度。 - 用户说这杯奶茶多少钱——意图是查价格应走商品查询能力。 - 用户说取消订单——当前版本不支持避免误触发。 ### 场景1点单orderMilkTea 执行参数 exec-cli(command: ohos-arkTSScript --skillName vge-org-milktea-assistant --scriptPath scripts/MilkteaSkill.ets --functionName orderMilkTea --args { arg1: 芝士葡萄, arg2: 五分甜, arg3: 少冰 }) JSON Schemaarg1 品名为必填arg2 糖度、arg3 冰量为可选枚举此处略 执行返回值 先列成功/参数非法/门店未命中/内部错误四组示例再用 oneOf 收口此处略三段各有各的命门元数据name三处一致不赘述description是系统做初次筛选的依据——写奶茶相关服务这种糊涂话筛选这关就过不去。触发场景官方明确建议除了列典型话术还要写**“不调用的情况”**来划清能力边界。V哥的理解不写边界相邻意图全算你头上误触发一多用户和小艺对你的信任一起掉。能力契约每项能力一个exec-cli调用示例加 JSON Schema。anyOf二选一必填、oneOf多种回包分支互斥都是标准 JSON Schema 玩法跟后端同学对过接口规范的会有熟悉感。四、契约质量 分发质量V哥的三条判断写完代码只是及格线Skill 化真正的功夫在契约上。V哥给三条判断① 典型话术要覆盖同义改写不是罗列功能。“点一杯”“来一杯”帮我带一杯是三个说法一个意图全写进典型话术匹配面才够宽。只写功能名的契约跟简历只写岗位名不写经历的候选人一样——不是不能干是没人敢调。② 边界条款是防误触发的保险丝。官方建议每条典型话术配不调用的情况V哥把它当测试用例来写把容易混淆的相邻意图一条条列出来、明确踢出去。误触发一次用户说这 App 抢答漏触发一次用户说这 App 失聪——但误触发的伤害更大因为它透支的是对整个入口的信任。③suggestion字段要写人话。回包里的suggestion是直接给用户看的。V哥见过把内部异常码直接怼进去的写法用户看到的回复是ERR_TIMEOUT_504——这不叫失败提示这叫劝退。错误分支的suggestion按用户下一步能干什么来写。五、上线前自检清单V哥把整条链路压成一张自检表六项全勾再提交#检查项挂了会怎样1Stage 模型FA 模型直接不可用先迁移编译期就过不去2目录名 SKILL.mdnameskillProfiles[].name注册失败或匹配不上无报错3方法名与契约functionName严格一致签名首参ArkTSScriptInfo调用静默失败4每个能力方法都有回包所有分支都走report出口系统侧Skill 失联被降权5exec-cli的skillName/scriptPath/functionName与工程一致匹配上了也调不通6触发场景含不调用的情况suggestion是人话误触发 / 回复劝退开发完成后走官方的真机测试流程调试真机测试别只跑模拟器。六、V哥的收尾判断红利窗口就是现在回看整套机制V哥的总结是Skill 化把应用分发的粒度从 App 降到了功能。以前用户装你的应用才能用你的功能现在你的功能直接进意图分发池被系统智能体按需调用。入口前置了露出变短了长尾功能第一次有机会被说出来。代价是新增了一种工程资产——契约文件。它不是写完就完的话术要随用户表达演进、边界要随能力扩展修订。V哥把它类比成给智能体维护的 API 文档版本化管起来和代码一个待遇。7.0 刚发意图分发池里占位的人还不多。第一期的上架审核那篇V哥写过一句话合规是门票。这一期补上后半句——Skill 化是新的门票门开着的窗口不会一直开着。参考与出处本文涉及的机制、接口与配置项来自以下官方文档基于ArkTS脚本的应用Skill开发指导Ability Kitohos.app.ability.scriptManager 接口参考Skill 真机测试HarmonyOS 新能力一览7 / API 26最后一句图标时代你的功能在等人点意图时代你的功能在被点名——把 SKILL.md 当简历写把薄脚本当翻译写小艺念到名字的那一刻就是你长尾功能翻身的那一天。