在HarmonyOS 6应用开发中,智能语音交互正成为提升用户体验的关键特性。无论是教育类应用的古诗跟读、工具类应用的语音指令,还是内容创作应用的语音输入,准确的语音识别都是基础保障。然而,开发者在实现这些功能时,常常会遇到一个令人困惑的问题:用户朗读的古诗或专业术语被识别为完全不相关的文字,且系统没有提供任何纠正建议。本文将深入剖析这一问题的技术根源,并提供从热词配置到发音评估的完整解决方案,帮助开发者打造更智能的语音交互体验。

一、问题场景:古诗跟读应用的“智能”尴尬

开发者的困扰

假设你正在开发一款面向中小学生的智能古诗学习应用。核心功能之一是“古诗跟读评测”:用户朗读指定的古诗,系统识别并评估发音准确性。然而,测试过程中出现了以下问题:

// 测试场景:用户朗读《静夜思》前两句
用户发音:"床前明月光,疑是地上霜"
预期识别:"床前明月光,疑是地上霜"
实际识别:"窗前明月光,疑是地上双"  // 识别错误且无纠正

更令人困惑的是,当用户发音稍有不准时:

  • “床(chuáng)”可能被识别为“创(chuàng)”

  • “疑(yí)”可能被识别为“一(yī)”

  • 系统直接输出错误结果,没有任何“您说的是XX吗?”的纠正提示

问题表象的背后

这不仅仅是单个应用的尴尬,而是HarmonyOS 6语音识别能力的普遍现象。根据华为官方文档的说明,核心问题在于:

  1. 纠正能力有限:端侧语音识别只预置了少量纠正能力

  2. 无上下文理解:当前识别模型对古诗、专有名词等特殊语境理解有限

  3. 无用户反馈机制:系统无法像搜索引擎那样提供“您要找的是不是”的备选

二、技术原理:端侧语音识别的优势与局限

HarmonyOS 6语音识别架构

在深入解决方案前,有必要了解HarmonyOS 6语音识别的技术架构:

语音识别处理流程:
1. 音频输入 → 2. 特征提取 → 3. 声学模型 → 4. 语言模型 → 5. 解码器 → 6. 文本输出

其中关键的限制点在于:

1. 端侧识别的“轻量化”代价

HarmonyOS语音识别主要运行在端侧(设备本地),这带来了隐私安全和响应速度的优势,但也意味着:

// 模型大小的权衡
端侧模型大小:约50-100MB
云端模型大小:可达数GB

// 结果:端侧模型的纠错能力、语言理解能力有限
// 无法像云端那样通过海量数据和复杂算法进行深度纠错
2. 语言模型的覆盖范围

内置的语言模型主要针对通用语言场景训练:

  • 日常对话:覆盖度较高

  • 新闻资讯:覆盖度中等

  • 古诗文言:覆盖度较低

  • 专业术语:几乎不覆盖

3. 解码器的保守策略

为了确保实时性,端侧解码器通常采用“一次识别,直接输出”的策略,而非生成多个候选结果供选择。

三、根本原因:为什么古诗识别容易出错?

1. 发音相似性陷阱

古诗中存在大量发音相近的字词,在没有上下文的情况下极易混淆:

// 常见混淆对示例
const confusionPairs = [
  { correct: "床(chuáng)", wrong: "创(chuàng)" },  // 声调不同
  { correct: "疑(yí)", wrong: "一(yī)" },          // 声母韵母相似
  { correct: "霜(shuāng)", wrong: "双(shuāng)" },  // 同音字
  { correct: "思(sī)", wrong: "诗(shī)" }         // 平翘舌不分
];

2. 训练数据偏差

语音识别模型的训练数据通常以现代白话文为主,古诗数据的比例极低:

训练数据分布(估算):
- 现代口语对话:60%
- 新闻媒体文本:25%
- 网络社交内容:10%
- 古诗文言文:<1%
- 专业领域术语:<1%

3. 无热词支持的默认行为

在没有显式配置的情况下,语音识别引擎会:

  1. 将输入视为通用语音

  2. 使用通用语言模型解码

  3. 输出最可能的通用文本

  4. 不针对特定领域优化

四、核心解决方案:热词优化机制

华为官方文档明确指出,解决识别不准的主要方法是配置热词。热词机制允许开发者告诉识别引擎:“这些词在我的应用场景中更重要、更可能出现。”

1. 热词的类型与特点

HarmonyOS 6提供了两种热词配置方式,各有适用场景:

热词类型

配置方式

生命周期

数量限制

适用场景

系统热词

创建引擎时配置

引擎生命周期

最大200个

应用全局常用词汇

会话热词

启动识别时配置

单次识别会话

最大200个

临时场景特定词汇

2. 系统热词配置

系统热词在语音识别引擎初始化时配置,适用于整个应用生命周期中都需要优先识别的词汇。

import { speechRecognizer } from '@kit.AbilityKit';

