【共创季稿事节】HarmonyOS 6.1 智慧语音服务接入实战:应用内的语音交互设计
文章目录

每日一句正能量
不要看见别人发光就觉得自己黯淡。
世界是多元的舞台,别人发光不代表你的光就不存在。每个人的时区、赛道、光源都不同。你只需发出自己的光,无论强弱。
摘要
摘要: 语音交互正在重塑人机交互范式。HarmonyOS 6.1 提供了完整的智慧语音服务框架,支持应用内语音唤醒、连续对话、语义理解和语音合成。本文从架构原理到代码实现,完整演示如何在应用中集成语音唤醒、指令识别、意图分发和 TTS 反馈的闭环系统,帮助开发者构建"听得懂、说得出"的智能应用体验。
一、智慧语音服务架构概述
1.1 为什么应用需要语音交互?
在以下场景中,语音交互相比触屏操作具有不可替代的优势:
| 场景 | 痛点 | 语音方案 |
|---|---|---|
| 驾驶导航 | 双手握方向盘,无法触屏 | “导航到最近的加油站” |
| 厨房烹饪 | 双手沾水/油污 | “定时 30 分钟” |
| 运动健身 | 手机装在臂包中 | “下一首歌” |
| 长辈用户 | 界面复杂、字体小 | “给张三打电话” |
| 视觉障碍 | 无法看见屏幕内容 | 全程语音操作 |
HarmonyOS 6.1 的智慧语音服务(Intelligent Voice Service)将语音能力下沉为系统级服务,开发者无需自建 ASR/NLP/TTS 模型,即可快速接入专业级的语音交互能力。
1.2 框架四层架构

