查看: 88|回复: 0

鸿蒙数据增广套件开发实战:邮件智能分类摘要与意图抽取

[复制链接]
发表于 22 分钟前 | 显示全部楼层 |阅读模式
现代职场邮件中,长篇周报、系统告警、会议邀约和客户催办混杂在一起,人工筛选效率低且容易漏掉关键待办。过去常见的方案是把邮件正文上传到云端大模型解析,但邮件往往涉及商业机密,直接上云存在数据出境和隐私稳定性风险。HarmonyOS 7.0(API 26)开放的Data Augmentation Kit,利用设备端NPU和本地小模型,提供文本特征提取、智能分类、长文本摘要、待办意图推理等端侧能力,让邮件可以在本地零延迟处理,不把敏感内容送出厂。

一、核心API与关键参数

Data Augmentation Kit对应模块为js-apis-data-augmentation,导出textProcessing对象。三个核心异步API分别覆盖摘要、分类和意图抽取:
  1. import { textProcessing } from '@kit.DataAugmentationKit';
  2. textProcessing.generateSummary(text: string, options?: SummaryOptions): Promise<string>;
  3. textProcessing.classifyText(text: string, categories: Array<string>): Promise<ClassificationResult>;
  4. textProcessing.extractIntent(text: string): Promise<IntentResult>;
复制代码

摘要接口通过SummaryOptions.maxLength控制输出长度,但NPU算力有限,maxLength设置过大会让耗时指数级上升。实测把目标摘要长度控制在30到50字符,能在阅读体验和推理耗时之间取得最佳平衡。分类接口的categories参数是实现零样本分类的关键,不需要在端侧微调模型,只需要传入“会议邀约”“报销审批”“系统通知”这类描述性标签,底层会自动计算文本特征向量与标签特征向量的余弦相似度,从而判断文本所属类别。

二、工程结构与服务封装

在示例“智能邮件助手”中,一封新邮件到达后,应用在毫秒级完成三件事:提取一句话摘要、判断邮件类别、抽取出行动意图(比如“周五前提交报告”)。核心工程结构如下:
  1. entry/src/main/ets/
  2. ├── pages/
  3. │   └── EmailInboxPage.ets
  4. ├── model/
  5. │   ├── EmailEntity.ets
  6. │   └── IntentCategory.ets
  7. └── service/
  8.     ├── DataAugmentationService.ets
  9.     └── NPUInferenceManager.ets
复制代码

