系列智能化篇·第47篇。全场景篇后,有产品经理问:“现在的电商Demo功能齐全了,但感觉还是‘死’的。能不能接入AI,让它像真人客服一样聊天,或者自动生成商品文案?” 这正是生成式AI(GenAI)带来的变革。今天我们将电商Demo升级为“AI电商助手”,深度集成华为盘古大模型MindSpore Lite端侧推理,实现智能客服对话、商品图文生成、个性化推荐、图像识别搜索。我们将解决大模型体积大、推理慢、隐私保护三大落地难题。全程基于API23,含官方文档未涉及的“端云协同推理架构”和“Prompt工程优化”技巧。

一、前言:为什么端侧AI是鸿蒙的“王炸”?

云端大模型(如ChatGPT)虽强,但存在延迟高、隐私泄露、断网不可用的问题。鸿蒙的端云协同AI架构给出了完美答案:

  • 端侧:负责实时性要求高、隐私敏感的任务(如键盘输入预测、本地相册识别),使用轻量化模型(MindSpore Lite)。

  • 云侧:负责复杂推理、知识问答(如盘古大模型),通过安全隧道传输数据。

  • 协同:端侧预处理,云侧深度推理,端侧后处理,兼顾效果与效率。

今天,我们将把AI能力植入电商Demo的每一个毛孔,让它从“工具”进化为“助手”。

二、核心概念辨析(传统算法 vs 大模型)

维度

传统算法 (ML/DL)

大语言模型 (LLM)

能力

单一任务(分类、回归)

通用任务(理解、生成、推理)

数据

需要大量标注数据训练

预训练+Few-shot学习,少量样本即可

交互

输入特征,输出结果

自然语言对话,多轮交互

泛化

泛化能力弱,换场景需重训

泛化能力强,适应新场景

资源

轻量,适合端侧

庞大,通常需云侧支持

三、代码实现:从“功能”到“智能”

3.1 集成盘古大模型:智能客服

使用AGC的GenAI Kit调用盘古大模型,实现电商场景的智能客服。

创建entry/src/main/ets/ai/PanguChatService.ets



import { genAi } from '@hw-agconnect/genai'
import { BusinessError } from '@kit.BasicServicesKit'

export interface ChatMessage {
  role: 'user' | 'assistant' | 'system'
  content: string
}

export class PanguChatService {
  private chatSession: genAi.ChatSession | null = null
  private systemPrompt: string = `
    你是一个专业的电商智能助手,名为“鸿蒙购物助手”。
    你的任务是帮助用户查找商品、解答售后问题、提供购物建议。
    请遵循以下规则:
    1. 回答简洁明了,不超过100字。
    2. 如果不知道答案,请诚实告知,不要编造。
    3. 优先推荐平台内的商品。
    4. 语气友好,使用表情符号增加亲和力。
    5. 当前平台主打电子产品,特别是华为鸿蒙生态产品。
  `

  /**
   * 初始化聊天会话
   */
  async init(): Promise<void> {
    try {
      // 1. 配置大模型参数
      const config: genAi.SessionConfig = {
        modelName: 'pangu-nlp-3.0', // 盘古NLP大模型
        temperature: 0.7, // 控制随机性,越低越确定
        maxTokens: 200, // 最大输出长度
        topP: 0.9,
        systemPrompt: this.systemPrompt
      }

      // 2. 创建聊天会话
      this.chatSession = await genAi.createChatSession(config)
      console.log('盘古大模型会话初始化成功')
    } catch (err) {
      console.error('初始化失败:', err)
    }
  }

  /**
   * 发送消息并获取回复
   */
  async sendMessage(userInput: string): Promise<string> {
    if (!this.chatSession) {
      await this.init()
    }

    try {
      // 1. 构建消息历史(实际应用中需维护完整的对话历史)
      const messages: ChatMessage[] = [
        { role: 'user', content: userInput }
      ]

      // 2. 调用大模型
      const response = await this.chatSession.sendMessage(messages)
      
      // 3. 提取回复内容
      const reply = response.getContent()
      console.log('AI回复:', reply)
      return reply
    } catch (err) {
      console.error('发送消息失败:', err)
      return '抱歉,AI助手暂时无法响应,请稍后再试。😥'
    }
  }

