一、引言

2025-2026 年,三大操作系统不约而同地将 AI 作为核心战略:

系统 AI 品牌 大模型 核心策略
HarmonyOS 鸿蒙智能 盘古大模型 系统级深度融合 + 端侧推理
iOS Apple Intelligence 自研(端侧+云) 隐私优先 + 端侧为主
Android Google AI / Gemini Gemini 云 + 端混合,Google 服务整合

本文将对比三者在 AI 能力上的策略差异,并重点给出鸿蒙 AI API 的开发者接入实战指南


二、三大操作系统 AI 策略对比

2.1 总体对比

维度 HarmonyOS 鸿蒙智能 iOS Apple Intelligence Android Gemini AI
大模型 盘古大模型 Apple 自研(ANE)+ GPT Gemini Nano(端)+ Gemini(云)
端侧推理 ✅ NPU + MindSpore Lite ✅ Neural Engine + CoreML ✅ Gemini Nano(部分设备)
云侧推理 ✅ 盘古云服务 ✅ 私有云 + ChatGPT ✅ Gemini Cloud
系统整合度 ⭐⭐⭐⭐⭐ 最深 ⭐⭐⭐⭐ ⭐⭐⭐
开发者 API HMAF + Neural API CoreML + MLX ML Kit + GenAI Kit
多模态 文字+图片+语音+文档 文字+图片+语音 文字+图片+语音+视频
隐私策略 端侧为主 + 星盾 端侧为主 + 私有云 端侧 + 云(需用户同意)
第三方模型支持 有限 ChatGPT 集成 Gemini 为主

2.2 小艺 vs Siri vs Gemini

维度 小艺(HarmonyOS) Siri(iOS) Google Gemini(Android)
方言支持 16 种方言 不支持中文方言 不支持中文方言
多轮对话 ✅ 盘古大模型驱动 ✅ ChatGPT 加持 ✅ Gemini 驱动
屏幕理解 ✅ 识屏对话 + 圈选 ❌ 有限 ✅ Circle to Search
跨应用操作 ✅ 智能体框架 HMAF ⚠️ Shortcuts 有限 ⚠️ 有限
文档处理 ✅ 拖拽上传分析 ✅ 有限 ✅ Google Docs 整合
图片编辑 ✅ AI 消除/成片/修复 ✅ Clean Up ✅ Magic Editor
端侧能力 ✅ 7.0 端侧自主决策 ✅ 端侧为主 ⚠️ 部分设备

2.3 开发者 API 成熟度

API 类别 HarmonyOS iOS Android
语音识别 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
图像分类 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
文字识别 OCR ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
自然语言理解 ⭐⭐⭐⭐(盘古) ⭐⭐⭐⭐ ⭐⭐⭐⭐(Gemini)
端侧模型部署 ⭐⭐⭐⭐(MindSpore) ⭐⭐⭐⭐⭐(CoreML) ⭐⭐⭐⭐(TF Lite)
智能体框架 ⭐⭐⭐⭐⭐(HMAF) ⭐⭐(Shortcuts) ⭐⭐(无系统级)
多模态推理 ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

HMAF(鸿蒙智能体框架)是三者中唯一的系统级智能体框架,这是鸿蒙 AI 对开发者最大的差异化优势。


三、鸿蒙 AI 能力接入实战

3.1 AI 能力全景图

鸿蒙 AI API(按使用频率排序)
│
├─ 基础 AI(无需模型,直接调用 API)
│   ├─ 语音识别 → @ohos.speechRecognizer
│   ├─ 文字转语音 → @ohos.textToSpeech
│   ├─ 图像 OCR → @ohos.ocr
│   └─ 人脸检测 → @ohos.faceDetector
│
├─ 进阶 AI(需加载模型,MindSpore Lite)
│   ├─ 图片超分 → imageAI.superResolution
│   ├─ 图片分类 → imageAI.classify
│   ├─ 物体检测 → imageAI.objectDetect
│   └─ 文本分析 → nlu.analyzeIntent
│
├─ 系统 AI(系统级能力,无需额外集成)
│   ├─ 小艺意图分发 → 注册为智能体
│   ├─ AI 防窥屏 → 系统自动
│   ├─ AI 防诈 → 系统自动
│   └─ 实况窗 → 接入实况窗 API
│
└─ 自定义 AI(开发者自己的模型)
    └─ MindSpore Lite 部署 → 自定义推理

3.2 语音识别:最常用的 AI API

// 语音识别 — 将语音转为文字
import { speechRecognizer } from '@kit.AIKit';