为了避免UI线程卡顿,不要在邮件到达时把2000字正文直接丢给主线程上的NPU推理接口。合理的设计是利用并发机制,把文本处理放到异步任务中,由底层NPU调度引擎自行管理硬件队列。下面是核心封装DataAugmentationService.ets的实现,把分类、摘要和意图抽取整合为高内聚服务,并加入针对超长文本的防御性截断:
  1. import { textProcessing } from '@kit.DataAugmentationKit';
  2. import { hilog } from '@kit.PerformanceAnalysisKit';
  3. const TAG = 'DataAugmentationService';
  4. export interface EmailKnowledge {
  5.     summary: string;
  6.     category: string;
  7.     intentAction: string | null;
  8.     intentTime: string | null;
  9. }
  10. export class DataAugmentationService {
  11.     private static readonly CATEGORIES = [
  12.         '紧急待办', '会议邀约', '进度汇报', '系统告警', '日常闲聊'
  13.     ];
  14.     private static readonly MAX_PROCESS_LENGTH = 1500;
  15.     public static async processEmailContent(rawText: string): Promise<EmailKnowledge> {
  16.         const cleanText = this.preprocessText(rawText);
  17.         hilog.info(0x0000, TAG, `开始处理邮件文本,截断后长度: ${cleanText.length}`);
  18.         try {
  19.             const [categoryRes, summaryRes, intentRes] = await Promise.all([
  20.                 this.fetchCategory(cleanText),
  21.                 this.fetchSummary(cleanText),
  22.                 this.fetchIntent(cleanText)
  23.             ]);
  24.             return {
  25.                 category: categoryRes,
  26.                 summary: summaryRes,
  27.                 intentAction: intentRes?.action || null,
  28.                 intentTime: intentRes?.targetTime || null
  29.             };
  30.         } catch (error) {
  31.             hilog.error(0x0000, TAG, `文本处理发生稳定性风险: ${JSON.stringify(error)}`);
  32.             return {
  33.                 category: '未知类别',
  34.                 summary: cleanText.substring(0, 30) + '...',
  35.                 intentAction: null,
  36.                 intentTime: null
  37.             };
  38.         }
  39.     }
  40.     private static preprocessText(text: string): string {
  41.         if (!text) {
  42.             return '';
  43.         }
  44.         let processed = text.replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ').trim();
  45.         if (processed.length > this.MAX_PROCESS_LENGTH) {
  46.             processed = processed.substring(0, this.MAX_PROCESS_LENGTH);
  47.         }
  48.         return processed;
  49.     }
  50.     private static async fetchCategory(text: string): Promise<string> {
  51.         const result = await textProcessing.classifyText(text, this.CATEGORIES);
  52.         if (result && result.topCategory && result.topScore > 0.5) {
  53.             return result.topCategory;
  54.         }
  55.         return '常规邮件';
  56.     }
  57.     private static async fetchSummary(text: string): Promise<string> {
  58.         const summary = await textProcessing.generateSummary(text, { maxLength: 40 });
  59.         return summary || '无摘要';
  60.     }
  61.     private static async fetchIntent(text: string): Promise<{ action?: string, targetTime?: string } | null> {
  62.         const result = await textProcessing.extractIntent(text);
  63.         if (result && result.primaryAction) {
  64.             return {
  65.                 action: result.primaryAction,
  66.                 targetTime: result.timeEntity || '待定'
  67.             };
  68.         }
  69.         return null;
  70.     }
  71. }
复制代码

UI层只需在收到邮件数据时调用processEmailContent,再把返回的EmailKnowledge映射到组件状态变量。数据计算与页面渲染彻底解耦,代码可维护性也更高。

三、避坑指南

1. 并行调用OOM

代码中通过Promise.all同时发起分类、摘要和意图抽取,在旗舰机上NPU资源充裕时调度器能完美并行;但在算力受限的老旧机型上,同时加载三个不同任务的权重可能触碰显存上限。如果强依赖低端机适配,建议改成串行await调用:先获取分类,如果是垃圾邮件则跳过后续的摘要与意图抽取,节省算力开销。

2. 零样本分类的标签设计

classifyText的categories参数需要传递语义明确的自然语言,不要传“TYPE_01”“TYPE_02”这类枚举代号。模型不认识代码符号,标签名应该像写Prompt一样,使用“需要回复的紧急工作”“无关紧要的通知”这种描述性强的词汇,分类准确率会明显提升。

3. 输入文本的“垃圾进,垃圾出”

邮件正文常包含Base64编码的内联图片、HTML标签、杂乱签名档。如果不对原始文本做preprocessText清洗,直接扔给extractIntent,特征提取引擎会被无意义字符淹没,拖慢提取速度,还容易产生“幻觉”或提取失败。因此必须先做换行折叠、空白压缩、长度截断等预处理。

四、总结

接入HarmonyOS 7.0的Data Augmentation Kit后,传统依赖云端的高成本NLP任务可以转化为端侧闭环的本地原生能力。从API设计到底层NPU调度,系统已经屏蔽了大量硬件复杂性。对于邮件、笔记、聊天记录这类隐私敏感文本,端侧处理既能保障数据不出设备,又能在无网弱网环境下提供流畅的智能体验,是应用走向“端智能”值得关注的方向。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-1 08:22 , Processed in 0.022323 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部