  /**
   * 流式对话(打字机效果)
   */
  async sendMessageStream(
    userInput: string,
    onChunk: (chunk: string) => void,
    onComplete: () => void
  ): Promise<void> {
    if (!this.chatSession) {
      await this.init()
    }

    try {
      const messages: ChatMessage[] = [
        { role: 'user', content: userInput }
      ]

      // 使用流式接口
      const stream = this.chatSession.sendMessageStream(messages)
      
      stream.on('data', (chunk: genAi.StreamChunk) => {
        const text = chunk.getText()
        onChunk(text)
      })

      stream.on('end', () => {
        console.log('流式对话结束')
        onComplete()
      })

      stream.on('error', (err: BusinessError) => {
        console.error('流式对话错误:', err)
        onChunk('抱歉,对话出错。')
        onComplete()
      })
    } catch (err) {
      console.error('启动流式对话失败:', err)
      onChunk('抱歉,AI助手暂时无法响应。')
      onComplete()
    }
  }

  /**
   * 清理会话资源
   */
  destroy(): void {
    if (this.chatSession) {
      this.chatSession.destroy()
      this.chatSession = null
    }
  }
}

3.2 端侧图像识别:拍照搜商品

使用MindSpore Lite在端侧运行轻量化图像识别模型,实现拍照搜商品。

创建entry/src/main/ets/ai/ImageSearchEngine.ets



import { mindSporeLite } from '@kit.MindSporeLiteKit'
import { image } from '@kit.ImageKit'
import { fileIo } from '@kit.CoreFileKit'

export class ImageSearchEngine {
  private model: mindSporeLite.Model | null = null
  private labels: string[] = []

  /**
   * 加载模型和标签
   */
  async loadModel(): Promise<void> {
    try {
      // 1. 加载模型文件(.ms模型)
      const modelPath = `${getContext().filesDir}/mobilenet_v2.ms`
      // 注:实际项目中需将模型文件放入rawfile目录,并复制到沙箱
      await this.copyModelToSandbox()
      
      // 2. 创建MSContext
      const context = new mindSporeLite.Context()
      context.target = ['cpu'] // 使用CPU推理,也可配置GPU/NPU
      context.cpuThreadPoolNum = 4 // 线程数
      context.cpuPowerMode = mindSporeLite.CpuPowerMode.BIG_CORES_FIRST

      // 3. 加载模型
      this.model = await mindSporeLite.Model.createFromFile(modelPath, context)
      
      // 4. 加载标签文件
      const labelPath = `${getContext().filesDir}/labels.txt`
      const labelContent = fileIo.readTextSync(labelPath)
      this.labels = labelContent.split('\n').map(line => line.trim())
      
      console.log('图像识别模型加载成功,标签数:', this.labels.length)
    } catch (err) {
      console.error('模型加载失败:', err)
    }
  }

  /**
   * 复制模型文件到沙箱
   */
  private async copyModelToSandbox(): Promise<void> {
    const resourceManager = getContext().resourceManager
    const modelData = await resourceManager.getRawFileContent('mobilenet_v2.ms')
    const labelData = await resourceManager.getRawFileContent('labels.txt')
    
    const modelPath = `${getContext().filesDir}/mobilenet_v2.ms`
    const labelPath = `${getContext().filesDir}/labels.txt`
    
    fileIo.writeSync(fileIo.openSync(modelPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY), modelData)
    fileIo.writeSync(fileIo.openSync(labelPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY), labelData)
  }

  /**
   * 识别图片并返回最可能的商品类别
   */
  async recognizeImage(imagePixelMap: image.PixelMap): Promise<{ label: string, confidence: number }[]> {
    if (!this.model) {
      await this.loadModel()
    }

    try {
      // 1. 图像预处理
      const tensor = await this.preprocessImage(imagePixelMap)
      
      // 2. 创建输入张量
      const inputs: mindSporeLite.Tensor[] = [tensor]
      
      // 3. 执行推理
      const outputs = await this.model!.predict(inputs)
      
      // 4. 后处理,解析结果
      const results = this.postprocessOutputs(outputs)
      
      return results.slice(0, 5) // 返回前5个最可能的结果
    } catch (err) {
      console.error('图像识别失败:', err)
      return []
    }
  }