async function startVoiceInput(): Promise<string> {
  const recognizer = speechRecognizer.createRecognizer();

  // 配置识别参数
  recognizer.setParams({
    language: 'zh-CN',      // 支持 16 种方言
    recognitionMode: 'real-time',  // 实时识别
    enablePunctuation: true // 自动加标点
  });

  // 开始监听
  recognizer.start();

  return new Promise((resolve) => {
    recognizer.on('result', (result: speechRecognizer.RecognizerResult) => {
      if (result.isFinal) {
        resolve(result.text);
        recognizer.stop();
      }
    });
  });
}
错误处理与边界条件
// 带错误处理的语音识别
import { speechRecognizer, BusinessError } from '@kit.AIKit';
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';

async function safeVoiceInput(): Promise<string | null> {
  // 1. 权限检查:麦克风权限
  const atManager = abilityAccessCtrl.createAtManager();
  const grantStatus = await atManager.checkAccessToken({
    permission: 'ohos.permission.MICROPHONE' as Permissions
  });
  if (grantStatus !== 0) {
    console.error('麦克风权限未授予');
    // 可在此调用 requestPermissionsFromUser 弹窗申请
    return null;
  }

  let recognizer: speechRecognizer.Recognizer | null = null;
  try {
    recognizer = speechRecognizer.createRecognizer();

    // 2. 设备能力检查:是否支持语音识别
    const isSupported = await recognizer.isRecognizerSupported();
    if (!isSupported) {
      console.error('当前设备不支持语音识别');
      return null;
    }

    recognizer.setParams({
      language: 'zh-CN',
      recognitionMode: 'real-time',
      enablePunctuation: true
    });

    recognizer.start();

    return await new Promise((resolve, reject) => {
      // 3. 超时保护:30 秒无结果自动取消
      const timeout = setTimeout(() => {
        recognizer?.stop();
        reject(new Error('语音识别超时'));
      }, 30000);

      recognizer!.on('result', (result) => {
        if (result.isFinal) {
          clearTimeout(timeout);
          resolve(result.text);
          recognizer?.stop();
        }
      });

      // 4. 错误监听
      recognizer!.on('error', (err: BusinessError) => {
        clearTimeout(timeout);
        reject(new Error(`语音识别错误: ${err.message} (code: ${err.code})`));
      });
    });
  } catch (err) {
    const bizErr = err as BusinessError;
    console.error(`语音识别失败: code=${bizErr.code}, message=${bizErr.message}`);
    return null;
  } finally {
    recognizer?.release(); // 释放资源
  }
}

边界条件说明:

场景 处理方式
麦克风权限被拒绝 检查 ohos.permission.MICROPHONE 权限,未授权时弹窗引导用户开启
设备不支持语音识别 调用 isRecognizerSupported() 预检,不支持时降级为文本输入
网络异常(云识别模式) 语音识别支持端侧离线模式,建议优先使用离线识别
用户长时间不说话 设置 30 秒超时,超时后自动停止并提示用户
识别结果为空 检查 result.text 长度,为空时提示用户重新说话
多实例冲突 每次使用前 release() 旧实例,避免重复创建
后台运行 onBackground() 中停止识别,onForeground() 恢复

3.3 图像超分:端侧 AI 的典型应用

// 端侧 AI 图片超分 — 无需联网,本地 NPU 推理
import { imageAI } from '@kit.ImageAIKit';

async function enhancePhoto(uri: string): Promise<imageAI.EnhanceResult> {
  const options: imageAI.SuperResolutionOptions = {
    scale: 2,           // 2x 超分
    denoise: true,      // 去噪
    enhanceFace: true,   // 人脸增强
    device: 'NPU'       // 在 NPU 上运行(华为 Kirin/Ascend NPU)
  };

  const result = await imageAI.superResolution(uri, options);
  console.info('超分完成:', result.outputUri);
  return result;
}
错误处理与边界条件
// 带错误处理的图像超分
import { imageAI, BusinessError } from '@kit.ImageAIKit';
import { fileIo } from '@kit.CoreFileKit';

interface SafeEnhanceResult {
  success: boolean;
  outputUri?: string;
  error?: string;
  fallback?: 'cpu' | 'skipped'; // 降级策略
}