上图展示了 HarmonyOS 6.1 智慧语音服务的完整交互流程。从用户说话到应用响应,经过唤醒、识别、理解、执行、反馈五个阶段,全程在 500ms-2s 内完成。
智慧语音服务框架可分为四层:
| 层级 | 模块 | 职责 |
|---|---|---|
| 信号层 | Audio Capturer、VAD | 音频采集、语音端点检测、降噪 |
| 识别层 | ASR Engine | 语音转文字、方言适配、个性化热词 |
| 理解层 | NLP Engine、Intent Parser | 语义理解、实体抽取、意图分类 |
| 交互层 | TTS Engine、Voice Synthesizer | 语音合成、情感音色、语速控制 |
二、语音唤醒:打开交互之门
2.1 唤醒方式对比
HarmonyOS 6.1 支持两种唤醒模式:
| 唤醒方式 | 触发条件 | 功耗 | 适用场景 |
|---|---|---|---|
| 关键词唤醒 | 说出预设唤醒词(如"小艺小艺") | 低(芯片级 DSP) | 常驻后台、随时待命 |
| 按钮唤醒 | 点击界面麦克风按钮 | 无额外功耗 | 应用内主动发起 |
2.2 应用内按钮唤醒实战
在应用内集成语音唤醒按钮是最轻量的接入方式:
// components/VoiceButton.ets
import { speechRecognizer } from '@kit.SpeechKit';
@Component
export struct VoiceButton {
@State isListening: boolean = false;
@State rippleScale: number = 1.0;
private recognizer: speechRecognizer.SpeechRecognizer | null = null;
aboutToAppear(): void {
// 初始化语音识别器
this.recognizer = speechRecognizer.createEngine({
language: 'zh-CN',
online: true, // 使用在线识别,精度更高
partialResults: true // 返回中间结果,提升响应速度
});
}
private async startListening(): Promise<void> {
if (!this.recognizer) return;
this.isListening = true;
this.startRippleAnimation();
try {
const result = await this.recognizer.startListening({
// 最长录音 60 秒
maxDuration: 60000,
// 静音超时 3 秒自动停止
silenceTimeout: 3000
});
// 识别结果通过回调返回
this.recognizer.on('result', (event) => {
if (event.isLast) {
this.handleVoiceResult(event.result);
} else {
// 中间结果可用于实时展示
this.showInterimResult(event.result);
}
});
this.recognizer.on('error', (err) => {
console.error('[Voice] 识别错误:', err.message);
this.isListening = false;
});
} catch (err) {
console.error('[Voice] 启动识别失败:', err);
this.isListening = false;
}
}
private stopListening(): void {
this.recognizer?.stopListening();
this.isListening = false;
}
private handleVoiceResult(text: string): void {
console.info(`[Voice] 识别结果: ${text}`);
this.isListening = false;
// 将识别文本交给指令分发器
CommandDispatcher.dispatch(text);
}
private showInterimResult(text: string): void {
// 更新 UI 展示中间识别结果
AppStorage.setOrCreate('voiceInterimText', text);
}
private startRippleAnimation(): void {
// 录音时的波纹动画
const timer = setInterval(() => {
if (!this.isListening) {
clearInterval(timer);
return;
}
animateTo({ duration: 800, curve: Curve.EaseInOut }, () => {
this.rippleScale = this.rippleScale === 1.0 ? 1.3 : 1.0;
});
}, 800);
}
build() {
Stack() {
// 外圈波纹
if (this.isListening) {
Circle()
.width(80)
.height(80)
.fill('rgba(52,152,219,0.2)')
.scale({ x: this.rippleScale, y: this.rippleScale })
.animation({ duration: 800, curve: Curve.EaseInOut });
}
// 麦克风按钮
Button() {
Image(this.isListening ? $r('app.media.ic_stop') : $r('app.media.ic_mic'))
.width(28)
.height(28)
.fillColor(this.isListening ? '#e74c3c' : '#3498db');
}
.width(56)
.height(56)
.backgroundColor(this.isListening ? '#fdf2f2' : '#f0f3ff')
.borderRadius(28)
.onClick(() => {
if (this.isListening) {
this.stopListening();
} else {
this.startListening();
}
});
}
.width(100)
.height(100);
}
}
2.3 系统级关键词唤醒
如果应用需要在后台响应语音指令,可申请接入系统级关键词唤醒:
// 在 EntryAbility 中注册唤醒词
import { wakeWord } from '@kit.SpeechKit';
onCreate(): void {
// 注册应用自定义唤醒词
wakeWord.register({
wakeWords: ['打开外卖', '查订单'],
callback: (word: string) => {
console.info(`[WakeWord] 唤醒词命中: ${word}`);
// 唤起应用到前台
this.context.startAbility({
bundleName: 'com.example.food',
abilityName: 'EntryAbility'
});
// 根据唤醒词执行对应操作
if (word === '查订单') {
CommandDispatcher.dispatch('查看最近订单');
}
}
});
}
三、语义理解与指令分发
3.1 为什么需要指令分发?
语音识别的结果是纯文本(如"帮我查一下昨天的外卖订单"),应用需要理解这句话的意图并执行对应操作。这个过程就是语义理解 + 指令分发。
3.2 指令分发器实现
// services/CommandDispatcher.ets
import { nlp } from '@kit.IntelligenceKit';
import { router } from '@kit.ArkUI';
export class CommandDispatcher {
// 指令路由表
private static readonly ROUTE_TABLE: RouteItem[] = [
{
pattern: /查(看|一下)?.*订单/,
intent: 'query_order',
handler: (entities) => CommandHandlers.handleQueryOrder(entities)
},
{
pattern: /(打开|进入).*首页/,
intent: 'navigate_home',
handler: () => CommandHandlers.handleNavigateHome()
},
{
pattern: /(搜索|找|查).*商家/,
intent: 'search_shop',
handler: (entities) => CommandHandlers.handleSearchShop(entities)
},
{
pattern: /(联系|呼叫).*骑手/,
intent: 'contact_rider',
handler: (entities) => CommandHandlers.handleContactRider(entities)
},
{
pattern: /(播放|放).*音乐/,
intent: 'play_music',
handler: (entities) => CommandHandlers.handlePlayMusic(entities)
},
{
pattern: /(定时|倒计时).*分钟/,
intent: 'set_timer',
handler: (entities) => CommandHandlers.handleSetTimer(entities)
}
];
static async dispatch(text: string): Promise<void> {
console.info(`[Dispatcher] 接收指令: ${text}`);
// 方式一:使用系统 NLP 服务进行意图识别(推荐)
try {
const nlpResult = await nlp.understandIntent({
text: text,
domain: 'food_delivery' // 指定领域,提升识别精度
});
if (nlpResult.intent && nlpResult.confidence > 0.7) {
await this.executeByNlpResult(nlpResult);
return;
}
} catch (err) {
console.warn('[Dispatcher] NLP 服务调用失败,降级到本地规则:', err);
}
// 方式二:本地正则规则匹配(兜底方案)
await this.executeByLocalRule(text);
}
private static async executeByNlpResult(result: nlp.IntentResult): Promise<void> {
const { intent, entities } = result;
switch (intent) {
case 'query_order':
await CommandHandlers.handleQueryOrder(entities);
break;
case 'search_shop':
await CommandHandlers.handleSearchShop(entities);
break;
case 'contact_rider':
await CommandHandlers.handleContactRider(entities);
break;
default:
await this.fallbackResponse(`收到指令: ${intent},正在为您处理`);
}
}
private static async executeByLocalRule(text: string): Promise<void> {
for (const route of this.ROUTE_TABLE) {
const match = text.match(route.pattern);
if (match) {
console.info(`[Dispatcher] 本地规则命中: ${route.intent}`);
await route.handler(match);
return;
}
}
// 未匹配到任何指令
await this.fallbackResponse('抱歉,我没听懂,您可以试试说"查订单"或"联系骑手"');
}
private static async fallbackResponse(text: string): Promise<void> {
// 播放 TTS 反馈
await TTSHelper.speak(text);
// 同时展示文字提示
promptAction.showToast({ message: text, duration: 3000 });
}
}
interface RouteItem {
pattern: RegExp;
intent: string;
handler: (match: RegExpMatchArray) => Promise<void>;
}
3.3 指令处理器实现
// services/CommandHandlers.ets
import { router } from '@kit.ArkUI';
import { call } from '@kit.TelephonyKit';
export class CommandHandlers {
// 查询订单
static async handleQueryOrder(entities?: Record<string, string>): Promise<void> {
const orderId = entities?.['orderId'];
const timeRange = entities?.['timeRange']; // 如 "昨天"
if (orderId) {
router.pushUrl({ url: 'pages/OrderDetail', params: { orderId } });
await TTSHelper.speak(`正在为您打开订单 ${orderId}`);
} else if (timeRange === '昨天') {
router.pushUrl({ url: 'pages/OrderList', params: { filter: 'yesterday' } });
await TTSHelper.speak('正在为您查看昨天的订单');
} else {
router.pushUrl({ url: 'pages/OrderList', params: { filter: 'latest' } });
await TTSHelper.speak('正在为您查看最近的订单');
}
}
// 导航到首页
static async handleNavigateHome(): Promise<void> {
router.clear();
router.pushUrl({ url: 'pages/Index' });
await TTSHelper.speak('已回到首页');
}
// 搜索商家
static async handleSearchShop(entities?: Record<string, string>): Promise<void> {
const keyword = entities?.['shopName'] ?? entities?.['keyword'];
router.pushUrl({ url: 'pages/SearchResult', params: { keyword } });
await TTSHelper.speak(`正在为您搜索 ${keyword}`);
}
// 联系骑手
static async handleContactRider(entities?: Record<string, string>): Promise<void> {
const orderId = entities?.['orderId'];
const riderPhone = await OrderService.getRiderPhone(orderId);
if (riderPhone) {
call.makeCall(riderPhone);
await TTSHelper.speak('正在为您呼叫骑手');
} else {
await TTSHelper.speak('抱歉,未找到骑手联系方式');
}
}
// 播放音乐
static async handlePlayMusic(entities?: Record<string, string>): Promise<void> {
const songName = entities?.['songName'];
// 调用音乐播放服务
await MusicService.play(songName);
await TTSHelper.speak(songName ? `正在播放 ${songName}` : '正在为您播放音乐');
}
// 设置定时器
static async handleSetTimer(entities?: Record<string, string>): Promise<void> {
const minutes = parseInt(entities?.['duration'] ?? '0');
if (minutes > 0) {
TimerService.start(minutes * 60 * 1000);
await TTSHelper.speak(`已设置 ${minutes} 分钟定时器`);
}
}
}
四、TTS 语音反馈:让应用会说话
4.1 TTS 基础接入
// services/TTSHelper.ets
import { textToSpeech } from '@kit.SpeechKit';
export class TTSHelper {
private static engine: textToSpeech.TextToSpeechEngine | null = null;
private static isSpeaking: boolean = false;
// 初始化 TTS 引擎
static async init(): Promise<void> {
this.engine = await textToSpeech.createEngine({
language: 'zh-CN',
person: textToSpeech.VoicePerson.MALE, // 男声
speed: 1.0, // 语速:正常
volume: 1.0, // 音量:最大
pitch: 1.0 // 音调:正常
});
this.engine.on('finish', () => {
this.isSpeaking = false;
});
}
// 播放语音
static async speak(text: string): Promise<void> {
if (!this.engine) {
await this.init();
}
if (this.isSpeaking) {
// 如果正在播放,先停止
await this.engine?.stop();
}
this.isSpeaking = true;
try {
await this.engine?.speak(text);
} catch (err) {
console.error('[TTS] 播放失败:', err);
this.isSpeaking = false;
}
}
// 停止播放
static async stop(): Promise<void> {
await this.engine?.stop();
this.isSpeaking = false;
}
// 设置语速(0.5 - 2.0)
static setSpeed(speed: number): void {
this.engine?.setSpeed(Math.max(0.5, Math.min(2.0, speed)));
}
}
4.2 场景化语音反馈设计
不同场景下,TTS 反馈的内容和语气应有所区别:
// services/VoiceFeedback.ets
export class VoiceFeedback {
// 成功反馈
static async success(action: string): Promise<void> {
const templates = [
`${action}成功`,
`已为您${action}`,
`${action}完成,还有什么可以帮您?`
];
const text = templates[Math.floor(Math.random() * templates.length)];
await TTSHelper.speak(text);
}
// 失败反馈
static async error(reason: string): Promise<void> {
const templates = [
`抱歉,${reason},请稍后再试`,
`操作失败了,${reason}`,
`遇到问题:${reason},您可以尝试其他方式`
];
const text = templates[Math.floor(Math.random() * templates.length)];
await TTSHelper.speak(text);
}
// 确认反馈
static async confirm(question: string): Promise<void> {
await TTSHelper.speak(`${question},请说"确认"或"取消"`);
}
// 引导反馈
static async guide(tip: string): Promise<void> {
await TTSHelper.speak(`您可以试着说:${tip}`);
}
// 进度反馈
static async progress(percent: number): Promise<void> {
if (percent % 25 === 0) {
await TTSHelper.speak(`已完成 ${percent}%`);
}
}
}
五、完整实战:语音点餐助手