// 创建语音识别引擎
async function createSpeechRecognizerWithHotWords() {
  try {
    // 1. 准备热词列表
    const hotWords = [
      {
        word: "床前明月光",  // 热词内容
        weight: 100          // 权重 (0-100,越高优先级越高)
      },
      {
        word: "疑是地上霜",
        weight: 100
      },
      {
        word: "举头望明月",
        weight: 90
      },
      {
        word: "低头思故乡",
        weight: 90
      },
      // 常见易错词
      {
        word: "李白",
        weight: 80
      },
      {
        word: "唐诗",
        weight: 70
      },
      {
        word: "跟读",
        weight: 60
      }
    ];
    
    // 2. 创建引擎参数
    const createParams: speechRecognizer.CreateEngineParams = {
      mode: speechRecognizer.AudioMode.AUDIO_MODE_NORMAL,
      hotWords: hotWords,  // 关键:配置系统热词
      enablePunctuation: true,  // 启用标点
      enableIntermediateResults: true  // 启用中间结果
    };
    
    // 3. 创建语音识别引擎实例
    const recognizer = await speechRecognizer.createEngine(createParams);
    
    console.info('语音识别引擎创建成功,已加载系统热词');
    return recognizer;
    
  } catch (error) {
    console.error(`创建语音识别引擎失败: ${error.code}, ${error.message}`);
    throw error;
  }
}

3. 会话热词配置

会话热词在每次启动语音识别时配置,适用于当前会话特有的词汇。

// 启动带会话热词的语音识别
async function startRecognitionWithSessionHotWords(
  recognizer: speechRecognizer.SpeechRecognizer
) {
  try {
    // 1. 准备当前会话的热词
    // 例如:当前学习的是《春晓》
    const sessionHotWords = [
      { word: "春眠不觉晓", weight: 100 },
      { word: "处处闻啼鸟", weight: 100 },
      { word: "夜来风雨声", weight: 100 },
      { word: "花落知多少", weight: 100 },
      { word: "孟浩然", weight: 80 },  // 诗人姓名
      { word: "春晓", weight: 90 }     // 诗名
    ];
    
    // 2. 配置启动参数
    const startParams: speechRecognizer.StartParams = {
      hotWords: sessionHotWords,  // 会话热词
      sampleRate: 16000,          // 采样率
      encoding: speechRecognizer.AudioEncoding.ENCODING_PCM_16BIT
    };
    
    // 3. 启动识别
    await recognizer.start(startParams);
    
    console.info('语音识别已启动,已加载会话热词');
    
  } catch (error) {
    console.error(`启动语音识别失败: ${error.code}, ${error.message}`);
    throw error;
  }
}

4. 热词权重策略

热词的权重配置需要策略,不是简单的“越高越好”:

// 智能热词权重分配策略
function calculateHotWordWeights(poemLines: string[]): Array<{word: string, weight: number}> {
  const hotWords: Array<{word: string, weight: number}> = [];
  
  poemLines.forEach((line, index) => {
    // 策略1:完整的诗句行权重最高
    hotWords.push({
      word: line,
      weight: 100 - index * 2  // 前面的诗句稍高
    });
    
    // 策略2:将诗句拆分为词语组合
    const words = splitChineseText(line);
    words.forEach(word => {
      if (word.length > 1) {  // 只添加2个字及以上的词语
        // 根据词语在诗句中的重要性分配权重
        let weight = 70;
        
        if (isKeyWord(word, line)) {
          weight = 85;  // 关键词权重更高
        }
        
        hotWords.push({ word, weight });
      }
    });
  });
  
  return hotWords;
}

// 辅助函数:中文分词(简化版)
function splitChineseText(text: string): string[] {
  // 实际开发中应使用专业的分词库
  // 这里简化为按字符分割,然后组合常见词语
  const result: string[] = [];
  const chars = text.split('');
  
  for (let i = 0; i < chars.length; i++) {
    // 单字
    result.push(chars[i]);
    
    // 两字词
    if (i < chars.length - 1) {
      result.push(chars[i] + chars[i + 1]);
    }
    
    // 三字词
    if (i < chars.length - 2) {
      result.push(chars[i] + chars[i + 1] + chars[i + 2]);
    }
  }
  
  return [...new Set(result)]; // 去重
}

// 判断是否为关键词
function isKeyWord(word: string, line: string): boolean {
  const keyIndicators = ['名词', '动词', '形容词'];
  const lineKeyWords = {
    '床前明月光': ['明月', '月光'],
    '疑是地上霜': ['地上', '霜'],
    '举头望明月': ['举头', '望月'],
    '低头思故乡': ['低头', '思乡', '故乡']
  };
  
  // 简化的关键词判断逻辑
  return lineKeyWords[line]?.includes(word) || false;
}

五、进阶优化:超越热词的综合方案

1. 发音评估与纠错建议