async function safeEnhancePhoto(uri: string): Promise<SafeEnhanceResult> {
  try {
    // 1. 输入校验:文件是否存在
    try {
      const file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
      fileIo.closeSync(file);
    } catch {
      return { success: false, error: '图片文件不存在或无法访问' };
    }

    // 2. 设备能力检查:是否支持 NPU 推理
    const npuAvailable = await imageAI.isNPUSupported();
    const device = npuAvailable ? 'NPU' : 'CPU';

    if (!npuAvailable) {
      console.warn('当前设备无 NPU,降级到 CPU 推理(速度较慢)');
    }

    // 3. 图片尺寸校验:超分后尺寸不应过大
    const fileInfo = await fileIo.stat(uri);
    if (fileInfo.size > 50 * 1024 * 1024) { // 50MB 限制
      return { success: false, error: '图片过大,建议压缩后重试' };
    }

    const options: imageAI.SuperResolutionOptions = {
      scale: 2,
      denoise: true,
      enhanceFace: true,
      device: device
    };

    const result = await imageAI.superResolution(uri, options);

    // 4. 结果校验
    if (!result.outputUri) {
      return { success: false, error: '超分结果为空' };
    }

    return {
      success: true,
      outputUri: result.outputUri,
      fallback: npuAvailable ? undefined : 'cpu'
    };
  } catch (err) {
    const bizErr = err as BusinessError;
    // 常见错误码处理
    switch (bizErr.code) {
      case 201: // 权限拒绝
        return { success: false, error: '缺少文件读取权限' };
      case 202: // 参数错误
        return { success: false, error: `参数无效: ${bizErr.message}` };
      case 2201006: // 内存不足
        return { success: false, error: '设备内存不足,请关闭其他应用后重试' };
      default:
        return { success: false, error: `超分失败: ${bizErr.message}` };
    }
  }
}

边界条件说明:

场景 处理方式
设备无 NPU 自动降级到 CPU 推理(速度慢 3-5 倍),并提示用户
图片文件损坏或不存在 调用前用 fileIo.openSync 校验文件可读性
图片过大(>50MB) 建议先压缩再超分,避免 OOM
内存不足 捕获 2201006 错误码,提示用户释放内存
超分倍数过高 建议 scale 不超过 4,否则画质损失明显
输入图片分辨率过低 小于 100×100 的图片超分效果有限,建议提示用户
NPU 驱动异常 降级到 CPU 并记录日志,不影响主流程

3.4 文本意图理解:构建智能交互

// NLU 意图分析 — 让 App 理解用户说什么
import { nlu } from '@kit.AIKit';

interface IntentResult {
  intent: string;          // 意图名称
  confidence: number;      // 置信度 0-1
  entities: Record<string, string>; // 提取的实体
}

async function analyzeUserInput(text: string): Promise<IntentResult> {
  const result = await nlu.analyzeIntent({
    text: text,
    domain: 'shopping',    // 领域限定,提高准确率
    // 预定义意图
    intents: [
      'search_product',
      'add_to_cart',
      'check_order',
      'cancel_order',
      'get_discount'
    ]
  });

  return {
    intent: result.intent,
    confidence: result.score,
    entities: result.entity
  };
}

// 使用示例
const result = await analyzeUserInput('帮我看看上次买的米粉还有优惠吗');
// → { intent: 'check_order', confidence: 0.92, entities: { product: '米粉' } }
错误处理与边界条件
// 带错误处理的意图分析
import { nlu, BusinessError } from '@kit.AIKit';

interface SafeIntentResult {
  success: boolean;
  intent?: string;
  confidence?: number;
  entities?: Record<string, string>;
  fallbackIntent?: string; // 兜底意图
  error?: string;
}

async function safeAnalyzeInput(text: string): Promise<SafeIntentResult> {
  // 1. 输入校验:空文本或过短
  if (!text || text.trim().length === 0) {
    return { success: false, error: '输入文本不能为空' };
  }
  if (text.trim().length < 2) {
    return { success: false, error: '输入文本过短,无法分析意图' };
  }
  if (text.length > 500) {
    console.warn('输入文本过长,将截取前 500 字符');
    text = text.substring(0, 500);
  }

  try {
    // 2. 设备能力检查
    const isSupported = await nlu.isNLUSupported();
    if (!isSupported) {
      // 降级:使用简单的关键词匹配
      return fallbackKeywordMatch(text);
    }

    const result = await nlu.analyzeIntent({
      text: text,
      domain: 'shopping',
      intents: [
        'search_product',
        'add_to_cart',
        'check_order',
        'cancel_order',
        'get_discount'
      ]
    });

    // 3. 置信度阈值判断
    if (!result || result.score < 0.3) {
      // 置信度过低,使用兜底意图
      return {
        success: true,
        intent: 'unknown',
        confidence: result?.score ?? 0,
        entities: {},
        fallbackIntent: 'fallback_to_human',
        error: '无法准确识别意图,已转人工'
      };
    }

    return {
      success: true,
      intent: result.intent,
      confidence: result.score,
      entities: result.entity || {}
    };
  } catch (err) {
    const bizErr = err as BusinessError;
    console.error(`意图分析失败: code=${bizErr.code}`);

    // 4. 异常降级:关键词匹配
    return fallbackKeywordMatch(text);
  }
}