以下是一个完整的语音点餐助手实现,整合了前文所有模块:
// pages/VoiceOrder.ets
import { speechRecognizer } from '@kit.SpeechKit';
import { CommandDispatcher } from '../services/CommandDispatcher';
import { TTSHelper } from '../services/TTSHelper';
import { VoiceFeedback } from '../services/VoiceFeedback';
@Entry
@Component
struct VoiceOrder {
@State voiceText: string = '点击麦克风开始说话...';
@State isListening: boolean = false;
@State commandHistory: string[] = [];
private recognizer: speechRecognizer.SpeechRecognizer | null = null;
aboutToAppear(): void {
TTSHelper.init();
this.initRecognizer();
}
private initRecognizer(): void {
this.recognizer = speechRecognizer.createEngine({
language: 'zh-CN',
online: true,
partialResults: true
});
this.recognizer.on('result', (event) => {
this.voiceText = event.result;
if (event.isLast) {
this.isListening = false;
this.commandHistory.unshift(event.result);
CommandDispatcher.dispatch(event.result);
}
});
this.recognizer.on('error', (err) => {
this.isListening = false;
this.voiceText = '识别失败,请重试';
VoiceFeedback.error('语音识别出错');
});
}
private toggleListening(): void {
if (this.isListening) {
this.recognizer?.stopListening();
this.isListening = false;
} else {
this.voiceText = '正在聆听...';
this.isListening = true;
this.recognizer?.startListening({ maxDuration: 30000, silenceTimeout: 2000 });
}
}
build() {
Column({ space: 20 }) {
// 顶部标题
Text('语音点餐助手')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#1a1a2e')
.margin({ top: 40 });
Text('说"查订单"、"联系骑手"、"搜索肯德基"试试')
.fontSize(13)
.fontColor('#888');
// 语音波形动画
VoiceWaveAnimation({ isActive: this.isListening });
// 识别文字展示
Text(this.voiceText)
.fontSize(18)
.fontColor(this.isListening ? '#3498db' : '#333')
.textAlign(TextAlign.Center)
.padding(20)
.backgroundColor('#f8f9fa')
.borderRadius(12)
.width('90%')
.animation({ duration: 300 });
// 麦克风按钮
VoiceButton({
isListening: this.isListening,
onToggle: () => this.toggleListening()
});
// 指令历史
if (this.commandHistory.length > 0) {
Column({ space: 8 }) {
Text('最近指令')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#555')
.width('90%');
ForEach(this.commandHistory.slice(0, 5), (cmd: string, index: number) => {
Row() {
Text(`"${cmd}"`)
.fontSize(13)
.fontColor('#666')
.layoutWeight(1);
}
.width('90%')
.padding(10)
.backgroundColor('#f0f0f0')
.borderRadius(8);
});
}
.margin({ top: 20 });
}
}
.width('100%')
.height('100%')
.backgroundColor('#ffffff');
}
}
六、唤醒效果与交互界面