虽然语音识别引擎本身纠正能力有限,但我们可以通过后处理提供纠错建议:

// 发音评估与纠错服务
class PronunciationEvaluationService {
  private recognizer: speechRecognizer.SpeechRecognizer;
  private referenceText: string;  // 标准文本
  
  constructor(recognizer: speechRecognizer.SpeechRecognizer) {
    this.recognizer = recognizer;
  }
  
  // 设置要评估的标准文本
  setReferenceText(text: string): void {
    this.referenceText = text;
  }
  
  // 评估用户发音并提供纠错建议
  async evaluatePronunciation(audioData: ArrayBuffer): Promise<EvaluationResult> {
    const result: EvaluationResult = {
      score: 0,
      accuracy: 0,
      fluency: 0,
      suggestions: [],
      correctedText: ''
    };
    
    try {
      // 1. 语音识别
      const recognizedText = await this.recognizeAudio(audioData);
      
      // 2. 文本对比
      const comparison = this.compareTexts(this.referenceText, recognizedText);
      
      // 3. 生成评估结果
      result.score = this.calculateScore(comparison);
      result.accuracy = comparison.accuracy;
      result.fluency = this.assessFluency(audioData);
      
      // 4. 生成纠错建议
      if (comparison.differences.length > 0) {
        result.suggestions = this.generateSuggestions(comparison);
        result.correctedText = this.generateCorrectedText(comparison);
      }
      
      return result;
      
    } catch (error) {
      console.error(`发音评估失败: ${error.message}`);
      throw error;
    }
  }
  
  // 文本对比算法
  private compareTexts(reference: string, recognized: string): TextComparison {
    const comparison: TextComparison = {
      reference,
      recognized,
      accuracy: 0,
      differences: []
    };
    
    // 使用编辑距离算法计算相似度
    const editDistance = this.calculateEditDistance(reference, recognized);
    const maxLength = Math.max(reference.length, recognized.length);
    
    comparison.accuracy = maxLength > 0 ? 
      (1 - editDistance / maxLength) * 100 : 100;
    
    // 找出具体差异
    comparison.differences = this.findDifferences(reference, recognized);
    
    return comparison;
  }
  
  // 计算编辑距离(Levenshtein距离)
  private calculateEditDistance(s1: string, s2: string): number {
    const m = s1.length;
    const n = s2.length;
    
    // 创建二维数组
    const dp: number[][] = Array(m + 1).fill(0).map(() => 
      Array(n + 1).fill(0)
    );
    
    // 初始化边界条件
    for (let i = 0; i <= m; i++) dp[i][0] = i;
    for (let j = 0; j <= n; j++) dp[0][j] = j;
    
    // 动态规划计算编辑距离
    for (let i = 1; i <= m; i++) {
      for (let j = 1; j <= n; j++) {
        if (s1[i - 1] === s2[j - 1]) {
          dp[i][j] = dp[i - 1][j - 1];
        } else {
          dp[i][j] = Math.min(
            dp[i - 1][j] + 1,     // 删除
            dp[i][j - 1] + 1,     // 插入
            dp[i - 1][j - 1] + 1  // 替换
          );
        }
      }
    }
    
    return dp[m][n];
  }
  
  // 找出具体差异
  private findDifferences(reference: string, recognized: string): TextDifference[] {
    const differences: TextDifference[] = [];
    const refChars = reference.split('');
    const recChars = recognized.split('');
    
    let i = 0, j = 0;
    
    while (i < refChars.length || j < recChars.length) {
      if (i < refChars.length && j < recChars.length && 
          refChars[i] === recChars[j]) {
        // 字符相同,继续前进
        i++;
        j++;
      } else {
        // 发现差异
        const diff: TextDifference = {
          position: i,
          expected: '',
          actual: '',
          type: 'substitution'
        };
        
        // 收集期望的字符
        if (i < refChars.length) {
          diff.expected = refChars[i];
          i++;
        }
        
        // 收集实际的字符
        if (j < recChars.length) {
          diff.actual = recChars[j];
          j++;
        }
        
        // 判断差异类型
        if (diff.expected && !diff.actual) {
          diff.type = 'deletion';
        } else if (!diff.expected && diff.actual) {
          diff.type = 'insertion';
        }
        
        differences.push(diff);
      }
    }
    
    return differences;
  }
  
  // 生成纠错建议
  private generateSuggestions(comparison: TextComparison): Suggestion[] {
    const suggestions: Suggestion[] = [];
    
    comparison.differences.forEach(diff => {
      switch (diff.type) {
        case 'substitution':
          suggestions.push({
            type: 'pronunciation',
            message: `"${diff.actual}"发音可能不准确,应该是"${diff.expected}"`,
            position: diff.position
          });
          break;
          
        case 'deletion':
          suggestions.push({
            type: 'omission',
            message: `可能漏读了"${diff.expected}"`,
            position: diff.position
          });
          break;
          
        case 'insertion':
          suggestions.push({
            type: 'addition',
            message: `可能多读了"${diff.actual}"`,
            position: diff.position
          });
          break;
      }
    });
    
    return suggestions;
  }
  
