HarmonyOS 6 SDK对接实战:从原生ASR到Copilot SDK(下)- Copilot SDK对接与重构(全网最新)

HarmonyOS 6 SDK对接实战:从原生ASR到Copilot SDK(下)- Copilot SDK对接与重构(全网最新)

我用Copilot SDK"干掉"了自己写的语音识别模块

这是系列的第二篇。上一篇,我头铁地花了一周手撸了近千行代码,实现了语音识别。今天这篇,我直接用Copilot SDK把这套逻辑全替换了,代码量砍到十分之一行。顺便聊聊,什么时候该造轮子,什么时候该用轮子。

说实话,看到@hw-agconnect/copilot文档的那一瞬间,我心态确实有点崩。文档里写得明明白白,语音输入是内置功能,几行配置就搞定。我那一周的手写代码,好像瞬间成了"无效加班"。

但冷静下来复盘了一下,这一周其实血赚。因为亲手写过,我彻底搞清楚了音频采集的参数怎么配、VAD的前后端点是什么意思、识别引擎的状态机怎么流转。有了这些底子,后来迁移SDK的时候,我看配置项就像看老朋友,遇到问题也能直接定位到根因,而不是对着文档瞎蒙。

所以,如果你也想给你的鸿蒙App加上语音输入,又不想走弯路,这篇直接给你一个30分钟上手的方案。如果你有好奇心,也建议先看看上一篇,了解一下底层原理。


一、千行代码干了啥?来review一下

上一篇的代码结构大概是这样的:

模块 文件 职责
应用入口 EntryAbility.ts 动态申请麦克风权限
主界面 Index.ets UI交互、引擎管理、流程控制
实时录音 AudioCapturer.ts 音频采集、参数配置
文件识别 FileCapturer.ts PCM文件读取(调试用)
采集接口 ICapturerInterface.ts 统一采集接口
常量定义 AsrConstants.ts VAD参数、事件码
工具类 Util.ts 延时工具
总计 7个文件 能跑,但维护起来很痛苦

流程也简单直接:
手写实现
EntryAbility

权限申请
Index.ets

UI+引擎管理
AudioCapturer

音频采集
speechRecognizer

识别引擎
用户说话
识别文字

功能是跑通了,但每次想加点新功能,都得动好几个文件。扩展性?不存在的。


二、先认识一下:Copilot SDK是什么?

在动手改造之前,有必要介绍一下我们今天要用的主角------@hw-agconnect/copilot

其实我在之前的内容已经讲到了,鸿蒙的技术博客,大部分的时效性太低了,我根本从百度上搜索不到任何这个库的讲解内容。

只有官网社区问答里,能看到只言片语的了解,你只能问AI助手,得到一个比较准确的回答。(所以说是我不用轮子吗,是我根本找不到,更别说屏幕前的友友们了)

话不多说,如果你只是想快速集成AI对话和语音输入,可以直接跳过这一节看后面的实战。但如果你想了解SDK的完整能力,这一节值得花3分钟看看。

这不是一个普通的SDK

有朋友问了,这不是你木木自己瞎编的吧,我怎么也搜不到呢?那哪能啊。

这是华为提供的AI智能聊天组件SDK,专门用于在鸿蒙应用中快速集成大模型对话能力。它的定位不是"封装了几个API",而是一套开箱即用的聊天解决方案。

因为实在没有什么文档说明,我整理了几个核心特性,大家可以感受一下:

  • 多模型支持:支持DeepSeek、Qwen、OpenAI等主流LLM,我们可以灵活选择模型,不被绑定。
  • 流式响应:支持流式对话,实时显示回复内容,体验更好,用户不用干等。
  • 内置UI组件:提供完整的聊天界面组件,不用自己写聊天UI,直接复用。
  • 语音识别:集成语音输入功能,这正是我们今天要用的核心能力。
  • Agent模式:支持注册自定义动作,让AI调用你的函数,可以做更复杂的业务逻辑。
  • 消息管理:内置消息存储和历史对话,不用自己写数据库操作。

说白了,这个SDK帮我们封装了三个层面的东西:底层是HTTP请求、流式解析、错误重试;中层是消息管理、状态机、存储;上层是聊天UI、语音输入、权限处理。我们之前的代码,覆盖的只是中下层的一部分,而SDK把这些全包了。