  /**
   * 图像预处理:缩放、归一化、格式转换
   */
  private async preprocessImage(pixelMap: image.PixelMap): Promise<mindSporeLite.Tensor> {
    // 1. 缩放到模型输入尺寸 (224x224)
    const scaleOptions: image.ScalingOptions = {
      size: { height: 224, width: 224 }
    }
    const scaledPixelMap = await image.createPixelMap(pixelMap, scaleOptions)
    
    // 2. 读取像素数据
    const pixelBytes = await scaledPixelMap.readPixelsToBuffer()
    
    // 3. 转换为Tensor (NHWC格式,float32)
    const tensor = mindSporeLite.Tensor.create([1, 224, 224, 3], mindSporeLite.DataType.FLOAT32)
    
    // 4. 归一化处理 (像素值 / 255.0)
    const floatData = new Float32Array(1 * 224 * 224 * 3)
    for (let i = 0; i < pixelBytes.length; i += 4) {
      const r = pixelBytes[i] / 255.0
      const g = pixelBytes[i + 1] / 255.0
      const b = pixelBytes[i + 2] / 255.0
      // 跳过alpha通道
      floatData[(i / 4) * 3] = r
      floatData[(i / 4) * 3 + 1] = g
      floatData[(i / 4) * 3 + 2] = b
    }
    
    tensor.setData(floatData.buffer)
    return tensor
  }

  /**
   * 后处理:解析输出张量,匹配标签
   */
  private postprocessOutputs(outputs: mindSporeLite.Tensor[]): { label: string, confidence: number }[] {
    const outputTensor = outputs[0]
    const data = new Float32Array(outputTensor.getData())
    
    // Softmax计算概率
    const expData = data.map(x => Math.exp(x))
    const sumExp = expData.reduce((a, b) => a + b, 0)
    const probabilities = expData.map(x => x / sumExp)
    
    // 创建索引-概率对并排序
    const results = probabilities
      .map((prob, index) => ({
        label: this.labels[index] || `未知类别_${index}`,
        confidence: prob
      }))
      .sort((a, b) => b.confidence - a.confidence)
    
    return results
  }
}

3.3 AI生成商品文案

使用大模型的生成能力,为商品自动生成营销文案。



export class ProductCopywriter {
  private panguService: PanguChatService

  constructor() {
    this.panguService = new PanguChatService()
    this.panguService.init()
  }

  /**
   * 生成商品标题
   */
  async generateTitle(productName: string, features: string[]): Promise<string> {
    const prompt = `
      请为以下商品生成一个吸引人的电商标题,要求:
      1. 包含核心关键词:${productName}
      2. 突出卖点:${features.join('、')}
      3. 长度控制在20字以内
      4. 使用感叹号或emoji增加吸引力
      
      示例:华为FreeBuds Pro 3 → 🎧华为FreeBuds Pro 3!静谧通话,无损音质!
    `
    return await this.panguService.sendMessage(prompt)
  }

  /**
   * 生成商品描述
   */
  async generateDescription(productName: string, specs: Record<string, string>): Promise<string> {
    const specStr = Object.entries(specs).map(([k, v]) => `${k}: ${v}`).join(';')
    const prompt = `
      请为商品${productName}撰写一段详细的电商描述,规格参数:${specStr}。
      要求:
      1. 分段落,每段一个小标题。
      2. 语言生动,突出用户体验。
      3. 包含使用场景建议。
      4. 字数在200字左右。
    `
    return await this.panguService.sendMessage(prompt)
  }