  // 生成纠正后的文本
  private generateCorrectedText(comparison: TextComparison): string {
    // 这里可以应用更智能的纠正逻辑
    // 例如:基于上下文选择最可能的正确文本
    return comparison.reference; // 简化为直接返回标准文本
  }
  
  // 计算综合分数
  private calculateScore(comparison: TextComparison): number {
    // 基于准确率、流畅度等多项指标计算
    return Math.floor(comparison.accuracy * 0.7 + this.assessFluency() * 0.3);
  }
  
  // 评估流畅度(简化版)
  private assessFluency(audioData?: ArrayBuffer): number {
    // 实际实现中应分析音频特征
    // 这里返回一个模拟值
    return 85;
  }
  
  private async recognizeAudio(audioData: ArrayBuffer): Promise<string> {
    // 语音识别实现
    return "模拟识别结果";
  }
}

// 类型定义
interface EvaluationResult {
  score: number;
  accuracy: number;
  fluency: number;
  suggestions: Suggestion[];
  correctedText: string;
}

interface TextComparison {
  reference: string;
  recognized: string;
  accuracy: number;
  differences: TextDifference[];
}

interface TextDifference {
  position: number;
  expected: string;
  actual: string;
  type: 'substitution' | 'deletion' | 'insertion';
}

interface Suggestion {
  type: string;
  message: string;
  position: number;
}

2. 上下文增强识别

通过应用内上下文信息,提高识别准确率:

// 上下文感知的语音识别器
class ContextAwareRecognizer {
  private recognizer: speechRecognizer.SpeechRecognizer;
  private context: RecognitionContext;
  
  constructor(recognizer: speechRecognizer.SpeechRecognizer) {
    this.recognizer = recognizer;
    this.context = {
      currentPoem: null,
      previousLines: [],
      userHistory: [],
      commonMistakes: new Map()
    };
  }
  
  // 设置当前上下文
  setContext(poem: Poem, previousLines: string[] = []): void {
    this.context.currentPoem = poem;
    this.context.previousLines = previousLines;
    
    // 基于上下文更新热词
    this.updateHotWords();
  }
  
  // 更新热词
  private updateHotWords(): void {
    if (!this.context.currentPoem) return;
    
    const hotWords: speechRecognizer.HotWord[] = [];
    const poem = this.context.currentPoem;
    
    // 1. 添加完整的诗句行
    poem.lines.forEach((line, index) => {
      hotWords.push({
        word: line,
        weight: 100 - index * 3
      });
    });
    
    // 2. 添加上下文相关的词汇
    if (this.context.previousLines.length > 0) {
      // 添加上一句诗句,因为用户可能连续朗读
      const lastLine = this.context.previousLines[this.context.previousLines.length - 1];
      hotWords.push({
        word: lastLine,
        weight: 95
      });
    }
    
    // 3. 添加诗人、诗名等元信息
    hotWords.push({
      word: poem.author,
      weight: 80
    });
    
    hotWords.push({
      word: poem.title,
      weight: 85
    });
    
    // 4. 添加用户常见错误的纠正
    this.context.commonMistakes.forEach((correction, mistake) => {
      hotWords.push({
        word: correction,
        weight: 90
      });
    });
    
    // 应用热词
    this.applyHotWords(hotWords);
  }
  
  // 记录用户错误模式
  recordMistake(recognized: string, correct: string): void {
    if (recognized !== correct) {
      const count = this.context.commonMistakes.get(recognized) || 0;
      this.context.commonMistakes.set(recognized, count + 1);
      
      // 如果错误频繁发生,将其加入热词
      if (count >= 3) {
        this.addCorrectionToHotWords(recognized, correct);
      }
    }
  }
  
  // 添加纠正到热词
  private addCorrectionToHotWords(mistake: string, correction: string): void {
    // 实现添加纠正的逻辑
  }
  
  private applyHotWords(hotWords: speechRecognizer.HotWord[]): void {
    // 实现热词应用逻辑
  }
}

interface RecognitionContext {
  currentPoem: Poem | null;
  previousLines: string[];
  userHistory: RecognitionHistory[];
  commonMistakes: Map<string, number>; // 错误 -> 次数
}

interface Poem {
  id: string;
  title: string;
  author: string;
  lines: string[];
  dynasty: string;
}

interface RecognitionHistory {
  timestamp: number;
  reference: string;
  recognized: string;
  accuracy: number;
}

3. 多候选结果处理

虽然语音识别引擎可能只返回一个结果,但我们可以通过配置获取更多信息:

// 多候选结果处理器
class MultiCandidateProcessor {
  private recognizer: speechRecognizer.SpeechRecognizer;
  
