文章目录
- [1 -> 概述](#1 -> 概述)
- [2 -> 端侧问答模型的技术优势](#2 -> 端侧问答模型的技术优势)
-
- [2.1 -> 零云端依赖的架构](#2.1 -> 零云端依赖的架构)
- [2.2 -> 响应速度与离线可用性](#2.2 -> 响应速度与离线可用性)
- [2.3 -> 与RAG能力的配合](#2.3 -> 与RAG能力的配合)
- [3 -> 关键API接口说明](#3 -> 关键API接口说明)
-
- [3.1 -> 模型资源与默认模型](#3.1 -> 模型资源与默认模型)
- [3.2 -> 约束条件](#3.2 -> 约束条件)
- [4 -> 完整的开发流程](#4 -> 完整的开发流程)
-
- [4.1 -> 权限声明](#4.1 -> 权限声明)
- [4.2 -> 初始化模型](#4.2 -> 初始化模型)
- [4.3 -> 流式与非流式问答](#4.3 -> 流式与非流式问答)
- [5 -> 完整示例代码:端侧问答助手](#5 -> 完整示例代码:端侧问答助手)
- [6 -> 最佳实践](#6 -> 最佳实践)
-
- [6.1 -> 模型下载与初始化时机](#6.1 -> 模型下载与初始化时机)
- [6.2 -> 上下文的长度控制](#6.2 -> 上下文的长度控制)
- [6.3 -> 错误处理](#6.3 -> 错误处理)
- [7 -> 应用场景展望](#7 -> 应用场景展望)
- [8 -> 结语](#8 -> 结语)

1 -> 概述
HarmonyOS 6.0作为鸿蒙生态演进的重要里程碑,在端侧AI能力上实现了关键突破。其中,Data Augmentation Kit新增的端侧问答模型(Local Chat Model)是一项重要能力------它让智能问答第一次真正意义上实现了"数据不出端"的本地化运行。
智能问答的核心机理在于RAG(检索增强生成)技术路线。当应用通过RAG接口进行知识问答时,系统会依次经过问题分解、查询改写、知识检索和检索生成等环节,整个过程需要与大语言模型(LLM)进行多次交互。传统的实现路径依赖云端大模型,开发者在面对这一需求时只有两种选择:要么自建云端模型服务器,承担高昂的运维成本和带宽开销;要么接入第三方云服务,必须面对敏感数据外传的安全性风险。
端侧问答模型的引入打破了这个两难局面。它在设备本地完成全部AI推理,完全没有云端环节,为开发者开辟了一条全新的技术路径。
2 -> 端侧问答模型的技术优势
2.1 -> 零云端依赖的架构
与自建云端大模型方案相比,端侧问答模型最显著的区别在于计算的全流程就地完成。用户输入的问题在终端设备完成向量化、检索、生成等全部计算任务,整个过程不产生任何向云端的网络请求。这意味着:
- 免除云端大模型的运维成本:不需要购买GPU实例、配置模型服务、处理高并发下的扩缩容,也不需要为每次API调用付费。
- 增强用户数据安全性:所有敏感数据都在端侧处理,没有数据外传路径,从物理层面杜绝了数据泄露的可能。这在金融、医疗、政务等强合规场景中具有决定性优势。
2.2 -> 响应速度与离线可用性
端侧推理的另一个核心优势是响应时间。网络延迟在移动环境下通常为几十到几百毫秒,加上云端模型的推理时间,端到端响应难以进入"无感知"区间。端侧模型完全本地运行,排除了网络这一不确定性因素,在硬件条件满足的前提下能够提供极为稳定的低延迟体验。
更重要的是离线可用性。在无网络覆盖的场景------飞机客舱、地下空间、偏远地区------云端方案完全失效,而端侧方案依然提供完整的智能问答服务。
2.3 -> 与RAG能力的配合
端侧问答模型与Data Augmentation Kit已有的知识检索(Knowledge Retrieval)和RAG能力形成了完整的端侧智能数据闭环。开发者可以构建纯本地的知识问答系统:私有的文档库在设备端完成向量化存储,用户提问时系统在本地完成检索,再由端侧模型生成答案。
3 -> 关键API接口说明
端侧问答模型的接口设计遵循HarmonyOS开发者熟悉的Promise/AsyncCallback双重异步模式,主要包含两个核心接口:
| 接口名 | 描述 |
|---|---|
init(): Promise<void> |
初始化端侧问答模型,负责拉起模型管理应用 |
chat(info: QuestionInfo, config: Config, callback: AsyncCallback): Promise<void> |
与端侧模型进行交互,实现端侧模型的问答功能 |
3.1 -> 模型资源与默认模型
模型资源来自Matrix模型库,chat接口默认调用的模型为Qwen25-7B-Instruct。这是一款参数规模达70亿的中文优化大语言模型,在端侧场景中实现了推理性能与生成质量的较好平衡。开发者可通过接口配置参数选择其他支持的模型版本。
3.2 -> 约束条件
需要特别说明的是,当前端侧模型问答仅支持PC和2in1设备类型,Phone和Tablet暂无法使用此能力。这一限制与端侧模型的算力需求有关------7B参数的模型需要足够的内存和NPU算力才能流畅运行,PC级设备的硬件配置更能满足这一要求。
同时,接口调用需要提交申请。根据官方说明,目前优先处理华为开放生态团队对接的企业方应用。申请时需要在在线提单页面填写应用名称、bundleName、AppID,并说明对PC/2in1设备的支持情况、当前鸿蒙化进展以及应用内容信息。
4 -> 完整的开发流程
4.1 -> 权限声明
端侧问答模型在问答过程中需要通过HTTP请求与LLM交互,因此必须在module.json5中配置网络权限:
json
{
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
4.2 -> 初始化模型
调用init接口拉起本地AI模型管理:
typescript
import { localChatModel } from '@kit.DataAugmentationKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 初始化端侧问答模型
localChatModel.init().then(() => {
console.info('模型初始化成功,模型管理已拉起');
}).catch((err: BusinessError) => {
console.error(`初始化失败: ${err.code} - ${err.message}`);
});
首次调用init时,系统会自动拉起本地AI模型管理应用,弹出隐私声明界面。用户同意后,系统开始下载默认模型。非首次使用时,用户可以手动通过"设置 > 系统 > 本地AI模型管理"路径下载或更新模型。
模型下载需要一定时间,下载完成后即可调用chat接口进行问答。
4.3 -> 流式与非流式问答
chat接口支持两种问答模式:流式和非流式。流式问答适合需要实时显示生成过程的场景(如AI助手打字机效果),非流式问答则适合一次性获取完整回答的批量处理场景。
typescript
import { localChatModel } from '@kit.DataAugmentationKit';
import { BusinessError } from '@kit.BasicServicesKit';
interface ChatMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
// 配置问答参数
const questionInfo: QuestionInfo = {
messages: [
{ role: 'system', content: '你是一个专业的HarmonyOS开发助手。' },
{ role: 'user', content: '如何初始化端侧问答模型?' }
]
};
const config: Config = {
stream: false, // 非流式模式
temperature: 0.7,
maxTokens: 2048
};
// 发起问答
localChatModel.chat(questionInfo, config, (err, data) => {
if (err) {
console.error(`问答失败: ${err.code} - ${err.message}`);
return;
}
console.info(`模型回答: ${data.choices[0].message.content}`);
});
流式问答配置方式类似,只需将config中的stream参数设为true,并通过回调接口逐块接收生成内容。
5 -> 完整示例代码:端侧问答助手
以下是一个完整的ArkUI页面实现,展示了一个具备流式/非流式切换、消息历史记录、滚动视图等功能的企业级端侧问答助手:
typescript
import { BusinessError } from '@kit.BasicServicesKit';
import { localChatModel } from '@kit.DataAugmentationKit';
type MessageRole = 'system' | 'user' | 'assistant';
interface ChatMessage {
role: MessageRole;
content: string;
}
@Entry
@Component
struct Index {
@State title: string = '端侧大模型问答助手';
@State isStreamMode: boolean = true;
@State messages: ChatMessage[] = [];
@State inputText: string = '';
@State initFlag: boolean = false;
@State isProcessing: boolean = false;
@State assistantContent: string = '';
@State chatCounter: number = 0;
private scroller: Scroller = new Scroller();
private conversationHistory: ChatMessage[] = [];
// 页面加载时初始化模型
onPageShow() {
console.info('modelChat onPageShow');
this.initModel();
}
private scrollToBottom() {
setTimeout(() => {
this.scroller.scrollEdge(Edge.Bottom);
}, 50);
}
private addMessage(role: MessageRole, content: string): void {
const newMessage: ChatMessage = { role: role, content: content };
this.messages = [...this.messages, newMessage];
this.scrollToBottom();
}
private async initModel(): Promise<void> {
try {
await localChatModel.init();
this.initFlag = true;
console.info('端侧模型初始化完成');
this.addMessage('assistant', '模型已就绪,请问有什么可以帮您?');
} catch (err) {
const error = err as BusinessError;
console.error(`模型初始化失败: ${error.code} - ${error.message}`);
this.addMessage('assistant', '模型初始化失败,请检查网络连接并重试。');
}
}
private async sendMessage(): Promise<void> {
if (!this.inputText.trim()) return;
if (!this.initFlag) {
this.addMessage('assistant', '模型未就绪,请稍后再试。');
return;
}
if (this.isProcessing) return;
const userMsg = this.inputText.trim();
this.addMessage('user', userMsg);
this.inputText = '';
this.isProcessing = true;
// 构建消息历史(保留最近10轮对话)
const historyMessages: ChatMessage[] = [
{ role: 'system', content: '你是一个专业的HarmonyOS开发助手。' }
];
const startIdx = Math.max(0, this.messages.length - 20);
for (let i = startIdx; i < this.messages.length; i++) {
historyMessages.push(this.messages[i]);
}
const questionInfo: QuestionInfo = { messages: historyMessages };
const config: Config = { stream: this.isStreamMode, temperature: 0.7 };
if (this.isStreamMode) {
let currentResponse = '';
try {
await localChatModel.chat(questionInfo, config, (err, data) => {
if (err) {
console.error(`流式问答错误: ${err.code} - ${err.message}`);
this.addMessage('assistant', '生成回答时出错,请重试。');
this.isProcessing = false;
return;
}
// 累积响应内容
if (data && data.choices && data.choices[0]?.delta?.content) {
currentResponse += data.choices[0].delta.content;
// 更新最后一条助手消息或创建新消息
if (this.messages.length > 0 && this.messages[this.messages.length - 1].role === 'assistant') {
const updatedMsg = { ...this.messages[this.messages.length - 1], content: currentResponse };
this.messages[this.messages.length - 1] = updatedMsg;
} else {
this.addMessage('assistant', currentResponse);
}
this.scrollToBottom();
}
});
} catch (error) {
console.error('流式问答异常:', JSON.stringify(error));
} finally {
this.isProcessing = false;
}
} else {
try {
const response = await localChatModel.chat(questionInfo, config);
const assistantReply = response.choices[0]?.message?.content || '未获取到有效回答';
this.addMessage('assistant', assistantReply);
} catch (err) {
const error = err as BusinessError;
console.error(`非流式问答错误: ${error.code} - ${error.message}`);
this.addMessage('assistant', '生成回答时出错,请重试。');
} finally {
this.isProcessing = false;
}
}
}
build() {
Column() {
// 标题栏
Row() {
Text(this.title)
.fontSize(20)
.fontWeight(FontWeight.Medium);
Blank();
Toggle({ type: ToggleType.Switch, isOn: this.isStreamMode })
.onChange((isOn: boolean) => {
this.isStreamMode = isOn;
})
Text(this.isStreamMode ? '流式' : '非流式')
.fontSize(14)
.margin({ left: 8 });
}
.width('100%')
.padding(16)
.backgroundColor('#F5F5F5');
// 消息列表
Scroll(this.scroller) {
Column() {
ForEach(this.messages, (message: ChatMessage, index: number) => {
this.MessageItem(message)
}, (item: ChatMessage, index: number) => index.toString())
}
.width('100%')
.padding(12)
}
.layoutWeight(1)
.scrollBar(BarState.Auto)
.scrollSnap(ScrollSnap.ALIGN_START)
.edgeEffect(EdgeEffect.Spring);
// 输入区域
Row() {
TextInput({ placeholder: '请输入问题...', text: this.inputText })
.layoutWeight(1)
.onChange((value: string) => {
this.inputText = value;
})
.onSubmit(() => {
this.sendMessage();
});
Button('发送')
.margin({ left: 12 })
.onClick(() => {
this.sendMessage();
})
.enabled(!this.isProcessing)
}
.width('100%')
.padding(12)
.backgroundColor('#F5F5F5')
}
.width('100%')
.height('100%')
}
@Builder
MessageItem(message: ChatMessage) {
Row() {
if (message.role === 'user') {
Blank();
Text(message.content)
.fontSize(16)
.padding(12)
.backgroundColor('#007AFF')
.fontColor(Color.White)
.borderRadius(12)
.maxLines(100)
.textOverflow({ overflow: TextOverflow.None })
} else {
Text(message.content)
.fontSize(16)
.padding(12)
.backgroundColor('#E9E9EF')
.fontColor(Color.Black)
.borderRadius(12)
.maxLines(100)
.textOverflow({ overflow: TextOverflow.None });
Blank();
}
}
.width('100%')
.margin({ bottom: 12 })
.constraintSize({ maxWidth: '80%' })
}
}
该示例实现了以下核心功能:
- 初始化管理 :页面加载时自动调用
init接口拉起模型管理应用,通过initFlag状态追踪初始化完成状态。 - 对话历史管理:保留最近20条消息作为上下文,超过限制时自动截断以保证模型输入长度不超限。
- 流式/非流式切换:通过Toggle开关实时切换问答模式,流式模式下支持打字机式逐字输出。
- 状态处理 :通过
isProcessing标识防止重复请求,完整的错误处理机制覆盖各类异常场景。
6 -> 最佳实践
6.1 -> 模型下载与初始化时机
端侧模型文件体积较大(7B参数模型通常需要数GB存储空间),建议在应用首次启动时预判用户可能使用问答功能,尽早调用init接口触发模型下载。官方文档指出:首次调用init拉起模型应用后弹出隐私声明,同意后开始下载默认模型;若用户拒绝,后续调用仍会继续拉起应用提示同意,建议设计友好的引导流程。模型下载可能需要较长时间,可以在下载期间显示进度指示器。
6.2 -> 上下文的长度控制
代码示例中保留了最近10轮对话作为上下文传递给模型。实际开发中,需要根据模型的最大输入长度限制(通常为4096或8192个token)来调整保留的轮数。对于长文档问答场景,优先使用RAG接口进行知识检索,再将对应用户问题和检索结果作为上下文发送给端侧模型,避免直接将文档全文输入。
6.3 -> 错误处理
由于网络是问答过程中的必要环节(模型资源下载、矩阵模型库连接等全部需要联网),应设计完善的错误处理机制,包括连接超时重试、模型下载中断后续传以及清晰的用户提示。
7 -> 应用场景展望
端侧问答模型的能力可以与Data Augmentation Kit中的知识检索、RAG接口形成组合,构建多种端侧智能应用。
在企业私有知识库 场景中,员工手册、内部规章制度等敏感文档可以完全存储在本地进行向量化管理,员工无需将文档内容上传云端即可获得智能化的问答体验。医疗健康 场景中,用户的个人体征数据、医疗记录等高隐私信息保留在设备端,端侧模型根据这些信息提供个性化的健康建议,整个过程数据不出设备。车载智能座舱场景下,即便在信号薄弱的隧道、地下车库等区域,系统仍能提供完整的语音交互和问答服务。
8 -> 结语
鸿蒙6.0 Data Augmentation Kit端侧问答模型的推出,重新定义了终端智能的能力边界。它让开发者摆脱云端大模型的高昂成本和数据安全顾虑,在终端设备上实现真正的本地智能问答。随着未来对更多设备类型的支持和模型能力的持续迭代,端侧智能将迎来更广阔的应用前景。对于正在规划智能应用的企业和开发者而言,现在正是评估、尝试这一新能力的最佳时机。
感谢各位大佬支持!!!
互三啦!!!