// 兜底方案:简单关键词匹配
function fallbackKeywordMatch(text: string): SafeIntentResult {
  const lowerText = text.toLowerCase();
  if (lowerText.includes('搜索') || lowerText.includes('找')) {
    return { success: true, intent: 'search_product', confidence: 0.5, fallbackIntent: 'keyword_match' };
  }
  if (lowerText.includes('订单') || lowerText.includes('查')) {
    return { success: true, intent: 'check_order', confidence: 0.5, fallbackIntent: 'keyword_match' };
  }
  return { success: true, intent: 'unknown', confidence: 0.1, fallbackIntent: 'fallback_to_human' };
}

边界条件说明:

场景 处理方式
输入文本为空或过短 长度 < 2 字符时直接返回错误,提示用户补充描述
输入文本过长 截取前 500 字符,避免模型处理超时
设备不支持 NLU 降级为关键词匹配兜底方案
置信度过低(<0.3) 标记为 unknown 意图,转人工或引导用户重新表述
意图不在预定义列表中 返回 unknown,由业务层决定是否扩展意图列表
领域不匹配 用户输入与 domain 无关时,置信度会很低,建议提供多领域支持
实体提取为空 返回空对象,不影响主流程,可提示用户补充关键信息

3.5 注册为智能体:让你的 App 被小艺调用

这是鸿蒙 AI 最独特的能力——让你的 App 成为小艺可以调度的智能体:

// 1. 创建智能体 Ability
import { AgentExtension } from '@kit.AbilityKit';

export default class ShoppingAgent extends AgentExtension {
  // 智能体被唤醒时调用
  onCall(intent: AgentIntent): void {
    console.info('智能体被调用:', intent.name);
  }

  // 处理用户的意图
  onHandleIntent(intent: AgentIntent): Promise<AgentResult> {
    switch (intent.name) {
      case 'search_product':
        return this.searchProduct(intent.parameters.query);
      case 'check_order':
        return this.checkOrder(intent.parameters.orderId);
      default:
        return Promise.reject(new Error('未支持的意图'));
    }
  }

  private async searchProduct(query: string): Promise<AgentResult> {
    // 调用你的搜索服务
    return { status: 'success', data: await api.search(query) };
  }
}
// 2. 注册智能体到 module.json5
{
  "extensionAbilities": [{
    "name": "ShoppingAgent",
    "type": "agent",
    "capabilities": {
      "intents": [
        {
          "name": "search_product",
          "description": "搜索商品",
          "parameters": [
            { "name": "query", "type": "string", "required": true }
          ]
        },
        {
          "name": "check_order",
          "description": "查询订单状态",
          "parameters": [
            { "name": "orderId", "type": "string", "required": true }
          ]
        }
      ]
    }
  }]
}

注册后,用户可以直接对小艺说"帮我查一下京东上次买的手机到哪了",小艺会自动调度你的智能体来完成。

错误处理与边界条件
// 带错误处理的智能体实现
import { AgentExtension, AgentIntent, AgentResult, BusinessError } from '@kit.AbilityKit';

export default class RobustShoppingAgent extends AgentExtension {
  onCall(intent: AgentIntent): void {
    console.info('智能体被调用:', intent.name);
  }

  async onHandleIntent(intent: AgentIntent): Promise<AgentResult> {
    try {
      // 1. 参数校验
      if (!intent.parameters) {
        return this.buildErrorResult('缺少参数', 'PARAM_MISSING');
      }

      switch (intent.name) {
        case 'search_product': {
          const query = intent.parameters.query;
          if (!query || query.trim().length === 0) {
            return this.buildErrorResult('搜索关键词不能为空', 'INVALID_QUERY');
          }
          if (query.length > 100) {
            return this.buildErrorResult('搜索关键词过长', 'QUERY_TOO_LONG');
          }
          return await this.safeSearchProduct(query);
        }

        case 'check_order': {
          const orderId = intent.parameters.orderId;
          if (!orderId) {
            return this.buildErrorResult('订单号不能为空', 'MISSING_ORDER_ID');
          }
          if (!/^ORD\d{10,20}$/.test(orderId)) {
            return this.buildErrorResult('订单号格式无效', 'INVALID_ORDER_ID');
          }
          return await this.safeCheckOrder(orderId);
        }

        default:
          return this.buildErrorResult(
            `不支持的意图: ${intent.name}`,
            'UNSUPPORTED_INTENT'
          );
      }
    } catch (err) {
      // 2. 全局异常捕获
      const bizErr = err as BusinessError;
      console.error('智能体处理异常:', bizErr);
      return this.buildErrorResult(
        '系统繁忙,请稍后重试',
        'INTERNAL_ERROR',
        bizErr.code
      );
    }
  }

  private async safeSearchProduct(query: string): Promise<AgentResult> {
    try {
      const data = await api.search(query);
      // 3. 结果为空处理
      if (!data || (Array.isArray(data) && data.length === 0)) {
        return {
          status: 'success',
          data: [],
          message: '未找到相关商品'
        };
      }
      return { status: 'success', data };
    } catch (err) {
      // 4. 网络/服务异常
      return this.buildErrorResult('搜索服务暂时不可用', 'SERVICE_UNAVAILABLE');
    }
  }