  constructor(recognizer: speechRecognizer.SpeechRecognizer) {
    this.recognizer = recognizer;
  }
  
  // 配置以获取更多识别信息
  async configureForMultipleCandidates(): Promise<void> {
    try {
      // 启用中间结果,这可以提供更多识别信息
      const params: speechRecognizer.CreateEngineParams = {
        mode: speechRecognizer.AudioMode.AUDIO_MODE_NORMAL,
        enableIntermediateResults: true,
        enablePunctuation: true
      };
      
      // 重新创建引擎
      this.recognizer = await speechRecognizer.createEngine(params);
      
    } catch (error) {
      console.error(`配置多候选失败: ${error.message}`);
    }
  }
  
  // 处理中间结果,尝试获取替代候选
  processIntermediateResults(results: any[]): string[] {
    const candidates: string[] = [];
    
    // 从中间结果中提取可能的候选
    results.forEach(result => {
      if (result.text && result.text.length > 0) {
        candidates.push(result.text);
      }
    });
    
    // 去重并排序
    return [...new Set(candidates)].sort((a, b) => b.length - a.length);
  }
  
  // 选择最佳候选
  selectBestCandidate(candidates: string[], context: string[]): string {
    if (candidates.length === 0) return '';
    if (candidates.length === 1) return candidates[0];
    
    // 使用简单的启发式规则选择最佳结果
    return candidates.reduce((best, current) => {
      const bestScore = this.scoreCandidate(best, context);
      const currentScore = this.scoreCandidate(current, context);
      
      return currentScore > bestScore ? current : best;
    }, candidates[0]);
  }
  
  // 为候选文本评分
  private scoreCandidate(candidate: string, context: string[]): number {
    let score = 0;
    
    // 规则1:长度适中(避免过短或过长)
    if (candidate.length >= 3 && candidate.length <= 20) {
      score += 20;
    }
    
    // 规则2:包含常见古诗词汇
    const poemKeywords = ['明月', '故乡', '春风', '秋月', '山水'];
    poemKeywords.forEach(keyword => {
      if (candidate.includes(keyword)) {
        score += 15;
      }
    });
    
    // 规则3:与上下文匹配
    context.forEach(line => {
      if (candidate.includes(line) || line.includes(candidate)) {
        score += 10;
      }
    });
    
    // 规则4:语法合理性(简单检查)
    if (this.isGrammaticallyPlausible(candidate)) {
      score += 5;
    }
    
    return score;
  }
  
  // 简单的语法合理性检查
  private isGrammaticallyPlausible(text: string): boolean {
    // 实际实现中应使用更复杂的检查
    // 这里只做简单检查
    
    // 检查是否包含常见虚词
    const functionWords = ['的', '了', '在', '是', '有'];
    const hasFunctionWord = functionWords.some(word => text.includes(word));
    
    // 检查是否为完整句子
    const hasCompleteStructure = text.length > 2 && 
      !text.endsWith('的') && 
      !text.endsWith('了');
    
    return hasFunctionWord || hasCompleteStructure;
  }
}

六、完整实战:智能古诗跟读应用

1. 应用架构设计

智能古诗跟读应用架构:
┌─────────────────────────────────────────────┐
│               用户界面层                    │
│  ┌─────────────┐  ┌─────────────┐          │
│  │  诗歌展示   │  │  录音按钮   │          │
│  └─────────────┘  └─────────────┘          │
└───────────────────┬─────────────────────────┘
                    │
┌───────────────────▼─────────────────────────┐
│           语音处理层                         │
│  ┌─────────────────────────────────────┐    │
│  │  录音控制 → 语音识别 → 发音评估     │    │
│  └─────────────────────────────────────┘    │
└───────────────────┬─────────────────────────┘
                    │
┌───────────────────▼─────────────────────────┐
│           业务逻辑层                         │
│  ┌─────────────┐  ┌─────────────┐          │
│  │  热词管理   │  │  纠错引擎   │          │
│  └─────────────┘  └─────────────┘          │
└───────────────────┬─────────────────────────┘
                    │
┌───────────────────▼─────────────────────────┐
│           数据持久层                         │
│  ┌─────────────┐  ┌─────────────┐          │
│  │  诗歌库     │  │  用户记录   │          │
│  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────┘

2. 核心组件实现

// 智能古诗跟读主组件
@Component
struct SmartPoemRecitation {
  @State currentPoem: Poem;
  @State isRecording: boolean = false;
  @State recognitionResult: string = '';
  @State evaluationResult: EvaluationResult | null = null;
  @State showCorrection: boolean = false;
  
  private recognizer: speechRecognizer.SpeechRecognizer | null = null;
  private evaluationService: PronunciationEvaluationService;
  private contextAwareRecognizer: ContextAwareRecognizer;
  
  aboutToAppear() {
    this.initializeRecognition();
  }
  
