欢迎光临
我们一直在努力

HarmonyOS应用<奇妙科学乐园>开发第2篇:Vibe Coding开发流程——从需求描述到应用上线

img

📖 引言

在上一篇《Agent与Skill框架重构——端侧AI能力全面下放》中,我们深入了解了HarmonyOS 7(API 26)带来的Agent智能体与标准化Skill开发体系。本篇将视角从框架原理转向实战工作流,系统讲解如何借助Vibe Coding理念与DevEco Code AI Agent工具,将自然语言需求转化为可运行的应用代码,并完成Skill的全流程开发、审核与多设备分发。

Vibe Coding(氛围编程)是2026年HarmonyOS生态最重要的开发范式变革——开发者用自然语言描述需求,AI Agent理解意图、拆解任务、生成代码、编译构建、本地调测,最终完成端到端交付。对于"奇妙科学乐园"这类儿童科普应用,Vibe Coding可以极大地加速科学问答Skill的开发、智能交互能力的接入以及多设备适配的效率。

通过本文,你将掌握:

  • Vibe Coding的核心理念及其在HarmonyOS开发中的落地方式
  • DevEco Code AI Agent的三种工作模式与完整开发流程
  • Skill从创建、编码、配置到测试、审核、分发的全生命周期
  • 小艺智能入口的接入方式与语音交互实现
  • 用Vibe Coding为"奇妙科学乐园"快速开发智能问答Skill的完整实战

🎯 学习目标

完成本文后,你将能够:

  • ✅ 理解Vibe Coding"意图即服务"的核心理念,区分传统编码与AI辅助编码的差异
  • ✅ 使用DevEco Code AI Agent进行需求分析、代码生成、本地调测的完整开发闭环
  • ✅ 独立完成Skill的SKILL.md编写、ArkTS入口脚本开发、module.json5配置
  • ✅ 接入小艺智能入口,实现语音驱动的科学问答交互
  • ✅ 掌握多设备适配策略与智能体市场审核分发流程

💡 需求分析

功能模块设计

本文涉及的知识点覆盖了从开发工具链到应用分发的完整链路,核心模块设计如下:

模块功能描述技术要点
Vibe Coding理念 理解"意图即服务"开发范式,从自然语言到可运行代码 DevEco Code Agent模式、GLM-5.1模型、Skill自动加载
DevEco Code工作流 需求分析→代码生成→本地调测→优化的完整闭环 Build/Plan/Goal三种Agent模式、DEVECO_HOME环境配置
Skill开发全流程 创建→编码→配置→测试→审核→分发 SKILL.md编写、ArkTS入口脚本、skillProfiles配置
小艺智能入口接入 系统级语音交互,用户一句话触发应用能力 系统智能体意图匹配、ExecuteResult回传、suggestion字段
实战:智能问答Skill 为"奇妙科学乐园"开发科学问答Skill 知识库查询、参数校验、错误兜底、触发场景定义
多设备适配与分发 手机/平板/车机等多终端适配与市场分发 智能体市场上架、设备能力预查询、版本管理

整体流程概览

Vibe Coding开发流程的核心链路可以概括为:

开发者自然语言描述需求

DevEco Code AI Agent 理解意图、拆解任务

自动生成 Skill 代码(SKILL.md + ArkTS入口 + 配置)

本地编译构建、模拟器/真机调测

优化完善 → 提交智能体市场审核

审核通过 → 多设备分发上线


🛠️ 核心实现

步骤1: Vibe Coding理念——意图即服务

功能说明

Vibe Coding(氛围编程)是2026年最受关注的开发范式。其核心理念是:开发者用自然语言表达需求意图,AI Agent负责将意图转化为可运行代码。在HarmonyOS 7生态中,这一理念通过DevEco Code工具链和Skill开发体系完整落地。

与传统编码相比,Vibe Coding带来三个根本性变化:

维度传统编码Vibe Coding
需求表达 技术规格文档、UML图、伪代码 自然语言对话描述业务意图
代码生成 手动逐行编写 AI理解意图后自动生成
调试修复 人工定位错误、查文档 AI分析编译/运行日志自动修复
核心认知:Vibe Coding不等于"AI代写代码"

💡 重要提示:Vibe Coding不是让开发者"躺着不动",而是将开发者从重复性编码工作中解放出来,让精力集中在业务逻辑设计、用户体验打磨和场景创新上。