  private async safeCheckOrder(orderId: string): Promise<AgentResult> {
    try {
      const order = await api.getOrder(orderId);
      if (!order) {
        return {
          status: 'success',
          data: null,
          message: '未找到该订单'
        };
      }
      return { status: 'success', data: order };
    } catch (err) {
      return this.buildErrorResult('查询订单服务异常', 'ORDER_SERVICE_ERROR');
    }
  }

  private buildErrorResult(
    message: string,
    errorCode: string,
    detail?: number
  ): AgentResult {
    return {
      status: 'error',
      error: {
        code: errorCode,
        message: message,
        detail: detail
      }
    };
  }
}

边界条件说明:

场景 处理方式
参数缺失或无效 校验每个意图的必填参数,缺失时返回 PARAM_MISSING 错误码
意图不在注册列表中 返回 UNSUPPORTED_INTENT,小艺会提示用户该 App 不支持此操作
搜索/查询结果为空 返回空数据 + 友好提示,而非直接报错
后端服务超时 设置请求超时(建议 5 秒),超时后返回 SERVICE_UNAVAILABLE
网络异常 捕获网络错误,返回可读的错误信息,小艺会提示用户检查网络
用户取消操作 监听 onCancel() 回调,及时释放资源
智能体被频繁调用 实现限流逻辑,同一用户 1 秒内最多处理 3 次请求
module.json5 配置错误 确保 type"agent"intents 中的参数名与代码一致

3.6 典型应用场景交互流程图

以下是三个典型应用场景中,用户、App、鸿蒙 AI API 之间的调用时序和数据流向示意图:

场景一:语音搜索
后端服务 鸿蒙AI API App 用户 后端服务 鸿蒙AI API App 用户 点击语音搜索按钮 1. 检查麦克风权限 权限状态 2. 调用 speechRecognizer.start() 3. 说话输入语音 4. 端侧语音识别 5. 返回识别文本 6. 发送搜索请求 7. 返回搜索结果 8. 展示搜索结果

数据流向说明:

  1. 权限检查:App 检查 ohos.permission.MICROPHONE 权限
  2. 启动识别:调用 speechRecognizer.start() 开始监听
  3. 语音输入:用户说话,音频数据流式传输到 API
  4. 端侧推理:在设备 NPU/CPU 上实时转文字
  5. 文本返回:识别完成的文本返回给 App
  6. 业务处理:App 将文本发送到后端搜索服务
  7. 结果返回:后端返回结构化搜索结果
  8. 界面展示:App 渲染搜索结果给用户
场景二:图片美化
文件系统 鸿蒙AI API App 用户 文件系统 鸿蒙AI API App 用户 1. 选择图片/拍照 2. 读取图片文件 3. 返回图片 URI 4. 检查 NPU 支持 NPU 可用性 5. 调用 imageAI.superResolution() 6. NPU/CPU 推理 7. 返回增强后图片 URI 8. 保存处理结果 保存成功 9. 展示美化后图片

数据流向说明:

  1. 用户输入:用户选择图片或拍照
  2. 文件读取:App 从文件系统读取图片
  3. URI 返回:获得图片的本地 URI
  4. 设备检查:调用 imageAI.isNPUSupported() 检查硬件
  5. 调用超分:传入图片 URI 和参数调用超分 API
  6. 端侧推理:在 NPU(优先)或 CPU 上执行图像增强
  7. 结果返回:返回处理后的图片 URI
  8. 结果保存:将增强后的图片保存到文件系统
  9. 界面更新:在 UI 中展示美化后的图片
场景三:智能客服
业务服务 App智能体 智能体框架 小艺 用户 业务服务 App智能体 智能体框架 小艺 用户 1. 语音/文字提问 2. 意图识别与分发 3. 调用 onHandleIntent() 4. 参数校验与处理 5. 调用后端 API 6. 返回业务数据 7. 返回 AgentResult 8. 格式化响应 9. 语音/文字回答

数据流向说明:

  1. 用户提问:用户通过语音或文字向小艺提问
  2. 意图识别:小艺识别用户意图,通过 HMAF 框架分发
  3. 智能体唤醒:框架调用对应 App 智能体的 onHandleIntent()
  4. 参数处理:智能体校验参数,准备业务请求
  5. 业务调用:智能体调用 App 的后端服务 API
  6. 数据返回:后端返回业务数据(如订单信息)
  7. 结果封装:智能体将数据封装为 AgentResult
  8. 响应格式化:框架将结果格式化为小艺可读的响应
  9. 用户反馈:小艺以语音或文字形式回答用户