  // 初始化语音识别
  async initializeRecognition() {
    try {
      // 1. 创建语音识别引擎
      this.recognizer = await this.createRecognitionEngine();
      
      // 2. 初始化服务
      this.evaluationService = new PronunciationEvaluationService(this.recognizer);
      this.contextAwareRecognizer = new ContextAwareRecognizer(this.recognizer);
      
      // 3. 设置当前诗歌上下文
      this.contextAwareRecognizer.setContext(this.currentPoem);
      
      console.info('语音识别初始化完成');
      
    } catch (error) {
      console.error(`语音识别初始化失败: ${error.message}`);
      // 可以在这里显示用户友好的错误提示
    }
  }
  
  // 创建语音识别引擎
  private async createRecognitionEngine(): Promise<speechRecognizer.SpeechRecognizer> {
    // 加载诗歌相关的热词
    const hotWords = this.preparePoemHotWords(this.currentPoem);
    
    const params: speechRecognizer.CreateEngineParams = {
      mode: speechRecognizer.AudioMode.AUDIO_MODE_NORMAL,
      hotWords: hotWords,
      enablePunctuation: true,
      enableIntermediateResults: true
    };
    
    return await speechRecognizer.createEngine(params);
  }
  
  // 准备诗歌热词
  private preparePoemHotWords(poem: Poem): speechRecognizer.HotWord[] {
    const hotWords: speechRecognizer.HotWord[] = [];
    
    // 添加完整的诗句行
    poem.lines.forEach((line, index) => {
      hotWords.push({
        word: line,
        weight: 100 - index * 2
      });
    });
    
    // 添加诗人信息
    hotWords.push({
      word: poem.author,
      weight: 80
    });
    
    // 添加诗名
    hotWords.push({
      word: poem.title,
      weight: 85
    });
    
    // 添加常见诗歌词汇
    const commonPoemWords = this.getCommonPoemWords();
    commonPoemWords.forEach(word => {
      hotWords.push({
        word: word,
        weight: 60
      });
    });
    
    return hotWords;
  }
  
  // 开始录音和识别
  async startRecognition() {
    if (!this.recognizer) {
      console.error('语音识别引擎未初始化');
      return;
    }
    
    this.isRecording = true;
    this.recognitionResult = '';
    this.evaluationResult = null;
    this.showCorrection = false;
    
    try {
      // 设置评估的标准文本
      this.evaluationService.setReferenceText(this.currentPoem.lines[0]);
      
      // 启动识别
      await this.contextAwareRecognizer.setContext(
        this.currentPoem,
        [] // 还没有之前的诗句
      );
      
      // 开始录音和识别
      // 实际实现中会处理音频流
      
      // 模拟识别结果
      setTimeout(() => {
        this.onRecognitionComplete("床前明月光");
      }, 2000);
      
    } catch (error) {
      console.error(`开始识别失败: ${error.message}`);
      this.isRecording = false;
    }
  }
  
  // 识别完成处理
  async onRecognitionComplete(text: string) {
    this.isRecording = false;
    this.recognitionResult = text;
    
    // 评估发音
    try {
      // 模拟音频数据
      const audioData = new ArrayBuffer(0);
      
      this.evaluationResult = await this.evaluationService.evaluatePronunciation(audioData);
      
      // 如果有错误,显示纠正建议
      if (this.evaluationResult.suggestions.length > 0) {
        this.showCorrection = true;
        
        // 记录错误模式
        this.contextAwareRecognizer.recordMistake(
          text,
          this.currentPoem.lines[0]
        );
      }
      
    } catch (error) {
      console.error(`发音评估失败: ${error.message}`);
    }
  }
  
  // 获取常见诗歌词汇
  private getCommonPoemWords(): string[] {
    return [
      '明月', '故乡', '春风', '秋月', '山水', '江河',
      '天地', '人间', '相思', '离别', '忧愁', '欢乐',
      '花开', '花落', '日出', '日落', '云卷', '云舒'
    ];
  }
  