对我们这个项目来说,用SDK的价值主要体现在这几个方面:开发效率直接拉满,稳定性有保障,华为官方维护,不用自己处理各种边界情况;维护成本几乎降为零,底层API变化由SDK跟进,我们不用操心;功能扩展空间大,Agent模式、多模态支持这些能力,后续想加就加;用户体验也更完善,内置的录音动画、权限处理、错误提示,比手写要细腻得多。

当然,SDK不是万能的。如果你需要深度定制UI,或者想在无网络环境下用离线识别,那手写原生还是必要的。但对我们这个场景------一个需要快速上线、标准语音输入够用的AI助手------SDK是最优解。

SDK的架构长什么样?

我多年的前端开发经验,先解包,从导出的模块来看,SDK的设计很清晰:

typescript 复制代码
import { 
  CopilotChat,           // 聊天UI组件(我们直接用它)
  CopilotController,     // 控制器(管理对话逻辑)
  StoreManager,          // 存储管理(历史对话)
  FrontendAction,        // 动作定义(Agent模式用)
  TextMessage,           // 消息模型
  MessageRole            // 消息角色
} from '@hw-agconnect/copilot';

核心就三个东西:

  • CopilotChat:UI组件,你把它往页面上一放,聊天界面就有了。
  • CopilotController:控制器,负责和大模型通信、管理消息列表。
  • StoreManager:存储管理器,自动保存对话历史。

我们这次的改造,只需要用到前两个。但了解完整的架构,能帮我们在未来扩展功能时少走弯路。

控制器是大脑

CopilotController是整个SDK的大脑。它的职责包括管理LLM配置、维护对话上下文、处理流式请求和响应、管理动作注册。我们只需要在创建时传入配置,后面所有的交互都通过它来完成:

typescript 复制代码
const controller = new CopilotController({
  llm: {
    baseUrl: 'https://api.deepseek.com/v1',
    apiKey: 'your-api-key',
    model: 'deepseek-chat',
    forwardedParameters: {
      temperature: 0.7,      // 控制回答的随机性
      maxTokens: 2000        // 最大回复长度
    }
  },
  storeId: 'chat',           // 存储标识,用于保存历史
  systemPrompt: '你是一个AI助手'  // 系统提示词
});

Agent模式有点意思

虽然我们这次用不到,但SDK的Agent模式值得一提。你可以注册自定义的"动作",比如查天气、订机票。当用户问"北京天气怎么样"时,AI会自动判断该调用哪个动作,提取参数,执行你的代码,然后把结果返回给用户。

typescript 复制代码
const weatherAction: FrontendAction = {
  name: 'get_weather',
  description: '获取指定城市的天气',
  parameters: [{
    name: 'city',
    type: 'string',
    description: '城市名称',
    required: true
  }],
  handler: async (args) => {
    // 这里可以调天气API
    return `城市: ${args.city}, 天气: 晴`;
  }
};

controller.registerAction(weatherAction);

这个能力很实用,相当于让AI变成了你的业务的智能入口。

版本要求

用这个SDK之前,先确认环境:DevEco Studio需要5.0.0 Release及以上,HarmonyOS SDK需要5.0.0 Release及以上,系统需要5.0.0(12)及以上。语音输入功能需要HarmonyOS 5.1.0+。我们项目用的是HarmonyOS 6,完全满足。

好了,介绍完SDK,我们回到正题:怎么用它替换掉我们之前手写的代码。


三、改造目标:增加可维护性,30分钟完成替换。

这次改造,目标很明确,减少代码量,同时增加可维护性:

改造后的架构,清爽多了:
SDK集成
ChatPage.ets

50行配置代码
CopilotChat组件

内置语音输入
CopilotController

AI对话管理
用户说话
自动识别文字
AI回复

原来手写的那些音频采集、引擎管理、权限处理,全都被SDK封装好了,我们只需要关心业务配置。


第一步:清理旧代码

先把上一篇文章里写的ASR模块全删了:

