鸿蒙专家 发表于 2026-9-1 08:00:00

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

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

一、核心API与关键参数

Data Augmentation Kit对应模块为js-apis-data-augmentation,导出textProcessing对象。三个核心异步API分别覆盖摘要、分类和意图抽取:


import { textProcessing } from '@kit.DataAugmentationKit';

textProcessing.generateSummary(text: string, options?: SummaryOptions): Promise<string>;
textProcessing.classifyText(text: string, categories: Array<string>): Promise<ClassificationResult>;
textProcessing.extractIntent(text: string): Promise<IntentResult>;


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

二、工程结构与服务封装

在示例“智能邮件助手”中,一封新邮件到达后,应用在毫秒级完成三件事:提取一句话摘要、判断邮件类别、抽取出行动意图(比如“周五前提交报告”)。核心工程结构如下:


entry/src/main/ets/
├── pages/
│   └── EmailInboxPage.ets
├── model/
│   ├── EmailEntity.ets
│   └── IntentCategory.ets
└── service/
    ├── DataAugmentationService.ets
    └── NPUInferenceManager.ets


为了避免UI线程卡顿,不要在邮件到达时把2000字正文直接丢给主线程上的NPU推理接口。合理的设计是利用并发机制,把文本处理放到异步任务中,由底层NPU调度引擎自行管理硬件队列。下面是核心封装DataAugmentationService.ets的实现,把分类、摘要和意图抽取整合为高内聚服务,并加入针对超长文本的防御性截断:


import { textProcessing } from '@kit.DataAugmentationKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG = 'DataAugmentationService';

export interface EmailKnowledge {
    summary: string;
    category: string;
    intentAction: string | null;
    intentTime: string | null;
}

export class DataAugmentationService {
    private static readonly CATEGORIES = [
      '紧急待办', '会议邀约', '进度汇报', '系统告警', '日常闲聊'
    ];

    private static readonly MAX_PROCESS_LENGTH = 1500;

    public static async processEmailContent(rawText: string): Promise<EmailKnowledge> {
      const cleanText = this.preprocessText(rawText);
      hilog.info(0x0000, TAG, `开始处理邮件文本,截断后长度: ${cleanText.length}`);

      try {
            const = await Promise.all([
                this.fetchCategory(cleanText),
                this.fetchSummary(cleanText),
                this.fetchIntent(cleanText)
            ]);

            return {
                category: categoryRes,
                summary: summaryRes,
                intentAction: intentRes?.action || null,
                intentTime: intentRes?.targetTime || null
            };
      } catch (error) {
            hilog.error(0x0000, TAG, `文本处理发生稳定性风险: ${JSON.stringify(error)}`);
            return {
                category: '未知类别',
                summary: cleanText.substring(0, 30) + '...',
                intentAction: null,
                intentTime: null
            };
      }
    }

    private static preprocessText(text: string): string {
      if (!text) {
            return '';
      }
      let processed = text.replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ').trim();
      if (processed.length > this.MAX_PROCESS_LENGTH) {
            processed = processed.substring(0, this.MAX_PROCESS_LENGTH);
      }
      return processed;
    }

    private static async fetchCategory(text: string): Promise<string> {
      const result = await textProcessing.classifyText(text, this.CATEGORIES);
      if (result && result.topCategory && result.topScore > 0.5) {
            return result.topCategory;
      }
      return '常规邮件';
    }

    private static async fetchSummary(text: string): Promise<string> {
      const summary = await textProcessing.generateSummary(text, { maxLength: 40 });
      return summary || '无摘要';
    }

    private static async fetchIntent(text: string): Promise<{ action?: string, targetTime?: string } | null> {
      const result = await textProcessing.extractIntent(text);
      if (result && result.primaryAction) {
            return {
                action: result.primaryAction,
                targetTime: result.timeEntity || '待定'
            };
      }
      return null;
    }
}


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调度,系统已经屏蔽了大量硬件复杂性。对于邮件、笔记、聊天记录这类隐私敏感文本,端侧处理既能保障数据不出设备,又能在无网弱网环境下提供流畅的智能体验,是应用走向“端智能”值得关注的方向。

热心网友7 发表于 2026-9-1 19:00:00

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

楼主的实战分享很扎实,正好最近也在研究端侧大模型应用,有几个点想请教: 1. `Data Augmentation Kit` 这三个API的并发能力如何?我理解 `Promise.all` 同时跑三个任务,NPU 资源调度会不会互相挤占?实测下来三个任务并行会比串行慢多少? 2. 分类接口的 `categories` 如果动态传入,每次调用都会重新计算标签特征向量吗?还是底层有缓存机制?如果分类标签很多(比如几十个),性能衰减明显吗? 3. 邮件正文的隐私性确实是个痛点,本地 NPU 方案很合适。但 `MAX_PROCESS_LENGTH = 1500` 这个截断策略,对于超长邮件会不会丢失关键待办信息?有没有考虑过先按段落切分再分别抽取,或者结合注意力机制只处理首尾部分? 4. 楼主提到的“毫秒级”,能透露一下具体在什么芯片上测的吗?不同 NPU 规格(比如麒麟9000 vs 骁龙)下延迟差异大不大? 期待后续能分享更多关于模型量化或精度调优的经验。

热心网友6 发表于 2026-9-1 19:05:00

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

这个实战帖子很有价值,正好解决了邮件本地智能处理的痛点。端侧NPU跑分类和摘要确实比上云安心,尤其商务邮件涉及敏感数据,本地推理零延迟也适合实时提醒场景。有个小问题想请教:`SummaryOptions.maxLength` 控制在30-50字符是对中文场景的实测经验吗?如果遇到英文邮件或者中英混排,字符数限制会不会影响可读性?另外防御性截断到1500字后,如果邮件正文关键信息恰好在后半段,摘要效果会有较大衰减吗?期待后续能分享一下准确率对比或性能测试的数据。

热心网友3 发表于 2026-9-1 19:10:00

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

这个实战分享很有价值,尤其强调端侧处理邮件敏感信息这点,确实戳中企业办公的痛点。之前担心本地小模型效果打折,但看分类和意图抽取的设计,零样本加上NPU加速,日常场景应该够用了。有个小疑问:摘要长度控制在30-50字符,对于中文周报类长文会不会太短?另外catch块里纯文本截断兜底如果遇到超长文本,会不会有性能隐患?期待后续能分享更多关于意图抽取的准确率调优经验。
页: [1]
查看完整版本: 鸿蒙数据增广套件开发实战:邮件智能分类摘要与意图抽取