  build() {
    Column({ space: 20 }) {
      // 诗歌展示区域
      Column() {
        Text(this.currentPoem.title)
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
        
        Text(`作者:${this.currentPoem.author}`)
          .fontSize(16)
          .fontColor(Color.Gray)
          .margin({ top: 8 })
        
        Divider()
          .margin({ vertical: 16 })
        
        // 显示当前朗读的诗句
        ForEach(this.currentPoem.lines, (line: string, index?: number) => {
          Text(line)
            .fontSize(index === 0 ? 20 : 18)
            .fontColor(index === 0 ? Color.Black : Color.Gray)
            .margin({ bottom: 12 })
        })
      }
      .width('90%')
      .padding(20)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .shadow({ radius: 8, color: Color.Black, offsetX: 2, offsetY: 2 })
      
      // 录音控制区域
      Column() {
        if (this.isRecording) {
          // 录音中状态
          Column() {
            LoadingProgress()
              .width(60)
              .height(60)
              .color(Color.Blue)
            
            Text('正在录音...')
              .fontSize(16)
              .margin({ top: 12 })
          }
        } else {
          // 录音按钮
          Button('开始朗读')
            .width(200)
            .height(60)
            .fontSize(20)
            .backgroundColor(Color.Blue)
            .onClick(() => this.startRecognition())
        }
      }
      .height(120)
      .justifyContent(FlexAlign.Center)
      
      // 识别结果显示
      if (this.recognitionResult) {
        Card() {
          Column({ space: 12 }) {
            Text('识别结果')
              .fontSize(18)
              .fontWeight(FontWeight.Medium)
            
            Text(this.recognitionResult)
              .fontSize(20)
              .fontColor(Color.Blue)
            
            if (this.evaluationResult) {
              Row({ space: 20 }) {
                Column() {
                  Text('准确率')
                    .fontSize(14)
                    .fontColor(Color.Gray)
                  
                  Text(`${this.evaluationResult.accuracy.toFixed(1)}%`)
                    .fontSize(24)
                    .fontColor(this.getAccuracyColor(this.evaluationResult.accuracy))
                }
                
                Column() {
                  Text('流畅度')
                    .fontSize(14)
                    .fontColor(Color.Gray)
                  
                  Text(`${this.evaluationResult.fluency.toFixed(1)}%`)
                    .fontSize(24)
                }
                
                Column() {
                  Text('综合评分')
                    .fontSize(14)
                    .fontColor(Color.Gray)
                  
                  Text(`${this.evaluationResult.score.toFixed(0)}分`)
                    .fontSize(24)
                    .fontWeight(FontWeight.Bold)
                }
              }
              .margin({ top: 12 })
            }
          }
        }
        .width('90%')
        .padding(20)
      }
      
      // 纠错建议
      if (this.showCorrection && this.evaluationResult?.suggestions) {
        Card() {
          Column({ space: 10 }) {
            Text('纠错建议')
              .fontSize(18)
              .fontWeight(FontWeight.Medium)
              .fontColor(Color.Red)
            
            ForEach(this.evaluationResult.suggestions, (suggestion: Suggestion) => {
              Text(`• ${suggestion.message}`)
                .fontSize(14)
                .textAlign(TextAlign.Start)
                .width('100%')
            })
            
            if (this.evaluationResult.correctedText) {
              Divider()
                .margin({ vertical: 10 })
              
              Text('建议朗读:')
                .fontSize(16)
                .fontColor(Color.Green)
                .margin({ bottom: 8 })
              
              Text(this.evaluationResult.correctedText)
                .fontSize(18)
                .fontColor(Color.Green)
                .fontWeight(FontWeight.Medium)
            }
          }
        }
        .width('90%')
        .padding(20)
        .backgroundColor('#FFF5F5')
        .border({ width: 1, color: Color.Red })
      }
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#F5F5F5')
    .alignItems(HorizontalAlign.Center)
  }
  
  // 根据准确率获取颜色
  private getAccuracyColor(accuracy: number): ResourceColor {
    if (accuracy >= 90) return Color.Green;
    if (accuracy >= 70) return Color.Orange;
    return Color.Red;
  }
}

3. 热词管理界面

// 热词管理界面
@Component
struct HotWordManager {
  @State hotWords: speechRecognizer.HotWord[] = [];
  @State newWord: string = '';
  @State newWeight: number = 80;
  
  // 添加新热词
  addHotWord() {
    if (!this.newWord.trim()) {
      return;
    }
    
    this.hotWords.push({
      word: this.newWord.trim(),
      weight: this.newWeight
    });
    
    this.newWord = '';
    this.saveHotWords();
  }
  
  // 删除热词
  deleteHotWord(index: number) {
    this.hotWords.splice(index, 1);
    this.saveHotWords();
  }
  
  // 保存热词
  saveHotWords() {
    // 实际实现中应保存到持久化存储
    console.info('热词已保存:', this.hotWords);
  }
  
  // 导入诗歌热词
  importPoemHotWords(poem: Poem) {
    const poemHotWords = this.extractPoemHotWords(poem);
    this.hotWords = [...this.hotWords, ...poemHotWords];
    this.saveHotWords();
  }
  
  // 从诗歌提取热词
  private extractPoemHotWords(poem: Poem): speechRecognizer.HotWord[] {
    const hotWords: speechRecognizer.HotWord[] = [];
    
    // 添加诗句
    poem.lines.forEach(line => {
      hotWords.push({ word: line, weight: 90 });
    });
    
    // 添加诗人
    hotWords.push({ word: poem.author, weight: 80 });
    
    // 添加诗名
    hotWords.push({ word: poem.title, weight: 85 });
    
    return hotWords;
  }
  