在HarmonyOS生态中,Vibe Coding有三个关键支撑:

  • DevEco Code AI Agent:内置GLM-5.1免费模型,专门训练了ArkTS/ArkUI知识库
  • 标准化Skill体系:SKILL.md声明式描述 + ArkTS薄适配层的开发模式
  • 小艺智能入口:系统级AI智能体完成意图匹配,用户语音直达应用能力
  • 代码示例:传统开发 vs Vibe Coding

    以创建一个"科学问答Skill"为例,对比两种开发方式:

    传统开发流程:

    1. 查阅Skill开发文档,理解目录结构和配置规范
    2. 手动创建 skills/science-qa/ 目录
    3. 编写 SKILL.md(触发场景、参数契约、返回值契约)
    4. 编写 ArkTS 入口脚本(参数解析、业务调用、结果回传)
    5. 修改 module.json5 添加 skillProfiles
    6. 编译、运行、调试
    7. 出错后查文档、搜索、修复
    预计耗时:2-3天

    Vibe Coding流程:

    1. 在 DevEco Code 中输入:
    "为奇妙科学乐园创建一个科学问答Skill,
    支持太空、自然、海洋等主题的智能问答,
    能回答'太阳系有几颗行星'这类问题"

    2. AI Agent 自动:
    – 分析需求,确定Skill类型
    – 生成 SKILL.md + ArkTS入口脚本 + module.json5配置
    – 编译构建,推送到模拟器
    3. 开发者审查生成的代码,用自然语言指出需要调整的地方
    4. AI Agent 自动修改、重新构建
    预计耗时:30分钟-2小时


    步骤2: DevEco Code AI Agent工作流程

    功能说明

    DevEco Code是华为基于开源项目OpenCode扩展开发的HarmonyOS专属AI Agent工具。它内置GLM-5.1免费模型、5大鸿蒙专属Skill和3种Agent模式,支持代码生成、编译构建、错误修复、文档搜索全链路开发。

    安装与配置

    DevEco Code的安装只需一行命令:

    # 全局安装 DevEco Code
    npm install -g @deveco/deveco-code

    # 验证安装
    deveco –version
    # 输出: 0.1.0 即安装成功

    # 启动 TUI 交互界面
    deveco

    环境要求:

    • Node.js >= 18(推荐22+)
    • DevEco Studio 26.0.0+(API 26 Beta1)
    • 华为开发者账号(登录后解锁GLM-5.1免费额度)

    DEVECO_HOME环境变量配置:

    # Windows PowerShell 设置环境变量
    [System.Environment]::SetEnvironmentVariable('DEVECO_HOME', 'C:\\Program Files\\Huawei\\DevEco Studio', 'User')

    # 验证
    echo $env:DEVECO_HOME

    三种Agent模式

    DevEco Code提供三种Agent模式,通过Tab键切换:

    Agent模式适用场景工作方式
    Build(默认) 日常代码生成、修改、编译、推包 直接动手型,能调DevEco Studio工具链
    Plan 复杂需求拆解、技术方案输出 先规划再行动,适合多页面工程
    Goal 从需求描述到完整交付 SDD五阶段端到端交付,最省心
    五大内置Skill

    DevEco Code内置的5个Skill会在对话中自动加载,这是它与通用AI工具(如ChatGPT)的本质区别:

    Skill名称自动触发时机作用
    deveco-create-project 说"创建项目"、"新建工程" 标准模板创建ArkTS工程,自动检测API Level
    arkts-grammar-standards 写或改.ets文件时 强制遵守ArkTS语法规范,拒绝any/as/模板字符串
    arkts-runtime-fix 出现运行时崩溃日志时 分析crash日志,给出修复方案
    arkui-knowledge 涉及UI组件布局时 调用ArkUI组件知识库,给出正确的组件用法
    arkts-error-fixes 编译报错时 针对常见ArkTS编译错误给出标准解法
    完整代码:用DevEco Code创建Skill项目

    以下是使用DevEco Code进行需求分析→代码生成的完整交互示例:

    // =============================================
    // DevEco Code 交互示例
    // 在 DevEco Code TUI 中输入以下需求描述:
    // =============================================

    // 开发者输入:
    // "帮我为奇妙科学乐园创建一个科学问答Skill,
    // 支持以下功能:
    // 1. 回答太空、自然、海洋、科技、人体、天气六大主题的科学问题
    // 2. 支持语音触发,如'小艺,太阳系有几颗行星'
    // 3. 对于不确定的问题,返回友好的提示语
    // 4. Skill名称为 science-qa"

    // AI Agent 会自动执行以下步骤:
    // 1. 分析需求,确定Skill类型和功能范围
    // 2. 生成目录结构和文件
    // 3. 编写 SKILL.md(触发场景、参数契约、返回值契约)
    // 4. 编写 ArkTS 入口脚本
    // 5. 修改 module.json5 配置
    // 6. 尝试编译构建

    代码解析

    1. 需求分析阶段——AI Agent如何理解意图

    DevEco Code在接收到自然语言需求后,会通过以下步骤进行意图分析:

    自然语言输入 → GLM-5.1语义理解 → 提取关键信息

    ├── Skill名称
    : science-qa
    ├── 功能类型: 知识问答
    ├── 触发方式: 语音/文字
    ├── 主题范围: 太空/自然/海洋/科技/人体/天气
    └── 错误策略: 友好提示语

    2. 代码生成阶段——ArkTS语法规范自动遵守

    与通用AI工具不同,DevEco Code内置的arkts-grammar-standards Skill会确保生成的代码符合ArkTS严格模式规范:

    // ❌ 通用AI工具可能生成的代码(ArkTS严格模式报错)
    // 使用了 any、as 类型断言、模板字符串等ArkTS禁止的语法

    function queryScience(topic: any, question: string): any {
    const result = data as Record<string, string>;
    return `答案是:${result.answer}`;
    }

    // ✅ DevEco Code 生成的代码(完全符合ArkTS规范)
    // 使用明确类型定义、Record类型、字符串拼接

    function queryScience(
    topic: string,
    question: string
    ): ScienceAnswer | null {
    const result: ScienceAnswer | null = ScienceDataService.search(topic, question);
    if (result === null) {
    return null;
    }
    return result;
    }

    3. 常用命令速查

    deveco # 启动TUI对话界面(默认Build Agent + GLM-5.1)
    deveco –agent plan # 以Plan模式启动(适合复杂需求拆解)
    deveco –agent goal # 以Goal模式启动(端到端交付)
    deveco –continue # 续接上次会话(保留上下文)
    deveco models # 查看可用模型列表
    deveco stats # 查看Token用量和费用统计
    deveco session list # 查看历史会话列表
    deveco upgrade # 升级到最新版本


    步骤3: Skill开发全流程——创建→编码→配置→测试→审核→分发

    功能说明

    Skill是HarmonyOS 7引入的声明式能力外化机制。通过编写SKILL.md描述文件和ArkTS入口脚本,将应用内的业务功能对外开放,让系统AI智能体(小艺)可以"一句话调用"你的应用能力。

    完整代码:Skill目录结构

    entry/
    ├── skills/ ← 【固定目录名】Skill根目录
    │ └── science-qa/ ← Skill名(三处一致)
    │ ├── scripts/ ← 【固定目录名】脚本目录
    │ │ └── ScienceQASkill.ets ← ArkTS入口脚本
    │ └── SKILL.md ← 【固定文件名】描述文件
    └── src/
    └── main/
    ├── ets/
    │ ├── service/
    │ │ └── ScienceDataService.ets ← 应用内已有的业务服务
    │ └── entryability/
    │ └── EntryAbility.ets
    ├── module.json5 ← 在此注册skillProfiles
    └── resources/

    ⚠️ 关键约束:Skill目录名science-qa、SKILL.md中的name字段、module.json5中skillProfiles的name,三者必须完全一致,否则Skill注册失败。

    完整代码:SKILL.md——Skill的灵魂文件

    SKILL.md是系统AI智能体进行"意图→能力"匹配的唯一依据。以下是为"奇妙科学乐园"编写的完整SKILL.md:


    name: science-qa
    description: 提供儿童科学知识问答能力,覆盖太空、自然、海洋、科技、人体、天气六大主题,
    响应"太阳系有几颗行星""水循环是怎么回事""人体有多少块骨骼"等科学类指令

    ## 触发场景

    当用户询问**科学知识相关的问题**时调用。典型话术:

    "太阳系有几颗行星"
    "水循环是怎么回事"
    "人体有多少块骨骼"
    "为什么天空是蓝色的"
    "最大的海洋是什么"
    "闪电是怎么产生的"
    "光合作用是什么原理"
    "小艺讲个太空故事"

    不调用的情况:

    – 用户说"帮我设个闹钟"——这是系统工具功能,不是科学问答
    – 用户说"今天天气怎么样"——这是天气查询,不是科学知识科普
    – 用户说"播放一首歌"——这是媒体控制,与科学无关
    – 用户说"打开微信"——这是应用启动,不是知识问答
    – 用户问数学计算题"3加5等于几"——这是数学计算,不是科学知识

    ### 场景1:查询科学知识(queryScience)

    #### 执行参数

    exec-cli(command: ohos-arkTSScript –skillName 'science-qa'
    –scriptPath 'scripts/ScienceQASkill.ets'
    –functionName 'queryScience'
    –args '{"arg1": "太空", "arg2": "太阳系有几颗行星"}'
    )

    参数Schema:

    ```json
    {
    "args": {
    "type": "object",
    "properties": {
    "arg1": {
    "type": "string",
    "description": "主题分类,如太空、自然、海洋、科技、人体、天气"
    },
    "arg2": {
    "type": "string",
    "description": "具体的科学问题"
    }
    },
    "required": ["arg2"]
    }
    }

    执行返回值

    // 1. 查询成功{ "type": "result", "status": "success", "data": { "topic": "太空", "question": "太阳系有几颗行星", "answer": "太阳系目前有8颗行星,按离太阳从近到远的顺序分别是: 水星、金星、地球、火星、木星、土星、天王星和海王星。", "funFact": "冥王星在2006年被重新分类为矮行星哦!", "difficulty": "简单" }}

    // 2. 问题未找到{ "type": "result", "status": "failed", "errCode": "ERR_NOT_FOUND", "data": { "searchedQuestion": "宇宙的外面是什么" }, "suggestion": "这个问题目前超出了我的知识范围, 不过好奇心是科学探索的第一步!你可以问问关于太空、自然、 海洋、科技、人体或天气方面的问题哦。"}

    // 3. 参数缺失{ "type": "result", "status": "failed", "errCode": "ERR_INVALID_PARAMS", "errMsg": "question is required", "suggestion": "请告诉我你想了解什么科学知识呢?"}

    #### 完整代码:ArkTS入口脚本(薄适配层)

    ```typescript
    // entry/skills/science-qa/scripts/ScienceQASkill.ets

    import { scriptManager } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';

    // 导入应用内已有的科学数据服务
    import {
    ScienceDataService,
    type ScienceAnswer
    } from '../../../src/main/ets/service/ScienceDataService';

    /**
    * 科学问答Skill入口类
    * 每个public async方法对应SKILL.md中声明的一项能力
    * 方法名必须与SKILL.md中的functionName严格一致
    */

    export default class ScienceQASkill {

    /**
    * 查询科学知识
    * @param info – 系统注入的脚本执行信息(含context和requestCode)
    * @param argv – AI智能体传入的参数列表(按位置排列)
    */

    public async queryScience(
    info: scriptManager.ArkTSScriptInfo,
    …argv: string[]
    ): Promise<void>
    {
    // 解析参数:arg1为主题(可选),arg2为问题(必填)
    const topic: string = argv.length > 0 ? argv[0].trim() : '';
    const question: string = argv.length > 1 ? argv[1].trim() :
    (argv.length > 0 ? argv[0].trim() : '');

    // 参数校验:问题不能为空
    if (question.length === 0) {
    const payload: Record<string, Object> = {
    'type': 'result',
    'status': 'failed',
    'errCode': 'ERR_INVALID_PARAMS',
    'errMsg': 'question is required',
    'suggestion': '请告诉我你想了解什么科学知识呢?'
    };
    await this.report(info, { code: -1, result: payload });
    return;
    }

    // 调用应用内已有的业务服务查询答案
    try {
    const answer: ScienceAnswer | null =
    ScienceDataService.search(topic, question);

    if (answer === null) {
    // 未找到匹配的答案
    const payload: Record<string, Object> = {
    'type': 'result',
    'status': 'failed',
    'errCode': 'ERR_NOT_FOUND',
    'data': { 'searchedQuestion': question },
    'suggestion': '这个问题目前超出了我的知识范围,' +
    '不过好奇心是科学探索的第一步!' +
    '你可以问问关于太空、自然、海洋、科技、人体或天气方面的问题哦。'
    };
    await this.report(info, { code: -1, result: payload });
    return;
    }

    // 查询成功,构造结果回传
    const data: Record<string, Object> = {
    'topic': answer.topic,
    'question': answer.question,
    'answer': answer.content,
    'funFact': answer.funFact,
    'difficulty': answer.difficulty
    };
    const payload: Record<string, Object> = {
    'type': 'result',
    'status': 'success',
    'data': data
    };
    await this.report(info, { code: 0, result: payload });
    } catch (e) {
    // 异常处理:分类捕获,输出可读中文错误信息
    const err = e as BusinessError;
    const payload: Record<string, Object> = {
    'type': 'result',
    'status': 'failed',
    'errCode': 'ERR_INTERNAL',
    'errMsg': err.message,
    'suggestion': '查询科学知识时出了点问题,稍后再试试吧!'
    };
    await this.report(info, { code: -1, result: payload });
    }
    }

    /**
    * 统一封装结果回传方法
    * 所有业务分支的结果回传都通过此方法
    * @param info – 系统注入的脚本执行信息
    * @param result – 执行结果(包含code、result、uris、flags)
    */

    private async report(
    info: scriptManager.ArkTSScriptInfo,
    result: scriptManager.ExecuteResult
    ): Promise<void>
    {
    try {
    await scriptManager.completeArkTSScriptInApp(
    info.context,
    info.requestCode,
    result
    );
    } catch (e) {
    const err = e as BusinessError;
    console.error(
    `[ScienceQASkill] 回传结果失败, code: ${err.code}, message: ${err.message}`
    );
    }
    }
    }

    代码解析

    1. 入口脚本的"薄适配层"设计理念

    Skill入口脚本的设计遵循"薄适配层"原则——它不承载业务逻辑,只做三件事:接参数 → 调业务 → 报结果。这意味着:

    // ✅ 正确:薄适配层设计,复用已有业务代码
    const answer: ScienceAnswer | null =
    ScienceDataService.search(topic, question);

    // ❌ 错误:在Skill入口脚本中直接写业务逻辑
    const answer: string = '';
    if (topic === '太空' && question.includes('行星')) {
    answer = '太阳系有8颗行星…';
    } else if (topic === '自然' && question.includes('水循环')) {
    answer = '水循环是指…';
    }

    原理:

    • 入口脚本只是AI与App之间的桥梁
    • 业务逻辑应放在App内部的Service层中
    • 这样做的好处是:原有业务代码一行都不用改,Skill只是增加了一个入口

    2. 参数校验的重要性

    AI智能体传进来的参数都是string类型,需要开发者自己校验:

    // ✅ 正确:严格校验必填参数
    const question: string = argv.length > 1 ? argv[1].trim() :
    (argv.length > 0 ? argv[0].trim() : '');
    if (question.length === 0) {
    // 返回友好的错误提示(suggestion字段)
    await this.report(info, { code: -1, result: payload });
    return;
    }

    // ❌ 错误:不校验参数,直接使用
    const question: string = argv[1]; // 可能越界或为空
    const result = ScienceDataService.search(topic, question); // 查询结果不可控

    3. suggestion字段——决定用户体验的关键

    // ✅ 正确:suggestion使用儿童友好的语言
    'suggestion': '这个问题目前超出了我的知识范围,' +
    '不过好奇心是科学探索的第一步!' +
    '你可以问问关于太空、自然、海洋、科技、人体或天气方面的问题哦。'

    // ❌ 错误:suggestion是冰冷的系统语言
    'suggestion': 'ERR_NOT_FOUND: 该问题无匹配结果'

    完整代码:module.json5配置

    // entry/src/main/module.json5
    {
    "module": {
    "name": "entry",
    "type": "entry",

    // Skill注册配置
    "skillProfiles": [
    {
    // 必须与目录名、SKILL.md的name三者一致
    "name": "science-qa",
    // 关联的Ability
    "abilityName": "EntryAbility",
    // 脚本路径(相对于 src/main/
    "srcEntries": [
    "../../skills/science-qa/scripts/ScienceQASkill.ets"
    ]
    }
    ],

    "abilities": [
    {
    "name": "EntryAbility",
    "srcEntry": "./ets/entryability/EntryAbility.ets",
    // …其他配置
    }
    ],

    "requestPermissions": [
    // Skill运行所需的权限
    ]
    }
    }


    步骤4: 小艺智能入口接入——系统级语音交互

    功能说明

    HarmonyOS 7的小艺智能体已经接入了超过2000个鸿蒙智能体和2100多项系统能力。开发者开发的Skill通过小艺搜索、系统导航条、小艺建议等系统级入口被用户触达。用户只需一句话,就能触发应用功能。

    整体调用链路

    理解小艺智能入口的工作链路,有助于正确编写SKILL.md:

    用户语音/文字输入:"小艺,太阳系有几颗行星"

    系统智能体解析意图(NLU语义理解)

    匹配所有Skill的SKILL.md触发场景

    命中 science-qa Skill 的触发条件

    exec-cli 构造调用参数
    –skillName 'science-qa'
    –functionName 'queryScience'
    –args '{"arg1": "太空", "arg2": "太阳系有几颗行星"}'

    调用 ArkTS 入口脚本 ScienceQASkill.ets

    解析参数 → 校验 → 调用 ScienceDataService.search()

    按返回值契约构造 ExecuteResult

    调用 completeArkTSScriptInApp 回传结果

    系统智能体将结果转换为自然语言回复用户

    用户听到:"太阳系目前有8颗行星,按离太阳从近到远的顺序分别是:
    水星、金星、地球、火星、木星、土星、天王星和海王星。
    冥王星在2006年被重新分类为矮行星哦!"

    代码解析:触发场景的精准定义

    SKILL.md中的触发场景定义直接决定Skill能否被正确调用。以下是关键技巧:

    <!– ✅ 正确:触发场景包含正向示例和反向排除 –>
    ## 触发场景
    当用户询问科学知识相关的问题时调用:
    – "太阳系有几颗行星" ← 正向示例
    – "水循环是怎么回事" ← 正向示例

    不调用的情况:
    – "今天天气怎么样" ← 反向排除:天气查询≠科学知识
    – "3加5等于几" ← 反向排除:数学计算≠科学知识
    – "帮我打电话给妈妈" ← 反向排除:通信功能≠科学知识

    <!– ❌ 错误:触发场景过于宽泛 –>
    ## 触发场景
    当用户问问题时调用。
    → 这会导致所有问题都触发这个Skill,误触发率极高

    完整代码:核心接口速查

    // Skill开发涉及的三个核心接口

    // 1. ArkTSScriptInfo —— 入口函数的首参,系统注入
    interface ArkTSScriptInfo {
    context: UIAbilityContext; // 绑定的Ability上下文
    requestCode: number; // 当前请求的唯一标识码
    }

    // 2. ExecuteResult —— 脚本执行结果
    interface ExecuteResult {
    code: number; // 结果码,0为成功,非0为失败
    result?: Record<string, Object>; // 结果内容
    uris?: Array<string>; // 需要授权给调用方的URI列表
    flags?: number; // URI读写权限
    }

    // 3. completeArkTSScriptInApp —— 上报执行结果
    // 不管成功还是失败,都必须调用此接口回传结果
    // 否则系统侧会超时等待
    await scriptManager.completeArkTSScriptInApp(
    info.context, // Ability上下文
    info.requestCode, // 请求标识码(必须原样传回)
    result // ExecuteResult结果对象
    );


    步骤5: 实战——为"奇妙科学乐园"开发智能问答Skill

    功能说明

    本步骤将前面所有知识串联起来,完成"奇妙科学乐园"智能问答Skill的完整开发流程。我们将使用Vibe Coding方式,通过DevEco Code完成从需求描述到代码生成的全过程。

    完整代码:ScienceDataService业务服务

    在编写Skill入口脚本之前,先确认应用内已有的科学数据服务:

    // entry/src/main/ets/service/ScienceDataService.ets

    /**
    * 科学数据服务
    * 负责从本地数据源搜索科学知识答案
    */

    export class ScienceDataService {

    /**
    * 搜索科学知识答案
    * @param topic – 主题分类(太空、自然、海洋、科技、人体、天气)
    * @param question – 用户提出的问题
    * @returns 匹配的科学答案,未找到返回null
    */

    static search(topic: string, question: string): ScienceAnswer | null {
    // 从ScienceData单例获取全量数据
    const allTopics: Topic[] = ScienceData.getInstance().getAllTopics();

    // 遍历所有主题文章,匹配问题关键词
    for (let i = 0; i < allTopics.length; i++) {
    const currentTopic: Topic = allTopics[i];
    // 主题匹配(可选)
    if (topic.length > 0 && currentTopic.category !== topic) {
    continue;
    }
    // 关键词匹配
    if (this.isQuestionMatched(question, currentTopic)) {
    return {
    topic: currentTopic.category,
    question: question,
    content: currentTopic.content,
    funFact: currentTopic.funFact,
    difficulty: currentTopic.difficulty
    };
    }
    }
    return null;
    }

    /**
    * 判断问题是否与文章内容匹配
    * @param question – 用户问题
    * @param topic – 文章数据
    * @returns 是否匹配
    */

    private static isQuestionMatched(question: string, topic: Topic): boolean {
    // 提取问题中的关键词
    const keywords: string[] = this.extractKeywords(question);
    // 在文章标题和内容中搜索关键词
    const searchField: string = topic.title + topic.content;
    let matchCount: number = 0;
    for (let i = 0; i < keywords.length; i++) {
    if (searchField.includes(keywords[i])) {
    matchCount++;
    }
    }
    // 至少匹配一个关键词即认为匹配
    return matchCount > 0;
    }

    /**
    * 从问题中提取关键词
    * @param question – 用户问题
    * @returns 关键词数组
    */

    private static extractKeywords(question: string): string[] {
    // 去除常见疑问词,提取核心词
    const stopWords: string[] = [
    '的', '是', '什么', '为什么', '怎么', '多少',
    '有', '能', '会', '可以', '吗', '呢', '啊', '吧'
    ];
    let cleaned: string = question;
    for (let i = 0; i < stopWords.length; i++) {
    cleaned = cleaned.replace(new RegExp(stopWords[i], 'g'), '');
    }
    // 按空格分割关键词
    return cleaned.split(' ').filter((word: string) => word.length > 0);
    }
    }

    /**
    * 科学答案数据模型
    */

    export interface ScienceAnswer {
    topic: string; // 主题分类
    question: string; // 用户问题
    content: string; // 答案内容
    funFact: string; // 趣味冷知识
    difficulty: string; // 难度等级
    }

    /**
    * 文章数据模型(简化版)
    */

    interface Topic {
    title: string;
    category: string;
    content: string;
    funFact: string;
    difficulty: string;
    }

    Vibe Coding实战:用DevEco Code生成Skill

    以下是完整的Vibe Coding交互过程,展示如何用自然语言驱动AI完成Skill开发:

    === 第1轮对话:创建Skill骨架 ===

    开发者输入:
    "为奇妙科学乐园创建一个科学问答Skill,
    名称为 science-qa,关联 EntryAbility。
    支持查询太空、自然、海洋、科技、人体、天气六大主题的科学知识。
    对于找不到答案的问题,返回儿童友好的提示语。"

    AI Agent执行:
    创建 entry/skills/science-qa/ 目录结构
    生成 SKILL.md(含触发场景、参数契约、返回值契约)
    生成 scripts/ScienceQASkill.ets 入口脚本
    修改 module.json5 添加 skillProfiles
    执行编译验证

    === 第2轮对话:调整触发场景 ===

    开发者输入:
    "SKILL.md的触发场景需要更精确:
    1. 排除数学计算类问题,比如'3加5等于几'
    2. 排除天气查询类问题,比如'今天出门要带伞吗'
    3. 增加'小艺讲个科学故事'的触发"

    AI Agent执行:
    读取当前 SKILL.md
    修改触发场景部分,添加排除规则
    保存文件,重新编译

    === 第3轮对话:优化返回结果 ===

    开发者输入:
    "queryScience的返回结果需要增加以下字段:
    1. relatedTopics – 相关主题推荐(最多3个)
    2. ageRange – 适合的年龄范围
    同时更新SKILL.md的返回值契约"

    AI Agent执行:
    读取 ScienceQASkill.ets
    在返回的data中添加 relatedTopics 和 ageRange 字段
    同步更新 SKILL.md 的返回值Schema
    编译验证通过

    === 第4轮对话:本地调测 ===

    开发者输入:
    "帮我模拟一个测试:用'太阳系有几颗行星'这个问问题调一下 science-qa Skill"

    AI Agent执行:
    在模拟器/真机上执行Skill测试
    输出测试结果:
    ✅ 触发成功
    ✅ 参数解析正确 (topic="太空", question="太阳系有几颗行星")
    ✅ 查询成功,返回8颗行星的答案
    ✅ suggestion中包含冥王星冷知识

    代码解析

    1. Vibe Coding的迭代式开发模式

    上面的交互展示了Vibe Coding的核心工作方式——对话式迭代开发。与传统开发的对比:

    // 传统开发:手动修改 → 编译 → 运行 → 查看结果 → 再修改
    // 每轮迭代需要 1030 分钟

    // Vibe Coding:自然语言描述 → AI自动修改 → 自动编译 → 自动测试
    // 每轮迭代需要 30 秒 – 2 分钟

    2. 分层返回结果设计

    为"奇妙科学乐园"这类儿童应用设计返回结果时,需要考虑不同场景的数据结构:

    // ✅ 正确:分场景设计返回结果
    // 场景1:查询成功
    const successPayload: Record<string, Object> = {
    'type': 'result',
    'status': 'success',
    'data': {
    'topic': '太空',
    'question': '太阳系有几颗行星',
    'answer': '太阳系目前有8颗行星…',
    'funFact': '冥王星在2006年被重新分类为矮行星哦!',
    'difficulty': '简单',
    'relatedTopics': ['月球探索', '恒星知识', '宇宙奥秘'],
    'ageRange': '4-10岁'
    }
    };

    // 场景2:答案未找到(儿童友好提示)
    const notFoundPayload: Record<string, Object> = {
    'type': 'result',
    'status': 'failed',
    'errCode': 'ERR_NOT_FOUND',
    'data': { 'searchedQuestion': '宇宙的外面是什么' },
    'suggestion': '这个问题目前超出了我的知识范围,' +
    '不过好奇心是科学探索的第一步!' +
    '你可以问问关于太阳系、恐龙、海洋动物等问题哦!'
    };

    // ❌ 错误:所有场景返回相同的结构
    // 不区分成功/失败,不提供suggestion提示
    const badPayload: Record<string, Object> = {
    'result': ScienceDataService.search(topic, question)
    };


    步骤6: 多设备适配与分发策略

    功能说明

    HarmonyOS的最大优势之一是"一次开发,多端部署"。当Skill开发完成并通过审核后,需要考虑如何在不同设备形态(手机、平板、车机、手表等)上正确运行和分发。

    多设备适配原则
    设备类型屏幕特征适配要点Skill调用方式
    手机 4.7-6.9英寸 语音/文字触发,标准卡片展示 小艺语音、搜索栏
    平板 10-12英寸 平行视界分栏,大屏沉浸展示 小艺语音、搜索栏、桌面卡片
    车机 10-25英寸 语音优先,大字体高对比度 方向盘语音按键、中控语音
    手表 1.4-2.0英寸 简短回答,关键信息优先 语音抬腕触发
    完整代码:设备能力预查询

    // entry/src/main/ets/utils/DeviceCapabilityUtil.ets

    import { deviceInfo } from '@kit.BasicServicesKit';

    /**
    * 设备能力工具类
    * 用于判断当前设备类型,适配不同设备形态
    */

    export class DeviceCapabilityUtil {

    /**
    * 获取设备类型
    * @returns 设备类型字符串
    */

    static getDeviceType(): string {
    const deviceType: string = deviceInfo.deviceType;
    return deviceType;
    }

    /**
    * 是否为大屏设备(平板、折叠屏)
    * @returns 是否大屏
    */

    static isLargeScreen(): boolean {
    // 平板或折叠屏判定
    const deviceType: string = this.getDeviceType();
    return deviceType === 'tablet' || deviceType === '2in1';
    }

    /**
    * 是否为可穿戴设备(手表)
    * @returns 是否可穿戴
    */

    static isWearable(): boolean {
    return this.getDeviceType() === 'watch';
    }

    /**
    * 是否为车机
    * @returns 是否车机
    */

    static isCarDevice(): boolean {
    return this.getDeviceType() === 'car';
    }

    /**
    * 根据设备类型获取适合的回答长度
    * @returns 最大回答字符数
    */

    static getMaxAnswerLength(): number {
    if (this.isWearable()) {
    return 100; // 手表:超短回答
    } else if (this.isCarDevice()) {
    return 200; // 车机:简短回答(语音播报)
    } else if (this.isLargeScreen()) {
    return 1000; // 平板:完整回答
    } else {
    return 500; // 手机:中等长度回答
    }
    }
    }

    完整代码:适配不同设备的Skill返回结果

    // 在 ScienceQASkill.ets 的 queryScience 方法中添加设备适配逻辑

    import { DeviceCapabilityUtil } from '../../../src/main/ets/utils/DeviceCapabilityUtil';

    // 在查询成功后,根据设备类型裁剪答案
    const maxLen: number = DeviceCapabilityUtil.getMaxAnswerLength();
    let displayAnswer: string = answer.content;
    if (displayAnswer.length > maxLen) {
    displayAnswer = displayAnswer.substring(0, maxLen) + '…';
    }

    const data: Record<string, Object> = {
    'topic': answer.topic,
    'question': answer.question,
    'answer': displayAnswer,
    'funFact': DeviceCapabilityUtil.isWearable() ? '' : answer.funFact,
    'difficulty': answer.difficulty,
    'deviceType': deviceInfo.deviceType
    };

    智能体市场审核与分发

    Skill开发完成后,通过以下流程上架到智能体市场:

    开发完成 → 自测验证 → 提交审核 → 审核通过 → 市场上架 → 多端分发

    审核要点:
    1. Skill名称:不超过8个中文字符,直观表达功能
    2. 触发场景:正向示例和反向排除都要清晰
    3. 参数契约:args Schema完整,required标记必填项
    4. 返回值契约:覆盖所有场景(成功、参数缺失、未找到、内部错误)
    5. suggestion字段:必须包含,且使用用户友好的语言
    6. 隐私政策:必须提供可访问的隐私政策链接
    7. 内容合规:涉及AI生成内容需填写备案信息
    8. 未成年人保护:面向儿童的应用需特别关注

    代码解析

    1. 审核常见驳回原因与规避

    // ❌ 常见驳回原因1:Skill名称不规范
    // "免费科学问答助手" → 包含"免费"营销词,会被驳回
    // "AI科学王" → 名称过于抽象,无法直观表达功能
    // ✅ 正确命名:"奇妙科学问答" → 清晰表达功能

    // ❌ 常见驳回原因2:触发场景边界不清晰
    // 只写了"当用户问问题时调用" → 误触发率极高
    // ✅ 正确写法:
    // "当用户询问科学知识相关的问题时调用"
    // 并明确列出"不调用的情况"

    // ❌ 常见驳回原因3:缺少隐私政策
    // 没有配置隐私政策链接
    // ✅ 正确做法:在智能体市场配置中关联有效的隐私政策URL

    2. 版本管理策略

    版本号规范:主版本号.次版本号.修订号(如 1.0.0 → 1.1.0 → 1.1.1)

    变更类型 版本号变化 示例
    新增功能 次版本号+1 1.0.0 → 1.1.0
    Bug修复 修订号+1 1.1.0 → 1.1.1
    重大架构调整 主版本号+1 1.1.0 → 2.0.0


    ⚠️ 常见问题与解决方案

    问题1: Skill注册失败——三处名称不一致

    现象:Skill开发完成后,通过语音或文字触发时系统无响应,控制台也没有错误日志。

    原因:Skill目录名、SKILL.md中的name字段、module.json5中skillProfiles的name,三者不一致导致Skill注册失败。

    错误代码:

    // ❌ 错误:三处名称不一致

    // 目录名: skills/science-qa/
    // SKILL.md: name: science_qa ← 下划线
    // module.json5: skillProfiles[0].name: "ScienceQA" ← 大驼峰

    正确代码:

    // 正确:三处名称完全一致

    // 目录名: skills/science-qa/
    // SKILL.md:

    name: science-qa 与目录名一致

    // module.json5:
    "skillProfiles": [
    {
    "name": "science-qa" 与目录名、SKILL.md一致
    }
    ]

    规则/建议:

    • Skill名称使用小写字母和短横线分隔(如science-qa)
    • 开发时先确定名称,然后三处同步填写
    • 编译前检查三处是否一致

    问题2: 入口脚本方法名与SKILL.md不匹配

    现象:系统智能体能匹配到Skill,但执行时返回"function not found"错误。

    原因:ArkTS入口脚本中的public方法名与SKILL.md中的functionName不一致。

    错误代码:

    // ❌ 错误:方法名不一致

    // SKILL.md中声明:
    // functionName: 'queryScience'

    // 入口脚本中:
    export default class ScienceQASkill {
    public async getScienceInfo( // ← 方法名不匹配
    info: scriptManager.ArkTSScriptInfo,
    argv: string[]
    ): Promise<void> { }
    }

    正确代码:

    // ✅ 正确:方法名与SKILL.md的functionName严格一致

    // SKILL.md中声明:
    // functionName: 'queryScience'

    // 入口脚本中:
    export default class ScienceQASkill {
    public async queryScience( // ← 与SKILL.md一致
    info: scriptManager.ArkTSScriptInfo,
    argv: string[]
    ): Promise<void> { }
    }

    规则/建议:

    • 方法名严格匹配,区分大小写
    • 建议在SKILL.md中写完functionName后,直接复制到入口脚本中

    问题3: 未调用completeArkTSScriptInApp导致超时

    现象:Skill被触发并开始执行,但系统一直等待,最终超时返回"执行失败"。

    原因:某个代码分支(如异常处理、参数校验失败)忘记调用completeArkTSScriptInApp回传结果。

    错误代码:

    // ❌ 错误:参数校验分支没有回传结果

    public async queryScience(
    info: scriptManager.ArkTSScriptInfo,
    …argv: string[]
    ): Promise<void>
    {
    const question: string = argv.length > 1 ? argv[1].trim() : '';
    if (question.length === 0) {
    return; // ← 直接返回,没有调用completeArkTSScriptInApp
    // 系统会一直等待直到超时
    }
    // …正常逻辑
    }

    正确代码:

    // ✅ 正确:所有分支都必须回传结果

    public async queryScience(
    info: scriptManager.ArkTSScriptInfo,
    argv: string[]
    ): Promise<void> {
    const question: string = argv.length > 1 ? argv[1].trim() : '';
    if (question.length === 0) {
    const payload: Record<string, Object> = {
    'type': 'result',
    'status': 'failed',
    'errCode': 'ERR_INVALID_PARAMS',
    'suggestion': '请告诉我你想了解什么科学知识呢?'
    };
    await this.report(info, { code: –1, result: payload }); // ← 必须回传
    return;
    }
    // …正常逻辑
    }

    规则/建议:

    • 封装统一的report()方法,所有分支都通过它回传结果
    • 不管成功还是失败,都必须调用completeArkTSScriptInApp
    • 每次新增错误分支时,检查是否调用了回传方法

    问题4: argv参数越界导致运行时异常

    现象:某些用户提问能正常回答,但某些提问会导致Skill崩溃。

    原因:AI智能体传入的参数数量不固定,直接按索引访问argv数组导致越界。

    错误代码:

    // ❌ 错误:直接按索引访问,可能越界

    public async queryScience(
    info: scriptManager.ArkTSScriptInfo,
    …argv: string[]
    ): Promise<void>
    {
    // 假设argv[0]是topic,argv[1]是question
    const topic: string = argv[0].trim(); // ← argv[0]可能不存在
    const question: string = argv[1].trim(); // ← argv[1]可能不存在
    }

    正确代码:

    // ✅ 正确:安全地访问argv,做好长度判断和默认值处理

    public async queryScience(
    info: scriptManager.ArkTSScriptInfo,
    …argv: string[]
    ): Promise<void>
    {
    // 安全获取参数,带默认值
    const topic: string = argv.length > 0 ? argv[0].trim() : '';
    const question: string = argv.length > 1 ? argv[1].trim() :
    (argv.length > 0 ? argv[0].trim() : '');

    // 进一步校验
    if (question.length === 0) {
    // 返回参数缺失错误
    }
    }

    规则/建议:

    • argv是string数组,AI传参数量不固定
    • 访问前务必判断length
    • 对第一个参数同时做topic和question的兜底处理

    问题5: DevEco Code生成的代码不符合ArkTS严格模式

    现象:DevEco Code生成的Skill代码在编译时报ArkTS严格模式错误,如"Spread operator is not allowed"。

    原因:GLM-5.1模型虽然内置了ArkTS语法规范Skill,但在复杂场景下可能仍然生成不符合严格模式的代码。

    错误代码:

    // ❌ DevEco Code可能生成的代码(ArkTS严格模式不兼容)

    // 1. 使用了展开运算符
    const result = { …baseData, …extraData };

    // 2. 使用了 any 类型
    const data: any = JSON.parse(jsonStr);

    // 3. 使用了模板字符串
    const msg = `答案是:${answer}`;

    // 4. 使用了 as 类型断言
    const result = data as ScienceAnswer;

    正确代码:

    // ✅ ArkTS严格模式兼容的写法

    // 1. 展开运算符 → 使用Object.assign或显式赋值
    const result: Record<string, Object> = Object.assign({}, baseData, extraData);

    // 2. any → 使用明确类型或unknown + 类型守卫
    const data: ScienceAnswer | null = ScienceDataService.parseJSON(jsonStr);

    // 3. 模板字符串 → 使用字符串拼接
    const msg: string = '答案是:' + answer;

    // 4. as类型断言 → 使用类型守卫函数
    function isScienceAnswer(obj: unknown): obj is ScienceAnswer {
    return obj !== null &&
    typeof obj === 'object' &&
    'topic' in obj &&
    'content' in obj;
    }
    if (isScienceAnswer(data)) {
    // 安全使用
    }

    规则/建议:

    • 收到编译错误后,在DevEco Code中直接粘贴错误信息,AI会自动修复
    • arkts-grammar-standards Skill会在生成时自动检查大部分语法问题
    • 对于复杂类型转换,建议使用类型守卫函数替代as

    📝 本章小结

    核心知识点

    本文详细讲解了Vibe Coding开发流程——从需求描述到应用上线,主要包括:

    1. Vibe Coding理念

    • 核心是"意图即服务",用自然语言表达需求,AI自动生成代码
    • 不是替代开发者,而是将精力从重复编码中释放到业务创新上
    • DevEco Code是HarmonyOS生态Vibe Coding的核心工具

    2. DevEco Code AI Agent工作流

    • 三种Agent模式:Build(日常开发)、Plan(需求拆解)、Goal(端到端交付)
    • 五大内置Skill:项目创建、语法规范、运行时修复、ArkUI知识、错误修复
    • 完整链路:需求分析 → 代码生成 → 编译构建 → 本地调测 → 优化

    3. Skill开发全流程

    • SKILL.md:Skill的灵魂文件,定义触发场景和参数/返回值契约
    • ArkTS入口脚本:薄适配层设计,只做"接参数→调业务→报结果"
    • module.json5:skillProfiles配置,三处名称必须一致
    • 关键接口:ArkTSScriptInfo、ExecuteResult、completeArkTSScriptInApp

    4. 小艺智能入口接入

    • 系统智能体完成意图匹配 → 参数构造 → 脚本调用 → 结果回传 → 自然语言回复
    • suggestion字段决定用户体验,必须使用友好语言

    5. 多设备适配与分发

    • 设备能力预查询,根据设备类型调整回答长度和内容
    • 智能体市场审核要点:名称规范、触发边界、隐私政策、内容合规

    最佳实践总结

    ✅ Skill名称三处一致

    // 目录名: skills/science-qa/
    // SKILL.md: name: science-qa
    // module.json5: skillProfiles[0].name: "science-qa"

    ✅ 薄适配层设计——复用已有业务代码

    // Skill入口脚本只做桥接,不写业务逻辑
    const answer: ScienceAnswer | null =
    ScienceDataService.search(topic, question);

    ✅ 统一的report方法——所有分支都回传结果

    // 不管成功还是失败,都通过report方法统一回传
    await this.report(info, { code: 0, result: successPayload });
    await this.report(info, { code: -1, result: errorPayload });

    ✅ 儿童友好的suggestion提示

    // 面向儿童应用,suggestion使用鼓励性、引导性的语言
    'suggestion': '这个问题目前超出了我的知识范围,' +
    '不过好奇心是科学探索的第一步!'

    ✅ DevEco Code Vibe Coding交互技巧

    # 先用Plan模式拆解复杂需求
    deveco –agent plan

    # 再用Build模式执行代码生成
    deveco –agent build

    # 编译错误直接粘贴给AI自动修复


    下一步预告

    在下一篇文章《A2A跨应用智能体互通——实现多软件联动复杂任务》中,我们将:

    • 🎨 深入了解A2A(Agent to Agent)跨应用通信机制
    • 📚 实现端侧A2A和云侧A2A的双向通道
    • 🏷️ 通过多Agent协同编排,完成"一句话查天气→推荐户外活动→自动添加日历"的跨应用任务链

    🔗 相关链接

    • 项目源码: Atomgit仓库
    • DevEco Code 开源地址: gitcode.com/openharmony-sig/deveco-code
    • HarmonyOS 官方文档: developer.harmonyos.com
    • 小艺开放平台: 小艺智能体开发指南

    💡 提示: 建议结合项目源码和DevEco Code工具同步阅读本文,动手实践效果更好!可以尝试用DevEco Code为"奇妙科学乐园"创建一个简单的Skill,体验Vibe Coding的完整开发流程。

    赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS应用<奇妙科学乐园>开发第2篇:Vibe Coding开发流程——从需求描述到应用上线
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址