文档管理中心
开发与测试开放能力API应用框架Ability Kit(程序框架服务)方舟智能开发框架开发指导基于ArkTS脚本的应用Skill开发指导

基于ArkTS脚本的应用Skill开发指导

概述

从API版本26.0.0开始,Ability Kit支持将应用内业务能力以Skill形式开放给系统智能体调用。Skill提供一种声明式的能力外化机制:开发者将应用内可被外部调用的业务能力组织为若干能力单元,每个单元由一份描述文件(声明其触发场景、入参约束与返回值契约)与一份ArkTS入口脚本(将外部调用桥接到应用内既有业务实现)共同构成,并通过模块配置绑定到指定Ability的运行上下文。运行时,系统智能体依据描述文件完成“意图—能力”的语义匹配,并将结果转化为面向用户的自然语言回复。

通过Skill,开发者得以在不改造既有业务实现的前提下,以薄封装将应用能力开放给系统智能体;系统智能体无需理解各应用的内部实现,仅依赖统一的声明契约即可完成调度。

说明

仅支持Stage模型,FA模型不可用。

接口说明

以下是基于ArkTS脚本开发应用Skill使用的主要接口,更多接口及使用方式请见@ohos.app.ability.scriptManager

展开
接口名 描述
ExecuteResult ArkTS脚本执行结果。
ArkTSScriptInfo 应用的ArkTS脚本入口函数的第一个参数,用于接收系统传递的脚本上下文信息。
completeArkTSScriptInApp(context: Context, requestCode: string, result: ExecuteResult): Promise<void> 完成应用的ArkTS脚本执行,上报执行结果。使用Promise异步回调。

开发步骤