bash 复制代码
# 删除这7个文件
rm entry/src/main/ets/pages/AudioCapturer.ts
rm entry/src/main/ets/pages/FileCapturer.ts
rm entry/src/main/ets/pages/ICapturerInterface.ts
rm entry/src/main/ets/pages/AsrConstants.ts
rm entry/src/main/ets/pages/Util.ts
rm entry/src/main/ets/pages/Index.ets  # 后面换成ChatPage.ets
rm entry/src/main/ets/entryability/EntryAbility.ts  # 权限申请大幅简化

第二步:添加SDK依赖

oh-package.json5里加上Copilot SDK:

json5 复制代码
{
  "dependencies": {
    "@hw-agconnect/copilot": "^0.1.0"
  }
}

然后执行ohpm install

第三步:简化权限配置

上一篇里,我们在EntryAbility.ts里写了40行代码来动态申请权限。现在直接在module.json5里声明就行,SDK会在用户点击麦克风时自动申请。

json5 复制代码
// entry/src/main/module.json5
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "$string:microphone_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

权限请求的交互逻辑,SDK全包了。

第四步:核心代码,50行搞定所有

这是最爽的一步,直接用CopilotChat组件替换掉原来200行的Index.ets

typescript 复制代码
// entry/src/main/ets/pages/ChatPage.ets
import { CopilotChat, CopilotController } from '@hw-agconnect/copilot';

@Entry
@ComponentV2
export struct ChatPage {
  controller: CopilotController = new CopilotController({
    llm: {
      baseUrl: 'https://api.deepseek.com/v1',
      apiKey: 'your-deepseek-api-key',
      model: 'deepseek-chat'
    },
    systemPrompt: `你是「旅行回忆盲盒」的AI助手,帮助用户规划旅行路线、推荐景点、
                   回忆旅行故事。回答要亲切、专业。`
  });

  build() {
    Column() {
      Row() {
        Text('旅行回忆盲盒 · AI助手')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
      .height(56)
      .padding(16)
      .backgroundColor('#FFF')

      CopilotChat({
        controller: this.controller,
        options: {
          labels: {
            introduce: '你好!我是你的旅行AI助手 🌍\n\n我可以帮你:\n• 规划旅行路线\n• 推荐当地美食\n• 回忆旅行故事',
            placeholder: '点击🎤语音输入,或直接输入文字',
            suggestions: [
              '东京三日游怎么安排?',
              '巴黎有哪些必去的景点?',
              '京都最值得吃的抹茶店'
            ]
          },
          icons: {
            assistant: $r('app.media.ic_assistant'),
            user: $r('app.media.ic_user')
          },
          isShowDefaultActionCard: true
        }
      })
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8F9FA')
  }
}

语音输入按钮、录音状态、识别结果、权限处理......所有这些,SDK都已经内置好了。

第五步:简化应用入口

原来的EntryAbility.ts也简化了,不需要再写权限申请逻辑:

typescript 复制代码
// entry/src/main/ets/entryability/EntryAbility.ts
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/ChatPage', (err) => {
      if (err.code) {
        console.error(`加载页面失败: ${err.message}`);
      }
    });
  }
}

四、实战结果复盘

代码量对比

维度 改造前 改造后 变化
核心文件数 7个 1个 -86%
总代码行数 880行 100行 -89%
开发时间 5天 30分钟 -99%
维护成本 高(7个文件) 低(1个文件) ⬇️⬇️⬇️

功能对比

功能 改造前 改造后
实时录音识别 ✅ 手写80行 ✅ 内置
识别结果实时显示 ✅ 手写 ✅ 内置
录音状态动画 ❌ 需要手写 ✅ 内置
权限自动申请 ❌ 手写40行 ✅ 自动处理
权限拒绝处理 ❌ 手写 ✅ 内置提示
网络错误处理 ❌ 手写 ✅ 内置提示
识别超时处理 ❌ 手写 ✅ 内置
VAD参数可配置 ✅ 需要改代码 ✅ 配置项
多语言支持 ❌ 需要扩展 ✅ 配置项
与聊天UI集成 ❌ 手写200行 ✅ 内置

代码对比

改造前,手写识别逻辑需要管理引擎生命周期:

typescript 复制代码
private async createEngine() { ... }
private setListener() { ... }
private async startRecording() { 
  // 配置音频参数
  // 启动引擎
  // 启动采集器
  // 循环写入音频数据
}
private async stopRecording() { ... }
private shutdownEngine() { ... }