关键特点:

  • 系统级集成:用户无需打开 App,直接对小艺说话
  • 自动分发:HMAF 框架根据意图自动路由到对应智能体
  • 无缝体验:用户感知不到 App 切换,全程由小艺调度

四、AI 应用场景实战对照表

场景 传统方案 鸿蒙 AI 方案 代码量减少
语音搜索 集成第三方 SDK speechRecognizer 内置 API -70%
图片美化 自研滤镜算法 imageAI.enhance 端侧 AI -90%
智能客服 自建 NLP 服务 注册为智能体,用小艺理解 -60%
内容审核 云 API imageAI.classify 端侧 -80%
语音笔记 第三方转录服务 speechRecognizer + 备忘录智能体 -75%
多语言翻译 云翻译 API @ohos/translation -50%
文档扫描 第三方扫描库 @ohos/ocr 内置 OCR -80%

五、MindSpore Lite:部署自定义模型

如果预置 AI API 不够用,你需要部署自己的模型。MindSpore Lite 是鸿蒙官方推荐的端侧推理框架,支持 CPU、GPU、NPU 多种硬件加速。

5.1 完整代码示例:加载、推理、释放全流程

// 完整示例:MindSpore Lite 自定义模型部署
import { mindSporeLite, BusinessError } from '@kit.AIKit';
import { fileIo } from '@kit.CoreFileKit';

// 定义模型输入输出接口
interface ModelInput {
  data: Float32Array;
  shape: number[];
}

interface ModelOutput {
  data: Float32Array;
  shape: number[];
  inferenceTime: number;
}

/**
 * 安全运行自定义模型
 * @param modelPath 模型文件路径(.ms 格式)
 * @param inputs 输入数据数组
 * @param device 推理设备:'CPU' | 'GPU' | 'NPU'
 * @returns 推理结果数组
 */
async function runCustomModelSafely(
  modelPath: string,
  inputs: ModelInput[],
  device: 'CPU' | 'GPU' | 'NPU' = 'NPU'
): Promise<ModelOutput[]> {
  let model: mindSporeLite.Model | null = null;
  
  try {
    // 1. 检查模型文件是否存在
    try {
      const fileInfo = await fileIo.stat(modelPath);
      if (fileInfo.size === 0) {
        throw new Error('模型文件为空');
      }
      console.info(`模型文件大小: ${(fileInfo.size / 1024 / 1024).toFixed(2)} MB`);
    } catch (err) {
      throw new Error(`模型文件不存在或无法访问: ${modelPath}`);
    }

    // 2. 检查设备支持情况
    const deviceSupport = await mindSporeLite.isDeviceSupported(device);
    if (!deviceSupport) {
      console.warn(`设备不支持 ${device},降级到 CPU`);
      device = 'CPU';
    }

    // 3. 加载模型
    console.info(`开始加载模型: ${modelPath}`);
    const loadStart = performance.now();
    
    model = await mindSporeLite.loadModel({
      modelPath: modelPath,
      device: device,
      numThread: 4,           // 推理线程数
      enableFloat16: device === 'NPU',  // NPU 启用 FP16 加速
      cacheDir: '/data/storage/el2/base/cache/models' // 模型缓存目录
    });
    
    const loadTime = performance.now() - loadStart;
    console.info(`模型加载完成,耗时: ${loadTime.toFixed(2)}ms`);

    // 4. 验证输入数据与模型匹配
    const modelInputs = model.getInputs();
    if (modelInputs.length !== inputs.length) {
      throw new Error(`模型期望 ${modelInputs.length} 个输入,但提供了 ${inputs.length}`);
    }

    // 5. 准备输入数据
    for (let i = 0; i < modelInputs.length; i++) {
      const inputTensor = modelInputs[i];
      const inputData = inputs[i];
      
      // 检查输入形状是否匹配
      const expectedShape = inputTensor.getShape();
      const actualShape = inputData.shape;
      
      if (!this.arraysEqual(expectedShape, actualShape)) {
        throw new Error(`输入 ${i} 形状不匹配。期望: ${expectedShape},实际: ${actualShape}`);
      }
      
      // 设置输入数据
      inputTensor.setData(inputData.data);
    }

    // 6. 执行推理
    console.info(`开始推理,设备: ${device}`);
    const inferenceStart = performance.now();
    
    const outputTensors = await model.predict(modelInputs);
    
    const inferenceTime = performance.now() - inferenceStart;
    console.info(`推理完成,耗时: ${inferenceTime.toFixed(2)}ms`);

    // 7. 提取输出结果
    const outputs: ModelOutput[] = [];
    for (const outputTensor of outputTensors) {
      const outputData = outputTensor.getData() as Float32Array;
      const outputShape = outputTensor.getShape();
      
      outputs.push({
        data: outputData,
        shape: outputShape,
        inferenceTime: inferenceTime
      });
    }

    return outputs;

  } catch (err) {
    const bizErr = err as BusinessError;
    console.error(`模型推理失败: code=${bizErr.code}, message=${bizErr.message}`);
    
    // 常见错误处理
    switch (bizErr.code) {
      case 201: // 权限拒绝
        throw new Error('缺少模型文件读取权限');
      case 202: // 参数错误
        throw new Error(`模型参数错误: ${bizErr.message}`);
      case 2201001: // 模型格式错误
        throw new Error('模型文件格式错误,请检查是否为 .ms 格式');
      case 2201002: // 模型加载失败
        throw new Error('模型加载失败,可能文件损坏或不兼容');
      case 2201006: // 内存不足
        throw new Error('设备内存不足,请关闭其他应用后重试');
      case 2201007: // 设备不支持
        throw new Error(`当前设备不支持 ${device} 推理,请切换到 CPU`);
      default:
        throw new Error(`推理错误: ${bizErr.message}`);
    }
  } finally {
    // 8. 释放模型资源
    if (model) {
      try {
        model.release();
        console.info('模型资源已释放');
      } catch (releaseErr) {
        console.warn('模型资源释放失败:', releaseErr);
      }
    }
  }
}