下文以“音乐助手Skill(example-org-music-assistant)”为示例,演示如何在自有应用中,通过代码开发和封装,实现按名称播放音乐(playMusicByName)与播放控制(controlPlayback)的能力。

  1. 创建文件和目录。

    在模块(entry)下按以下结构创建文件和目录:

    收起
    自动换行
    深色代码主题
    复制
    1. Application/
    2. ├── AppScope/
    3. │ ├── app.json5
    4. │ └── resources/
    5. └── entry/
    6. ├── skills/ <- 【固定值】当前模块所有Skill的根目录
    7. │ └── example-org-music-assistant/ <- Skill名,需与SKILL.md的name一致,为防止命名冲突,推荐使用公司或组织名作为前缀
    8. │ ├── scripts/ <- 【固定值】ETS脚本目录
    9. │ │ └── MusicSkill.ets <- Skill入口脚本
    10. │ └── SKILL.md <- 【固定值】Skill描述文件
    11. └── src/
    12. └── main/
    13. ├── ets/
    14. │ ├── entryability/
    15. │ │ └── EntryAbility.ets
    16. │ └── service/
    17. │ └── MusicPlayer.ets <- 应用内业务服务(被Skill入口脚本调用)
    18. ├── module.json5
    19. └── resources/
  2. 配置module.json5文件。

    在entry/src/main/module.json5的module标签下新增skillProfiles标签,将Skill注册到模块。

    示例Skill运行时需要添加网络权限访问云端音乐列表,在module标签下的requestPermissions标签配置。

    收起
    自动换行
    深色代码主题
    复制
    1. {
    2. "module": {
    3. // ...
    4. "skillProfiles": [
    5. {
    6. "name": "example-org-music-assistant", // Skill名,需与SKILL.md的name一致
    7. "abilityName": "EntryAbility", // 与该Skill关联的组件名称
    8. "srcEntries": [ // 实现Skill的代码文件路径列表
    9. "../../skills/example-org-music-assistant/scripts/MusicSkill.ets"
    10. ],
    11. "version": "1.0.0"
    12. }
    13. ],
    14. "requestPermissions": [ // Skill运行需要的权限列表
    15. { "name": "ohos.permission.INTERNET" }
    16. ],
    17. // ...
    18. }
    19. }
  3. 实现ArkTS脚本。

    ArkTS入口脚本(MusicSkill.ets)是Skill调用链路上的“薄适配层”,负责把系统智能体传入的字符串参数转交给应用内已有业务实现,并把业务执行结果按SKILL.md声明的契约回传。其调用的MusicPlayer属于应用既有业务实现,与Skill机制本身无耦合,本节不再展开其内部逻辑。

    3.1 导入Skill相关接口。

    入口脚本需要从@kit.AbilityKit引入scriptManager,同时引入待桥接的应用内业务模块。

    收起
    自动换行
    深色代码主题
    复制
    1. import { scriptManager } from '@kit.AbilityKit';
    2. import { BusinessError } from '@kit.BasicServicesKit';
    3. // 应用既有业务模块
    4. import { MusicPlayer, Track, PlayResult } from '../../../src/main/ets/service/MusicPlayer';

    3.2 定义入口类骨架。

    入口脚本以export default方式导出一个类,类内每个public async方法对应SKILL.md声明的一项能力,须满足以下约定:

    • 方法名约定:必须与SKILL.md中的functionName严格一致(本例为playMusicByName、controlPlayback)。
    • 方法签名约定:第一个参数类型固定为ArkTSScriptInfo
    收起
    自动换行
    深色代码主题
    复制
    1. export default class MusicSkill {
    2. // ...
    3. public async playMusicByName(info: scriptManager.ArkTSScriptInfo, ...argv: string[]): Promise<void> {
    4. /* 见 3.3 ~ 3.5 */
    5. // ...
    6. }
    7. // ...
    8. public async controlPlayback(info: scriptManager.ArkTSScriptInfo, ...argv: string[]): Promise<void> {
    9. /* 同上模式 */
    10. // ...
    11. }
    12. // ...
    13. }

    3.3 解析并校验入参。

    每个能力方法的第一项任务是从argv中按位置获取参数,对照SKILL.md的args Schema完成前置校验。

    收起
    自动换行
    深色代码主题
    复制
    1. // 例1:playMusicByName 的两个可选参数,至少一个非空
    2. const songName: string = argv.length > 0 ? argv[0].trim() : '';
    3. const singer: string = argv.length > 1 ? argv[1].trim() : '';
    4. if (songName.length === 0 && singer.length === 0) {
    5. // 走 ERR_INVALID_PARAMS 分支回包(见 3.5),不再进入业务
    6. // ...
    7. return;
    8. }
    9. // ...
    10. // 例2:controlPlayback 的枚举值参数,需用白名单二次校验
    11. const action: string = argv.length > 0 ? argv[0].trim() : '';
    12. const validActions: string[] = ['pause', 'resume', 'next', 'previous'];
    13. if (!validActions.includes(action)) {
    14. // 走 ERR_INVALID_PARAMS 分支回包
    15. // ...
    16. return;
    17. }

    3.4 调用应用内业务实现。

    校验通过后,调用既有业务接口完成实际任务。入口脚本不承载业务逻辑,仅充当“参数适配器”,读取业务返回值与运行时异常,分别映射到SKILL.md声明的不同结果分支。

    收起
    自动换行
    深色代码主题
    复制
    1. try {
    2. // 直接调用应用内已有业务API
    3. const playResult: PlayResult | null = MusicPlayer.searchAndPlay(songName, singer);
    4. // 业务返回值 → 映射到"成功"或"未命中"分支(见 3.5)
    5. // ...
    6. } catch (e) {
    7. // 业务异常 → 统一映射到 ERR_INTERNAL 分支(见 3.5)
    8. const err = e as BusinessError;
    9. // ...
    10. }

    3.5 按契约构造ExecuteResult并回传。

    业务执行完成后,需将结果封装为ExecuteResult,并通过调用completeArkTSScriptInApp回传给系统智能体,回包内容应与SKILL.md中“执行返回值”声明的分支保持一致。

    收起
    自动换行
    深色代码主题
    复制
    1. // 成功分支示例
    2. const first: Track = playResult.tracks[0];
    3. const playingTrack: Record<string, Object> = {
    4. 'name': first.name,
    5. 'singer': first.singer,
    6. 'duration': first.duration
    7. };
    8. const data: Record<string, Object> = {
    9. 'playingTrack': playingTrack,
    10. 'matchedCount': playResult.tracks.length
    11. };
    12. const payload: Record<string, Object> = {
    13. 'type': 'result',
    14. 'status': 'success',
    15. 'data': data
    16. };
    17. await this.report(info, { code: 0, result: payload });
    18. // ...
    19. // 失败分支示例(以 ERR_NOT_FOUND 为例)
    20. const payloadData: Record<string, Object> = {
    21. 'searchedKeywords': []
    22. };
    23. const payload: Record<string, Object> = {
    24. 'type': 'result',
    25. 'status': 'failed',
    26. 'errCode': 'ERR_NOT_FOUND',
    27. 'data': payloadData,
    28. 'suggestion': `没有找到${singer}${songName.length > 0 ? `的《${songName}》` : '相关歌曲'}`
    29. };
    30. await this.report(info, { code: -1, result: payload });
    31. // ...
    32. // 唯一的回包出口:只封装API调用与异常打印,不参与结果构造
    33. private async report(info: scriptManager.ArkTSScriptInfo, result: scriptManager.ExecuteResult): Promise<void> {
    34. try {
    35. await scriptManager.completeArkTSScriptInApp(info.context, info.requestCode, result);
    36. } catch (e) {
    37. const err = e as BusinessError;
    38. console.error(`completeArkTSScriptInApp failed, code: ${err.code}, message: ${err.message}`);
    39. }
    40. }
  4. 编写SKILL.md。

    SKILL.md是Skill的声明契约文件,是系统智能体进行“意图—能力”匹配的唯一依据。主要由“元数据->触发场景->能力契约”三段构成。

    4.1 撰写元数据(YAML Front Matter)。

    在文件头部使用YAML Front Matter声明 name 与 description。其中,name 必须与 Skill 目录名以及 module.json5 中 skillProfiles[].name 完全保持一致;description 应简洁地描述能力范围,作为系统智能体进行 Skill 初次筛选的关键依据。

    收起
    自动换行
    深色代码主题
    复制
    1. ---
    2. name: example-org-music-assistant
    3. description: 提供音乐搜索播放与播控能力,响应“放首歌”、“切歌”、“暂停”等播放控制类指令
    4. ---

    4.2 撰写触发场景。

    用自然语言列出典型话术,并补充不要调用的情况以划清能力边界,降低误触发。典型话术应覆盖能力对应的多种说法,边界说明应覆盖容易混淆的相邻意图。

    收起
    自动换行
    深色代码主题
    复制
    1. ## 触发场景
    2. 当用户明确表达**播放音乐**或**控制当前播放**时调用。典型话术:
    3. - “放一首SingerA的《SongA》”
    4. - “来首《SongA》”
    5. - “暂停”、“继续播放”
    6. - “切歌”、“下一首”、“上一曲”
    7. 不调用的情况:
    8. - 用户说“把这首歌加入收藏”——意图是修改歌单,本Skill仅支持播放与播控。
    9. - 用户说“今天有什么演唱会”——意图是查询资讯,非播放。
    10. - 用户没有明确指向播放(如“这首歌真好听”)——情绪表达,无需调用。
    11. - 用户说“调小音量”——意图是系统音量控制,应走系统能力。

    4.3 为每项能力撰写“执行参数”契约。

    每项能力对应一个### 场景N:能力名(functionName)子小节,“执行参数”部分需包含exec-cli形式的调用示例和一份JSON Schema约束。

    调用示例包含四个核心字段:command固定为ohos-arkTSScript;skillName需与SKILL.md中的name保持一致;scriptPath为相对于Skill目录的入口脚本路径;functionName必须与MusicSkill.ets中public方法名严格对应。

    收起
    自动换行
    深色代码主题
    复制
    1. exec-cli(command: ohos-arkTSScript --skillName 'example-org-music-assistant' --scriptPath 'scripts/MusicSkill.ets' --functionName 'playMusicByName' --args '{
    2. "arg1": "SongA",
    3. "arg2": "SingerA"
    4. }'
    5. )

    Schema的核心在于args子对象,它定义了系统智能体可填写的入参结构。playMusicByName支持“歌名”和“歌手”二选一填写,因此使用anyOf约束以确保至少包含其中之一:

    收起
    自动换行
    深色代码主题
    复制
    1. "args": {
    2. "type": "object",
    3. "properties": {
    4. "arg1": {
    5. "type": "string",
    6. "description": "歌曲名,如《SongA》"
    7. },
    8. "arg2": {
    9. "type": "string",
    10. "description": "歌手名,如SingerA"
    11. }
    12. },
    13. "anyOf": [
    14. { "required": ["arg1"] },
    15. { "required": ["arg2"] }
    16. ]
    17. }

    4.4 为每项能力撰写“执行返回值”契约。

    “执行返回值”部分需先列出所有可能的结果示例(成功 + 各类失败),再给出整体的JSON Schema约束。

    以playMusicByName为例,需逐一列出四组示例:

    收起
    自动换行
    深色代码主题
    复制
    1. // 1. 成功播放
    2. {
    3. "type": "result",
    4. "status": "success",
    5. "data": {
    6. "playingTrack": {
    7. "name": "SongA",
    8. "singer": "SingerA",
    9. "duration": 269
    10. },
    11. "matchedCount": 1
    12. }
    13. }
    收起
    自动换行
    深色代码主题
    复制
    1. // 2. 入参非法
    2. {
    3. "type": "result",
    4. "status": "failed",
    5. "errCode": "ERR_INVALID_PARAMS",
    6. "errMsg": "songName and singer are both empty",
    7. "suggestion": "我没听清,你想听哪首歌?"
    8. }
    收起
    自动换行
    深色代码主题
    复制
    1. // 3. 未命中
    2. {
    3. "type": "result",
    4. "status": "failed",
    5. "errCode": "ERR_NOT_FOUND",
    6. "data": {
    7. "searchedKeywords": ["SongA", "SingerA"]
    8. },
    9. "suggestion": "没有找到SingerA的《SongA》"
    10. }
    收起
    自动换行
    深色代码主题
    复制
    1. // 4. 内部错误
    2. {
    3. "type": "result",
    4. "status": "failed",
    5. "errCode": "ERR_INTERNAL",
    6. "errMsg": "network timeout",
    7. "suggestion": "播放失败了,稍后再试试"
    8. }

    对应的Schema顶层定义公共字段,并通过oneOf枚举上述四种分支形态:

    收起
    自动换行
    深色代码主题
    复制
    1. {
    2. "type": "object",
    3. "required": ["type", "status"],
    4. "properties": {
    5. "type": { "type": "string", "const": "result" },
    6. "status": { "type": "string", "enum": ["success", "failed"] },
    7. "data": { "type": "object" },
    8. "errCode": {
    9. "type": "string",
    10. "enum": ["ERR_INVALID_PARAMS", "ERR_NOT_FOUND", "ERR_INTERNAL"]
    11. },
    12. "errMsg": { "type": "string", "minLength": 1 },
    13. "suggestion": { "type": "string", "minLength": 1 }
    14. },
    15. "oneOf": [
    16. /* 成功: 要求 data.playingTrack 与 data.matchedCount */
    17. /* ERR_INVALID_PARAMS: 要求 errMsg 与 suggestion */
    18. /* ERR_NOT_FOUND: 要求 data.searchedKeywords 与 suggestion */
    19. /* ERR_INTERNAL: 要求 errMsg 与 suggestion */
    20. ]
    21. }
  5. 在完成Skill开发后,请参考真机测试进行调试。

在 开发与测试 开放能力API 中进行搜索
请输入您想要搜索的关键词