  build() {
    Column({ space: 20 }) {
      // 标题
      Text('热词管理')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      
      // 添加热词表单
      Column({ space: 10 }) {
        Text('添加新热词')
          .fontSize(18)
          .width('100%')
          .textAlign(TextAlign.Start)
        
        Row({ space: 10 }) {
          TextInput({ placeholder: '输入热词', text: this.newWord })
            .onChange((value: string) => {
              this.newWord = value;
            })
            .layoutWeight(1)
          
          Text('权重:')
            .fontSize(14)
          
          Slider({
            value: this.newWeight,
            min: 0,
            max: 100,
            step: 1
          })
            .width(100)
            .onChange((value: number) => {
              this.newWeight = value;
            })
          
          Text(`${this.newWeight}`)
            .fontSize(14)
            .width(30)
        }
        
        Button('添加')
          .width('100%')
          .onClick(() => this.addHotWord())
      }
      .width('100%')
      .padding(15)
      .backgroundColor(Color.White)
      .borderRadius(12)
      
      // 热词列表
      List({ space: 10 }) {
        ForEach(this.hotWords, (hotWord: speechRecognizer.HotWord, index?: number) => {
          ListItem() {
            Row({ space: 10 }) {
              Column({ space: 5 }) {
                Text(hotWord.word)
                  .fontSize(16)
                  .fontWeight(FontWeight.Medium)
                  .width('100%')
                  .textAlign(TextAlign.Start)
                
                Text(`权重: ${hotWord.weight}`)
                  .fontSize(12)
                  .fontColor(Color.Gray)
                  .width('100%')
                  .textAlign(TextAlign.Start)
              }
              .layoutWeight(1)
              
              Button('删除')
                .fontSize(12)
                .padding({ left: 8, right: 8 })
                .onClick(() => this.deleteHotWord(index!))
            }
            .padding(10)
          }
        })
      }
      .layoutWeight(1)
      .width('100%')
      
      // 操作按钮
      Row({ space: 20 }) {
        Button('导入诗歌热词')
          .layoutWeight(1)
          .onClick(() => {
            // 这里应该弹出诗歌选择界面
          })
        
        Button('导出配置')
          .layoutWeight(1)
          .onClick(() => {
            this.exportHotWords();
          })
      }
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
  
  // 导出热词配置
  private exportHotWords() {
    const config = {
      version: '1.0',
      timestamp: new Date().toISOString(),
      hotWords: this.hotWords
    };
    
    console.info('热词配置已导出:', config);
    // 实际实现中可以将配置导出为文件
  }
}

七、最佳实践与总结

1. 热词配置的最佳实践

实践要点

具体建议

原因

数量控制

不超过150个,重点优化关键词汇

太多热词会稀释权重,影响识别性能

权重分配

关键内容100,重要内容80-90,相关内容60-70

明确的权重梯度提高识别准确性

定期更新

根据用户错误模式动态调整

适应不同用户的发音习惯

场景化配置

不同功能使用不同的热词集

提高场景识别准确率

2. 性能优化建议

// 热词加载优化策略
class OptimizedHotWordLoader {
  private cache: Map<string, speechRecognizer.HotWord[]> = new Map();
  
  // 按需加载热词
  async loadHotWordsForScenario(scenarioId: string): Promise<speechRecognizer.HotWord[]> {
    // 检查缓存
    if (this.cache.has(scenarioId)) {
      return this.cache.get(scenarioId)!;
    }
    
    // 从存储加载
    const hotWords = await this.loadFromStorage(scenarioId);
    
    // 缓存结果
    this.cache.set(scenarioId, hotWords);
    
    return hotWords;
  }
  
  // 清理不再使用的热词
  clearUnusedHotWords(usedScenarioIds: string[]) {
    const keysToDelete: string[] = [];
    
    this.cache.forEach((_, key) => {
      if (!usedScenarioIds.includes(key)) {
        keysToDelete.push(key);
      }
    });
    
    keysToDelete.forEach(key => {
      this.cache.delete(key);
    });
  }
}

3. 用户体验优化

  1. 渐进式识别:先识别关键词,再逐步扩展

  2. 实时反馈:识别过程中提供视觉反馈

  3. 错误容忍:允许一定程度的发音偏差

  4. 学习机制:记录用户常见错误,个性化优化

4. 总结

HarmonyOS 6的语音识别在端侧提供了良好的基础能力,但在专业领域和特定场景下的纠正能力确实有限。通过合理的热词配置、发音评估算法和上下文感知,开发者可以显著提升语音识别的准确率。

关键收获:

  • 热词是核心:合理配置热词是提高识别准确率的最有效方法

  • 权重需策略:不是所有热词都平等,需要根据重要性分配权重

  • 上下文很重要:利用应用内上下文信息辅助识别

  • 后处理可增强:即使识别引擎纠正能力有限,应用层仍可提供纠错建议

在HarmonyOS 6的语音交互生态中,虽然系统提供的直接纠正能力有限,但通过灵活运用热词机制和智能的后处理算法,开发者完全可以打造出准确、智能的语音交互体验。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