// 辅助函数:比较数组是否相等
private arraysEqual(arr1: number[], arr2: number[]): boolean {
  if (arr1.length !== arr2.length) return false;
  for (let i = 0; i < arr1.length; i++) {
    if (arr1[i] !== arr2[i]) return false;
  }
  return true;
}

// 使用示例:图像分类模型
async function classifyImage(imageUri: string): Promise<string> {
  try {
    // 1. 预处理图像数据(这里简化处理,实际需要图像预处理)
    const imageData = await preprocessImage(imageUri);
    
    // 2. 准备模型输入
    const inputs: ModelInput[] = [{
      data: imageData, // Float32Array 格式的图像数据
      shape: [1, 224, 224, 3] // 假设是标准的 224x224 RGB 图像
    }];
    
    // 3. 运行模型
    const outputs = await runCustomModelSafely(
      '/data/storage/el2/base/models/mobilenet_v2.ms',
      inputs,
      'NPU' // 优先使用 NPU
    );
    
    // 4. 后处理:获取分类结果
    const probabilities = outputs[0].data;
    const classId = this.argmax(probabilities);
    const className = CLASS_NAMES[classId] || `类别 ${classId}`;
    
    return `识别结果: ${className} (置信度: ${probabilities[classId].toFixed(4)})`;
    
  } catch (err) {
    console.error('图像分类失败:', err);
    return '识别失败,请重试';
  }
}

// 辅助函数:获取最大值索引
private argmax(array: Float32Array): number {
  let maxIndex = 0;
  let maxValue = array[0];
  for (let i = 1; i < array.length; i++) {
    if (array[i] > maxValue) {
      maxValue = array[i];
      maxIndex = i;
    }
  }
  return maxIndex;
}

// 图像预处理函数(简化示例)
async function preprocessImage(uri: string): Promise<Float32Array> {
  // 实际实现需要:
  // 1. 读取图片文件
  // 2. 调整尺寸到模型输入大小
  // 3. 归一化像素值
  // 4. 转换为 Float32Array
  
  // 这里返回模拟数据
  const size = 224 * 224 * 3; // 224x224 RGB
  return new Float32Array(size).fill(0.5);
}

// 类别名称映射(示例)
const CLASS_NAMES = [
  '苹果', '香蕉', '橙子', '草莓', '葡萄',
  '猫', '狗', '鸟', '汽车', '飞机'
];

5.2 错误处理与边界条件

场景 处理方式 代码示例
模型文件不存在 加载前检查文件可访问性 fileIo.stat(modelPath)
模型格式错误 捕获 2201001 错误码 case 2201001: throw new Error('模型格式错误')
输入形状不匹配 验证输入与模型期望形状 arraysEqual(expectedShape, actualShape)
内存不足 捕获 2201006 错误码,提示用户 case 2201006: throw new Error('内存不足')
设备不支持 NPU 降级到 CPU 推理 if (!deviceSupport) device = 'CPU'
推理超时 设置超时限制 Promise.race([predict(), timeoutPromise])
多线程冲突 确保单次只运行一个模型实例 使用锁或队列机制
模型缓存失效 定期清理缓存目录 fileIo.rmdir(cacheDir)

5.3 性能优化建议

// 性能优化:模型预热与缓存
class ModelManager {
  private modelCache: Map<string, mindSporeLite.Model> = new Map();
  