上图展示了语音唤醒时的交互界面。包括:麦克风波纹动画、实时文字转写、指令历史记录。整个界面简洁明了,弱化了视觉元素,强化了语音交互的主体地位。
七、性能优化与最佳实践
7.1 性能优化
| 优化项 | 方案 |
|---|---|
| 识别延迟 | 开启 partialResults,边录边识别,缩短首字响应时间 |
| TTS 预加载 | 常用话术提前合成缓存,避免重复调用引擎 |
| 音频焦点 | 录音前请求音频焦点,避免与音乐播放冲突 |
| 降噪处理 | 启用 VAD + AEC,过滤环境噪音和回声 |
7.2 体验设计原则
| 原则 | 说明 |
|---|---|
| 一听就懂 | TTS 反馈避免技术术语,使用自然口语 |
| 一呼就应 | 语音唤醒响应时间 < 500ms,超时需有提示 |
| 一错就帮 | 识别失败时,给出示例指令引导用户 |
| 一多就简 | 搜索结果过多时,语音播报前 3 条,其余展示在屏幕 |
7.3 隐私与权限
// module.json5 权限声明
{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.MICROPHONE" },
{ "name": "ohos.permission.RECORD_AUDIO" },
{ "name": "ohos.permission.INTERNET" }
]
}
}
八、总结
本文从唤醒、识别、理解、执行到反馈,完整演示了 HarmonyOS 6.1 智慧语音服务的接入全链路:
- 语音唤醒:通过按钮或关键词唤醒语音识别引擎;
- 语音识别:使用 SpeechKit 将语音转为文字,支持中间结果返回;
- 语义理解:通过系统 NLP 服务或本地规则匹配,解析用户意图;
- 指令分发:基于路由表将意图映射到具体业务处理函数;
- TTS 反馈:使用 TextToSpeech 引擎将执行结果播报给用户。
开发者行动清单:
- 在
module.json5中申请麦克风与录音权限; - 封装
SpeechRecognizer管理器,统一处理识别生命周期; - 设计指令路由表,覆盖应用的核心高频操作;
- 接入系统 NLP 服务提升意图识别精度,本地规则兜底;
- 初始化 TTS 引擎,设计场景化的语音反馈话术;
- 实现录音时的波纹动画与中间结果实时展示;
- 处理音频焦点冲突,避免与系统音乐/通话互相干扰;
- 为语音交互界面提供文字兜底,满足多模态使用需求。
语音交互不是对触屏的替代,而是对触屏的补充。在双手被占用、视力受限或追求效率的场景中,语音是最自然、最高效的交互方式。HarmonyOS 6.1 的智慧语音服务让这一切变得触手可及。
转载自:https://blog.csdn.net/u014727709/article/details/162938629
欢迎 👍点赞✍评论⭐收藏,欢迎指正
更多推荐



所有评论(0)