HarmonyOS 6 SDK对接实战:从原生ASR到Copilot SDK(下)- Copilot SDK对接与重构(全网最新)
- [我用Copilot SDK"干掉"了自己写的语音识别模块](#我用Copilot SDK“干掉”了自己写的语音识别模块)
-
- 一、千行代码干了啥?来review一下
- [二、先认识一下:Copilot SDK是什么?](#二、先认识一下:Copilot SDK是什么?)
- 三、改造目标:增加可维护性,30分钟完成替换。
- 四、实战结果复盘
- 总结:造轮子和用轮子,不冲突
我用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提升效率。