  async getModel(modelPath: string, device: string): Promise<mindSporeLite.Model> {
    const cacheKey = `${modelPath}_${device}`;
    
    // 1. 检查缓存
    if (this.modelCache.has(cacheKey)) {
      console.info('从缓存加载模型');
      return this.modelCache.get(cacheKey)!;
    }
    
    // 2. 预热加载(后台线程)
    console.info('预热加载模型...');
    const model = await mindSporeLite.loadModel({
      modelPath,
      device,
      numThread: 4
    });
    
    // 3. 加入缓存
    this.modelCache.set(cacheKey, model);
    
    // 4. 设置缓存清理(30分钟)
    setTimeout(() => {
      this.modelCache.delete(cacheKey);
      model.release();
    }, 30 * 60 * 1000);
    
    return model;
  }
  
  // 批量推理优化
  async batchPredict(
    modelPath: string,
    inputsBatch: ModelInput[][]
  ): Promise<ModelOutput[][]> {
    const model = await this.getModel(modelPath, 'NPU');
    const results: ModelOutput[][] = [];
    
    for (const inputs of inputsBatch) {
      const start = performance.now();
      const outputTensors = await model.predict(model.getInputs());
      const elapsed = performance.now() - start;
      
      const outputs = outputTensors.map(tensor => ({
        data: tensor.getData() as Float32Array,
        shape: tensor.getShape(),
        inferenceTime: elapsed
      }));
      
      results.push(outputs);
    }
    
    return results;
  }
}

5.4 模型转换流程

训练框架(PyTorch / TensorFlow / MindSpore)
    ↓
导出为通用格式(ONNX / SavedModel)
    ↓
MindSpore Lite 转换器(converter_lite)
    ↓
.ms 模型文件(端侧优化)
    ↓
部署到鸿蒙 App(assets 或下载)

转换命令示例:

# 转换 ONNX 模型到 .ms 格式
converter_lite \
  --modelFile=model.onnx \
  --outputFile=model \
  --fmk=ONNX \
  --optimize=ascend_oriented \
  --configFile=config.cfg

转换配置文件示例(config.cfg):

[ascend_context]
input_format=NCHW
input_shape=input:[1,3,224,224]
precision_mode=allow_fp32_to_fp16

六、鸿蒙 AI 开发的 5 条建议

六、鸿蒙 AI 开发的 5 条建议

建议 1:能用内置 API 就别自己写模型

鸿蒙提供的大量 AI API(语音识别、OCR、图像增强等)已经经过系统级优化,比自己部署模型更省电、更快、更省代码量

建议 2:优先端侧推理

HarmonyOS 7.0 的核心方向就是端侧 AI。只要 NPU 跑得动,就不要上云:

  • 端侧:无延迟、无网络依赖、隐私安全
  • 云侧:算力更强,但延迟和隐私成本高

建议 3:合理使用智能体框架

HMAF 是你在其他系统上找不到的能力:

  • 适合:电商、出行、工具、生活服务类 App
  • 不适合:游戏、播放器、图形设计

建议 4:注意 NPU 兼容性

并非所有鸿蒙设备都有 NPU:

设备类型 NPU 端侧 AI 支持
旗舰手机(Kirin) ✅ 有 完整
中端手机 ⚠️ 可能有 部分
平板 / 智慧屏 ✅ 有 完整
穿戴设备 ❌ 无 仅 CPU 推理
鸿蒙 PC ✅ 有 完整

建议 5:关注 7.0 的端侧自主决策

HarmonyOS 7.0 将带来:

  • 设备根据用户习惯自主决策
  • 端侧 AI 规模化普及
  • 跨终端 AI 算力协同

如果你的 App 需要在 7.0 发布时第一时间适配,建议从现在开始用 HMAF 框架注册智能体能力。


七、鸿蒙 AI 的差异化优势

鸿蒙 AI 独有能力 是什么 为什么重要
HMAF 智能体框架 系统级智能体调度 App 可被小艺直接调用
端侧 + 盘古云协同 同一模型灵活选择 简单任务本地、复杂任务上云
星盾 + 端侧推理 数据不出设备 隐私保护与 AI 兼得
16 种方言识别 覆盖主要方言区 对国内用户更友好
分布式 AI 协同 多设备共享算力 弱设备可借用强设备的 NPU

相比之下,iOS 的 Apple Intelligence 强在端侧隐私,Android 的 Gemini 强在多模态能力,而鸿蒙 AI 的独特优势是 “系统级智能体调度 + 分布式 AI 协同”


八、写在最后

对开发者而言,鸿蒙 AI 的接入门槛比看起来要低得多:

  • 基础 AI 功能:5 行代码就能集成语音识别或 OCR
  • 进阶 AI 能力:用 MindSpore Lite 部署自定义模型
  • 智能体接入:让你的 App 被小艺调度,获得更多曝光

在 AI 时代,最好的 AI 是用户感受不到 AI 存在的 AI。鸿蒙智能的设计哲学正是如此——AI 不是 App 里的一个功能按钮,而是贯穿整个系统的底层能力。


参考资源:

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