【共创季稿事节】鸿蒙 NEXT 原生应用开发实战:AI 修仙模拟器
AI 修仙模拟器:鸿蒙 NEXT 原生应用开发实战



一、项目背景
HarmonyOS NEXT 作为华为自主研发的操作系统,已经进入了全面商用阶段。它去除了 AOSP 兼容层,采用纯鸿蒙内核,为开发者带来了全新的技术栈和开发范式。本文将基于一个真实的鸿蒙原生开源项目 app622,带领读者深入剖析如何在鸿蒙 NEXT 平台上构建一款融合 AI 大语言模型与修仙文化主题的创新应用。
app622 项目包含两个核心模块:AI 修仙模拟器(基于大模型 API 的智能问答系统,以修仙文化为背景,提供 8 大修炼方向的指导)和 拖拽排序组件(使用 Stack 层叠布局实现卡片拖拽重排的视觉反馈效果)。项目已升级至 API 24(HarmonyOS 7.0.0),紧跟鸿蒙生态的最新演进。
本文将从架构设计、核心实现、代码解析、工程实践四个维度,全面展示鸿蒙原生应用的开发全貌。
二、项目全景概览
2.1 模块架构
| 模块 | 功能描述 | 技术亮点 |
|---|---|---|
| AI 修仙模拟器(Index.ets) | 基于 DeepSeek-V3 的智能问答,8 大修仙方向分类指导 | SSE 流式响应、古风角色扮演提示词体系、非流式回退机制 |
| 拖拽排序演示(DragSortStack.ets) | 卡片列表拖拽重排组件 | Stack 三层叠加法 + PanGesture 手势联动、声明式动画 |
2.2 技术栈
| 维度 | 选型 | 说明 |
|---|---|---|
| 开发语言 | ArkTS | 鸿蒙原生声明式 UI 语言,基于 TypeScript 扩展 |
| UI 框架 | ArkUI | 鸿蒙原生声明式 UI 框架,响应式数据驱动 |
| 网络框架 | @kit.NetworkKit | 鸿蒙原生 HTTP 网络请求库,支持 SSE 流式 |
| 目标 SDK | API 24(HarmonyOS 7.0.0) | 最新稳定版 SDK |
| AI 模型 | DeepSeek-V3 | 通过 Gitcode AI API 调用,兼容 OpenAI 格式 |
| 构建工具 | Hvigor | 鸿蒙原生构建系统 |
三、AI 修仙模拟器:架构设计
3.1 三层架构体系
AI 修仙模拟器采用了经典的分层架构,将 UI 层、服务层、网络层清晰分离:
┌──────────────────────────────────────────────┐
│ UI 层(Index.ets) │
│ @Entry @Component struct Index │
│ ├─ buildHeader() 标题栏 + 仙缘设置 │
│ ├─ buildCategorySelector() 修炼方向选择器 │
│ ├─ buildChatArea() 聊天消息展示区 │
│ ├─ buildInputArea() 输入 + 发送区域 │
│ └─ buildSettingsPanel() 仙缘设置面板 │
├──────────────────────────────────────────────┤
│ 服务层(AIChatService.ets) │
│ ├─ queryAI() 发起 AI 请求(核心) │
│ ├─ cancelAI() 取消请求 │
│ ├─ setSystemPrompt() 设置前置心法(提示词) │
│ ├─ setCategoryPrompt() 按修炼方向设置心法 │
│ ├─ setApiConfig() 配置灵脉地址和灵钥 │
│ └─ setModel() 切换修行法门(模型) │
├──────────────────────────────────────────────┤
│ 网络层(@kit.NetworkKit) │
│ ├─ http.createHttp() 创建 HTTP 请求 │
│ ├─ on('dataReceive') SSE 流式数据监听 │
│ └─ request() 发起 POST 请求 │
└──────────────────────────────────────────────┘
UI 层只关心展示和交互,不涉及网络请求细节;服务层封装了所有业务逻辑,包括提示词管理、API 配置、SSE 解析等;网络层由鸿蒙原生 SDK 提供。各层之间通过接口契约通信,耦合度极低,便于单元测试和功能扩展。
3.2 核心数据流
当弟子(用户)在输入框中描述修行疑惑并点击发送时,完整的数据流如下:
弟子发送 → onSendMessage()
→ 校验输入(非空、非加载中)
→ 将弟子问加入 messages[]
→ 设置 isLoading = true(触发 UI 显示加载指示器)
→ 调用 queryAI(callbacks, historyMessages)
→ 取消上一次未完成请求(防止道法冲突)
→ 构造请求体(前置心法 + 历史问答)
→ 注册 dataReceive 监听(SSE 流式)
→ 发起 HTTP POST 请求
→ SSE 模式:逐 token 触发 onData()
→ onData(delta) → currentAIResponse += delta
→ UI 实时刷新(@State 驱动)
→ SSE 结束:触发 onDone()
→ 将累积回复加入 messages[]
→ 重置 isLoading = false
→ 非流式回退:若 dataReceive 未触发
→ 从完整响应体解析 SSE 或 JSON
→ 一次性返回完整内容
3.3 非流式回退设计
项目中一个值得称道的设计是非流式回退机制。鸿蒙的 http 模块在不同版本中对 SSE 的支持行为不完全一致,某些版本下 dataReceive 事件可能不被触发。开发者通过 receivedAnyData 标志位检测这种情况,一旦发现则在 request 回调中手动解析完整响应体。这种防御性编程思维,是生产级应用所必需的。
请求完成
→ dataReceive 已触发?→ 是→ 正常 SSE 流式结束
→ dataReceive 未触发?→ 是→ 尝试 SSE 格式解析
→ 成功?→ 是→ 拼接完整内容
→ 失败?→ 尝试 JSON 格式解析
→ 成功?→ 是→ 返回完整内容
→ 失败?→ 返回错误预览
四、修仙修炼方向预设体系
4.1 八大修炼方向
项目的核心亮点是将修仙文化体系融入 AI 对话系统,设计了 8 个修炼方向,每个方向配备精心编写的角色扮演提示词:
| 修炼方向 | 图标 | 角色定位 | 传授内容 |
|---|---|---|---|
| 练气入门 | 🌀 | 引气长老 | 感应灵气、打通经脉、吐纳调息 |
| 筑基功法 | ⛰️ | 宗门长老 | 筑基丹炼制、道基种类、心境磨砺 |
| 金丹大道 | ✨ | 太上长老 | 金丹凝练、丹火掌控、心魔抵御 |
| 元婴出窍 | 🌟 | 大能修士 | 元婴养炼、元神出窍、神识修炼 |
| 炼丹制药 | 🔥 | 丹道宗师 | 丹方配伍、天材地宝、火候掌控 |
| 炼器锻造 | ⚔️ | 隐世器师 | 法器品阶、材料提纯、器纹铭刻 |
| 阵法符箓 | 📜 | 阵道宗师 | 聚灵阵、杀阵、符箓绘制 |
| 渡劫飞升 | ⚡ | 大乘期修士 | 天劫应对、渡劫准备、心魔降服 |
4.2 提示词工程解析
每个修炼方向的提示词(在代码中称为"前置心法")都经过精心设计,包含四个维度:
角色定义:明确 AI 的身份和修为境界。例如练气入门的提示词开头:
'你是一位修仙门派中德高望重的引气长老,专司新弟子的启蒙教导。\n\n' +
'你的任务是指导刚刚踏入仙途的弟子掌握练气期的基础法门,包括:\n' +
' ① 感应天地灵气的基础心法\n' +
' ② 打通十二正经与奇经八脉的功法\n' +
' ③ 吐纳调息与周天运转的技巧\n' +
覆盖范围:列出该方向下 AI 能够解答的具体问题类别,每个方向 5 个要点。
回答原则:定义 AI 的回答风格和约束。例如筑基功法的原则包括"因材施教,根据弟子的灵根属性推荐不同的筑基法门"“告诫天道筑基之艰难,地道筑基之稳妥”。
语气风格:统一要求"古风雅韵的中文回复",并针对不同方向微调——练气入门"语气慈祥而威严",金丹大道"语气超然而亲切",炼器锻造"语气刚毅而专注"。这种精细化的角色扮演提示词工程,能显著提升 AI 回答的质量和沉浸感。
五、SSE 流式响应实现
5.1 核心代码
流式响应(Streaming Response)是 AI 对话应用的关键体验。用户发出问题后,AI 的回复逐字逐句地实时展示,而不是等待完整回复生成后再一次性显示。这种体验更接近真实对话,能显著降低等待焦虑。
httpRequest.on('dataReceive', (data: ArrayBuffer) => {
const text = arrayBufferToString(data);
buffer += text;
receivedAnyData = true;
const lines = buffer.split('\n');
buffer = lines.pop() ?? ''; // 最后一段可能不完整,留到下次
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed.startsWith('data:')) continue;
if (trimmed === 'data:[DONE]') {
if (!isDone) { isDone = true; callbacks.onDone(); }
continue;
}
const content = parseSSEDataLine(trimmed);
if (content) callbacks.onData(content);
}
});
5.2 关键技术细节
行缓冲区:SSE 数据分块传输,一个数据块可能包含半行数据。代码使用 buffer 变量累积接收到的数据,每次按 \n 分割后,将最后一段(可能不完整)保留到下一次处理。这是 SSE 解析的标准做法,能确保数据边界正确。
ArrayBuffer 解码:鸿蒙的 dataReceive 事件传递的是 ArrayBuffer 类型,需要自行解码为字符串:
function arrayBufferToString(buffer: ArrayBuffer): string {
const uint8Arr = new Uint8Array(buffer);
let text = '';
for (let i = 0; i < uint8Arr.length; i++)
text += String.fromCharCode(uint8Arr[i]);
return text;
}
DONE 标记处理:SSE 流的结束标记 data:[DONE] 需要特殊处理,且通过 isDone 标志位防止重复触发 onDone() 回调。
六、拖拽排序组件:Stack 三层叠加法
6.1 设计理念
拖拽排序组件完全使用 ArkUI 内置的 Stack(层叠布局)和 PanGesture(平移手势)实现,不依赖任何第三方库。其核心设计理念是三层叠加法,将三种视觉状态沿 Z 轴叠放在同一坐标系中:
┌─────────────────────────────────────────────┐
│ 第 3 层(最顶层):浮动卡片 │
│ → 仅被拖拽时可见 │
│ → shadow + scale + translate 产生悬浮感 │
├─────────────────────────────────────────────┤
│ 第 2 层(中间层):静止卡片 │
│ → 列表正常展示状态 │
│ → 被拖拽时将透明度降为 0(隐藏原位卡片) │
├─────────────────────────────────────────────┤
│ 第 1 层(最底层):占位虚线框 │
│ → 仅本项被拖拽时显示 │
│ → 在原位置展示空槽以引导用户视觉 │
└─────────────────────────────────────────────┘
6.2 核心实现
每一张卡片都是一个 Stack 容器,内部包含三层 Row 布局:
Stack() {
// 第 1 层:占位虚线框
if (this.draggingIndex === index) {
Row()
.height(this.CARD_HEIGHT)
.border({ width: 2, color: '#CCCCCC', style: BorderStyle.Dashed })
}
// 第 2 层:静止卡片
Row() { /* 序号标 + 标题 + 描述 + 拖拽手柄 */ }
.opacity(this.draggingIndex === index ? 0 : 1)
// 第 3 层:浮动卡片
Row() { /* 与第 2 层相同的内容 */ }
.shadow({ radius: 24, offsetY: 8, color: 'rgba(0,0,0,0.20)' })
.scale({ x: this.draggingIndex === index ? 1.05 : 1.0 })
.translate({ y: this.draggingIndex === index ? this.dragOffsetY : 0 })
.opacity(this.draggingIndex === index ? 0.95 : 0)
}
.clip(false)
.animation({ duration: 250, curve: Curve.FastOutSlowIn })
6.3 三大视觉反馈要素
| 要素 | 参数值 | 效果 |
|---|---|---|
| 大阴影 | shadow radius: 24, offsetY: 8 | 卡片从表面浮起,投影在下方 |
| 轻微放大 | scale: 1.05 | 卡片比正常大 5%,突出选中感 |
| Y 轴位移 | translate y: dragOffsetY | 卡片跟随手指移动 |
6.4 手势处理
手势使用 PanGesture 实现,只监听纵向拖拽,并设置 distance: 10 的触发阈值防止误触:
PanGesture({ direction: PanDirection.Vertical, distance: 10 })
.onActionStart(() => {
this.draggingIndex = index;
this.startGlobalY = event.fingerList[0].globalY;
})
.onActionUpdate(() => {
this.dragOffsetY = currentGlobalY - this.startGlobalY;
const stepHeight = CARD_HEIGHT + CARD_GAP;
const offsetSteps = Math.round(this.dragOffsetY / stepHeight);
this.targetInsertIndex = clamp(index + offsetSteps, 0, items.length - 1);
})
.onActionEnd(() => {
if (this.targetInsertIndex !== index) {
const moveItem = this.items.splice(index, 1)[0];
this.items.splice(this.targetInsertIndex, 0, moveItem);
}
this.draggingIndex = -1;
this.dragOffsetY = 0;
})
目标插入位置的计算公式直观有效:原始索引加上偏移量除以卡片步高(卡片高度 + 间距)的取整值。当拖拽结束且目标位置与原始位置不同时,通过数组的 splice 操作完成重排。
七、鸿蒙开发关键概念
7.1 ArkTS 装饰器体系
ArkTS 扩展了 TypeScript 的装饰器体系,用于声明式 UI 编程。项目中用到了以下核心装饰器:
| 装饰器 | 作用 | 使用示例 |
|---|---|---|
| @Entry | 标记页面入口 | Index.ets, DragSortStack.ets |
| @Component | 定义组件 | struct Index, struct DragSortPage |
| @State | 声明响应式状态 | messages, inputText, draggingIndex |
| @Builder | 定义可复用 UI 构建函数 | buildHeader(), buildChatArea() |
@State 是 ArkTS 响应式编程的核心。当 @State 装饰的变量被修改时,框架会自动追踪依赖并仅更新受影响的部分 UI,而不是整页重绘。例如,currentAIResponse 每次更新时,只有流式消息气泡区域会重新渲染,标题栏和输入框不受影响。
7.2 Ability 生命周期
鸿蒙中页面被组织在 Ability 中。EntryAbility 负责管理应用的启动和页面加载:
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) { /* 初始化 */ }
onWindowStageCreate(windowStage) {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) { hilog.error(...); return; }
});
}
onForeground() { /* 进入前台 */ }
onBackground() { /* 进入后台 */ }
onDestroy() { /* 销毁 */ }
}
7.3 API 24 升级要点
本项目的 build-profile 配置已升级至 API 24:
{
products: [{
name: "default",
targetSdkVersion: "7.0.0(24)",
compatibleSdkVersion: "7.0.0(24)",
runtimeOS: "HarmonyOS",
}]
}
API 24(HarmonyOS 7.0.0)相较于 API 23 的主要改进包括:更稳定的 SSE 流式支持、更完善的 ArkUI 动画能力、性能优化和内存管理改进。
八、工程实践与最佳实践
8.1 错误处理
项目中体现了分层次的错误处理策略:
网络层:检查 HTTP 状态码,截断过长的错误信息。解析层:JSON 解析失败时静默跳过,不中断整体流程。UI 层:将错误信息作为 AI 消息展示给用户,而非弹出刺眼的原生对话框。这种做法在用户体验上更温和,即使用户的问题无法解答,应用也会以角色的口吻告知原因。
8.2 资源清理
AI 请求的网络资源在多个场景下需要清理:切换修炼方向时、用户点击取消时、发起新请求前。cancelAI() 函数确保在任何情况下都不会积压未完成的请求:
export function cancelAI(): void {
if (httpRequestTask) {
try { httpRequestTask.destroy(); } catch (_) { }
httpRequestTask = null;
}
}
8.3 条件渲染
ArkUI 通过 @State 加 if 条件渲染实现动态 UI:
if (messages.length === 0) buildEmptyGuide()
if (isLoading && currentAIResponse.length > 0) buildAIBubble()
if (isLoading && currentAIResponse.length === 0) buildLoadingIndicator()
if (isLoading) buildCancelButton() else buildSendButton()
这些条件逻辑清晰定义了 UI 的四种状态:空状态、等待中、流式输出中、完成。
8.4 模块化设计
AIChatService 作为独立服务模块,通过 export 和 import 机制与 UI 层解耦。项目导出了分类常量(CATEGORY_PROMPTS、CATEGORY_ICONS、CATEGORY_ORDER)和服务函数(queryAI、cancelAI、setSystemPrompt 等),遵循了关注点分离原则。
九、两模块技术对比
| 维度 | AI 修仙模拟器 | 拖拽排序组件 |
|---|---|---|
| 核心能力 | 网络通信 + 数据解析 + AI 集成 | 手势交互 + 动画 + 布局 |
| 状态管理 | 大量 @State 响应式变量 | 少量 @State + 非响应式变量 |
| 异步处理 | SSE 流式 + 回调 + 超时 | 手势事件同步处理 |
| 视觉复杂度 | 气泡 + 列表 + 弹窗 | 阴影 + 缩放 + 位移 |
| 错误处理 | 多层兜底降级解析 | 边界约束 + 异常保护 |
| 可配置性 | 灵脉/灵钥/心法均可配置 | 固定数据展示 |
AI 修仙模拟器偏向应用层能力集成——对接外部 AI 服务、处理网络异常、提供灵活的配置。拖拽排序组件则展示了原生交互的深度定制——利用 Stack 层叠和手势实现流畅的拖拽体验。两者合在一起,展示了鸿蒙 NEXT 作为成熟移动平台的能力广度。
十、总结与展望
通过深入分析 app622 项目,我们看到了鸿蒙原生应用从架构设计到细节实现的完整图谱。AI 修仙模拟器展示了分层架构、SSE 流式响应、修仙文化角色扮演提示词体系的设计思路;拖拽排序组件展示了 Stack 层叠布局、手势处理、过渡动画的精妙组合。
最值得学习的是项目中贯穿的工程思维——非流式回退机制、多层的解析降级策略、资源清理的防护逻辑。这些看不见的代码才是区分演示项目和实际应用的关键。
项目已升级至 API 24,紧跟鸿蒙生态的最新发展。鸿蒙生态正在蓬勃发展,选择拥抱鸿蒙 NEXT,不仅是进入一个新平台,更是参与到建设自主操作系统生态的历史进程中。希望本文能为你的鸿蒙开发之旅提供有价值的参考。
本文基于 app622 项目源码撰写(HarmonyOS NEXT / ArkTS / API 24)
更多推荐


所有评论(0)