  /**
   * 生成社交媒体短文案
   */
  async generateSocialMediaPost(productName: string, platform: 'weibo' | 'xiaohongshu'): Promise<string> {
    const prompt = `
      请为商品${productName}生成一条适合${platform}发布的文案。
      ${platform === 'xiaohongshu' ? '要求:包含emoji,使用小红书风格的语言,添加相关话题标签。' : ''}
      ${platform === 'weibo' ? '要求:简洁有力,包含热门话题,适合转发。' : ''}
    `
    return await this.panguService.sendMessage(prompt)
  }
}

3.4 端云协同架构

设计一个高效的端云协同推理流程。



export class AICoordinator {
  private cloudService: PanguChatService
  private edgeEngine: ImageSearchEngine
  private cache: Map<string, any> = new Map()

  constructor() {
    this.cloudService = new PanguChatService()
    this.edgeEngine = new ImageSearchEngine()
  }

  /**
   * 端云协同推理:智能问答
   */
  async hybridChat(userQuery: string): Promise<string> {
    // 1. 端侧意图识别(快速过滤)
    const intent = await this.classifyIntent(userQuery)
    
    if (intent === 'image_search') {
      // 2. 如果是图像搜索,直接在端侧处理
      return '请拍摄商品照片,我将为您搜索相似商品。'
    } else if (intent === 'simple_faq') {
      // 3. 简单FAQ,使用端侧小模型
      return await this.answerWithEdgeModel(userQuery)
    } else {
      // 4. 复杂问题,调用云端大模型
      return await this.cloudService.sendMessage(userQuery)
    }
  }

  /**
   * 端侧意图分类(简化版)
   */
  private async classifyIntent(query: string): Promise<string> {
    const keywords = {
      image_search: ['拍照', '图片', '识别', '这是什么'],
      simple_faq: ['多少钱', '在哪买', '营业时间', '退货政策']
    }
    
    for (const [intent, words] of Object.entries(keywords)) {
      if (words.some(word => query.includes(word))) {
        return intent
      }
    }
    return 'complex_query'
  }

  /**
   * 使用端侧小模型回答问题(模拟)
   */
  private async answerWithEdgeModel(query: string): Promise<string> {
    const faq: Record<string, string> = {
      '多少钱': '商品价格请查看详情页,不同配置价格不同哦~',
      '在哪买': '您可以在华为商城、京东、天猫官方旗舰店购买。',
      '退货政策': '支持7天无理由退货,1年质保服务。'
    }
    
    for (const [question, answer] of Object.entries(faq)) {
      if (query.includes(question)) {
        return answer
      }
    }
    return '这个问题有点难,让我问问云端专家...'
  }
}

四、踩坑记录(官方文档没写的AI落地细节)

  1. 模型转换的“暗坑”:MindSpore Lite支持的模型格式是.ms,通常需要将PyTorch/TensorFlow模型通过converter_lite工具转换。转换时要注意算子支持度,有些复杂算子可能不支持,需要替换或修改模型结构。

  2. 端侧推理的内存爆炸:在手机端加载大模型(即使是轻量版)也会占用大量内存。如果同时加载多个模型(如OCR+图像识别+NLP),极易触发OOM。解决方案:使用懒加载,按需加载模型;及时调用model.destroy()释放资源;使用NAPI将推理放在Native层,减少JS堆压力。

  3. Prompt工程的“玄学”:大模型的输出质量极度依赖Prompt的设计。同样的请求,不同的Prompt模板,效果天差地别。经验:给AI设定明确的角色(Role)任务(Task)约束(Constraints)示例(Examples)。比如“你是一个专业的电商文案专家,请用小红书风格写一段文案,要求包含emoji,不超过100字”。

  4. 流式输出的UI卡顿:使用流式对话(打字机效果)时,如果每次收到一个字符就更新UI,高频刷新会导致UI线程阻塞。解决方案:使用节流(Throttle),每积累一定长度的文本(如10个字符)或每隔一定时间(如50ms)更新一次UI;或者使用TaskPool将字符串拼接放在后台线程。

  5. 隐私合规红线:AI涉及大量用户数据(聊天记录、图片)。必须明确告知用户数据用途,并提供撤回同意数据删除的入口。对于端侧AI,强调“数据不出端”;对于云侧AI,使用匿名化脱敏技术,并遵守GDPR等法规。

更多推荐