改造后,只需要配置:

typescript 复制代码
CopilotChat({
  controller: this.controller,
  options: {
    labels: { placeholder: '点击🎤语音输入' }
  }
})

用户点击麦克风
自动申请权限
开始录音
实时显示识别文字
用户确认发送
AI思考回复
TTS语音播报

集成过程中,发现SDK自带了一些我之前没想过要做的细节:

录音状态动画:麦克风图标变化、波纹效果、识别结果显示,这些细节在改造前要花至少50行代码。

智能权限处理:首次点击时自动申请,拒绝后引导去设置,这个交互流程非常流畅,而且我们不用写一行代码。

识别结果追加模式 :通过resultAppendMode: true可以开启追加模式,适合长语音口述的场景,而不用像手写那样自己处理拼接逻辑。

VAD参数可配置:之前需要改代码才能调整的参数,现在直接配置就行:

typescript 复制代码
options: {
  speechRecognizerConfig: {
    language: 'zh-CN',
    mode: 'short',
    online: true,
    vadBegin: 2000,    // 等待2秒开始说话
    vadEnd: 3000,      // 静音3秒后结束
    maxAudioDuration: 20000
  }
}

多语言支持 :改一下language配置就能切换语言,不用再关心底层的模型文件。


如果你也打算这么干,这几个点可以注意一下:

权限声明不能省 。虽然SDK会自动申请,但module.json5里的声明还是要写,否则系统直接拒绝。

VAD参数按场景调 。短语音问答可以用vadBegin: 2000, vadEnd: 3000,给用户2秒准备时间;长语音口述可以把vadEnd调到5000,给更长的思考时间。

API Key别硬编码。建议放到配置文件或者用服务端代理,安全第一。

网络依赖处理。在线识别依赖网络,可以考虑在UI上提示用户网络状态。


总结:造轮子和用轮子,不冲突

回顾一下这两篇文章:

上篇花一周,搞懂了语音识别的工作原理。下篇花30分钟,集成SDK,用50行代码交付了产品功能。

我的感受是:造轮子不是目的,理解轮子才是。没有那一周的手写经历,我可能到现在都不知道VAD参数是干啥的,遇到SDK的配置项也只能瞎猜。但正因为亲手实现过,我才能精准地调优参数,遇到问题也能快速定位。

用轮子不是偷懒,是效率。在产品开发中,我们的目标是快速交付价值。如果每个功能都要从底层造轮子,项目永远无法上线。

所以,如果你正在开发鸿蒙应用,需要给AI助手加上语音输入功能:

  • 目标是快速交付产品,直接用Copilot SDK。
  • 目标是学习技术,可以从原生API开始手写一遍。
  • 想两者兼顾,像我一样,先手写理解原理,再集成SDK提升效率。
相关推荐
枫叶丹42 小时前
【HarmonyOS 6.0】应用预加载机制,让应用启动快人一步
开发语言·华为·harmonyos
Rick19932 小时前
Agent 岗位高频面试题
ai·agent
飞Link2 小时前
动态嵌入:Transformer 架构下的语义重构与演进
人工智能·深度学习·重构·transformer
国医中兴2 小时前
ClickHouse监控与运维策略:从告警到故障处理
flutter·harmonyos·鸿蒙·openharmony
_waylau2 小时前
鸿蒙架构师修炼之道-实践应用
华为·harmonyos·鸿蒙·鸿蒙系统
希望上岸的大菠萝2 小时前
HarmonyOS 6.0 V2 状态管理实战(上)- 基于「今天空白」当前实现拆解 @ObservedV2、@Trace、@ComponentV2
华为·harmonyos·鸿蒙
希望上岸的大菠萝2 小时前
HarmonyOS 6.0 ArkUI 声明式 UI 实战 - 基于「今天空白」当前页面实现拆布局、条件渲染、弹层封装
华为·harmonyos·鸿蒙·仓颉
2501_933329552 小时前
深度解析:Infoseek数字公关AI中台的技术架构与实践
人工智能·自然语言处理·重构·架构
实在智能RPA2 小时前
实在 Agent 如何处理企业非标准化流程?:深度拆解执行级 AI 的落地路径
人工智能·ai