


一、引言
修仙小说中的「引路人」------一位修为深不可测的老前辈,在关键时刻为迷茫的修行者指点迷津。这种「AI 引路人」的概念,天然适合用大语言模型来实现。
本文将以「AI 修仙功法」应用为案例,详细讲解在 HarmonyOS NEXT 上使用 ArkTS 原生框架构建 AI 对话应用的完整技术路径。与通用生活助手不同,修仙功法应用需要:
- 角色化 System Prompt:让 AI 扮演修仙引路人而非通用助手
- 领域专业术语:练气、筑基、金丹、元婴、化神等境界体系
- 风格化回答:古朴典雅的语气 + 修仙 emoji(☯✨🌊)
- 结构化提示词:8 大修仙领域 × 4 段式回答结构
本文将从项目架构、提示词工程、SSE 流式解析、UI 组件设计、编译错误排查五个维度,完整呈现一个鸿蒙原生 AI 对话应用的技术细节。
二、项目架构
2.1 三层架构设计
应用采用经典的三层架构,职责清晰、易于扩展:
Index.ets(UI 层)
↓ 调用 queryAI() / cancelAI()
AIChatService.ets(服务层)
↓ @kit.NetworkKit HTTP 请求
GitCode AI API / DeepSeek-V3(AI 模型层)
各层职责:
| 层级 | 文件 | 职责 | 关键代码 |
|---|---|---|---|
| UI 层 | Index.ets |
渲染聊天界面、处理用户交互、管理消息状态 | @State 装饰器、ChatBubble 组件 |
| 服务层 | AIChatService.ets |
封装 HTTP 请求、SSE 流式解析、错误处理 | queryAI()、parseSSEDataLine() |
| API 层 | GitCode AI | 提供 DeepSeek-V3 模型推理能力 | REST API / SSE 协议 |
2.2 应用入口 EntryAbility
typescript
// entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, 'App',
'Failed to load content: %{public}s', JSON.stringify(err));
return;
}
hilog.info(0x0000, 'App', 'Succeeded in loading content.');
});
}
}
windowStage.loadContent('pages/Index') 加载 Index.ets 页面,路径不含 .ets 后缀。
2.3 项目文件结构
entry/src/main/ets/
├── entryability/
│ └── EntryAbility.ets # 应用入口
└── pages/
├── Index.ets # AI 修仙功法聊天界面
├── AIChatService.ets # AI 服务层
├── RunnerPage.ets
└── TabsBottomDemo.ets
三、System Prompt 工程:让 AI 成为修仙引路人
System Prompt(系统提示词)是 AI 对话应用的灵魂。对于修仙功法应用,提示词的设计决定了 AI 是否能「入戏」------能否真正像一位活了千年的修仙前辈那样思考和回答。
3.1 完整 System Prompt
typescript
/** 系统提示词:AI 修仙功法 --- 你的修仙引路人 */
const SYSTEM_PROMPT: string =
'你是一位名叫「AI 修仙功法」的资深修仙引路人,来自上古修仙界,' +
'精通各大修炼体系(练气、筑基、金丹、元婴、化神等),' +
'擅长为修仙者解答修炼中的各种疑惑。\n\n' +
'你的回答应遵循以下原则:\n\n' +
'1. 修炼体系:涵盖但不限于以下修仙领域------\n' +
' · 功法心法:修炼法门、心法口诀、运功路线\n' +
' · 突破进阶:瓶颈突破、渡劫心得、境界感悟\n' +
' · 丹药灵药:丹方配伍、灵药辨识、药效解析\n' +
' · 法宝法器:法器炼制、灵宝鉴定、法宝运用\n' +
' · 阵法符箓:阵法布置、符箓绘制、禁制破解\n' +
' · 秘境探险:秘境攻略、妖兽图鉴、天材地宝\n' +
' · 宗门事务:宗门管理、师门关系、修炼资源\n' +
' · 心魔道心:心魔化解、道心稳固、因果轮回\n\n' +
'2. 回答结构:每个回答包含------\n' +
' · 道友好:先以道友相称,拉近距离\n' +
' · 修行分析:从修仙角度分析问题的本质\n' +
' · 功法建议:提供 1~3 条具体的修炼方法或口诀\n' +
' · 温馨提示:如涉及危险功法,务必叮嘱注意安全\n\n' +
'3. 回答风格:\n' +
' · 语气古朴典雅,像一位活了千年的老前辈在传道\n' +
' · 适当使用修仙术语(灵气、丹田、经脉、元神等)\n' +
' · 建议具体可操作,不空谈玄妙\n' +
' · 涉及天劫、魔修等危险内容时,务必提醒「量力而行」\n\n' +
'4. 格式要求:\n' +
' · 使用中文回答,可适当使用文言文增加韵味\n' +
' · 适当使用 ☯ ✨ 🌊 🔥 🌪️ 等修仙相关 emoji\n' +
' · 分点排列,方便道友阅读\n' +
' · 保持回答凝练,一般不超过 500 字\n\n' +
'请记住:你的使命是引导每一位道友在修仙之路上走得更远。';
3.2 提示词工程四维模型
一个好的 System Prompt 需要从四个维度进行设计:
┌──────────────────────────────────────────────┐
│ System Prompt │
├──────────────┬───────────────┬───────────────┤
│ 角色定义 │ 能力范围 │ 回答结构 │
│ (你是谁) │ (能做什么) │ (怎么说) │
├──────────────┼───────────────┼───────────────┤
│ 修仙引路人 │ 8 大修仙领域 │ 道友→分析 │
│ 来自上古 │ 功法/丹药 │ →建议→提示 │
│ 精通修炼体系 │ 法宝/阵法 │ 四段式结构 │
├──────────────┼───────────────┼───────────────┤
│ │ 风格约束 │ │
│ │ (什么语气) │ │
│ ├───────────────┤ │
│ │ 古朴典雅 │ │
│ │ 文言韵味 │ │
│ │ ☯ emoji │ │
└───────────────┴───────────────┘───────────────┘
对比通用助手的提示词差异:
| 维度 | 通用生活助手 | 修仙功法引路人 |
|---|---|---|
| 角色 | 「智能生活助手」 | 「资深修仙引路人,来自上古修仙界」 |
| 称呼 | 「你」 | 「道友」 |
| 领域 | 情感/职场/学习/健康 | 功法/丹药/法宝/阵法/秘境/心魔 |
| 术语 | 通俗易懂 | 灵气/丹田/经脉/元神/渡劫 |
| 语气 | 温和亲切 | 古朴典雅,可带文言文 |
| emoji | 😊📊🎯 | ☯✨🌊🔥🌪️ |
| 安全提醒 | 「建议咨询专业人士」 | 「量力而行」「注意危险」 |
3.3 Prompt 工程的最佳实践
- 角色先行:第一句话定义角色,让 AI 从一开始就「入戏」
- 范围明确:列出 8 大修仙领域,避免 AI 跑题到现代话题
- 结构约束:四段式结构(道友→分析→建议→提示)确保回答完整性
- 风格锚定:「古朴典雅」「千年老前辈」等锚定语引导 AI 的语气
- 安全护栏:「量力而行」提示词作为安全阀门
- 示例暗示:虽然没有显式 Few-shot,但「道友好」等短语就是隐式的格式示例
四、数据模型与服务层设计
4.1 核心数据结构
typescript
/** 聊天消息结构体 */
export interface ChatMessage {
role: string; // 'system' | 'user' | 'assistant'
content: string; // 消息文本内容
}
/** 请求体结构体 */
export interface ChatCompletionRequest {
model: string; // 模型名称
messages: ChatMessage[]; // 完整消息历史
stream: boolean; // 是否流式
max_tokens: number; // 最大生成长度
temperature: number; // 温度参数
top_p: number; // 核采样参数
frequency_penalty: number; // 频率惩罚
thinking_budget: number; // 思考预算
}
/** SSE 解析结果回调 */
export interface AICallbacks {
onData: (text: string) => void; // 每次收到新 token
onDone: () => void; // 流结束
onError: (errMsg: string) => void; // 错误处理
}
接口设计要点:
ChatMessage.role遵循 OpenAI 规范:system/user/assistantAICallbacks使用回调而非 Promise,因为 SSE 流式数据会分多次到达stream: true是 SSE 流式的开关标记
4.2 SSE 流式响应解析
SSE(Server-Sent Events)是一种服务器推送协议。AI 对话中,「打字机效果」的核心就是 SSE 流式响应。
SSE 数据格式示例:
data: {"choices":[{"delta":{"content":"道"}}]}
data: {"choices":[{"delta":{"content":"友"}}]}
data: {"choices":[{"delta":{"content":"请"}}]}
data: {"choices":[{"delta":{"content":"问"}}]}
data: [DONE]
解析函数:
typescript
/**
* 解析 SSE data 行,提取 content 增量
* @param line - 以 "data:" 开头的行(不含前缀)
* @returns 提取到的 content,或 null
*/
function parseSSEDataLine(line: string): string | null {
const jsonStr = line.slice(5).trim(); // 去掉 "data:" 前缀
if (!jsonStr) return null;
try {
const parsed = JSON.parse(jsonStr) as Record<string, Object>;
const choices = parsed.choices as Object[];
if (choices && choices.length > 0) {
const choice = choices[0] as Record<string, Object>;
// 兼容 delta(流式)和 message(非流式)
const delta = choice.delta as Record<string, Object>;
if (delta) return delta.content as string;
const message = choice.message as Record<string, Object>;
if (message) return message.content as string;
}
} catch (_) { /* JSON 解析失败,跳过 */ }
return null;
}
解析流程:
- 去掉
data:前缀(前 5 个字符) - 检查是否为结束标记
[DONE] - 尝试将剩余部分解析为 JSON
- 提取
choices[0].delta.content(流式格式) - 如果
delta不存在,尝试message.content(非流式回退)
4.3 完整请求流程
typescript
export function queryAI(
callbacks: AICallbacks,
messages: ChatMessage[],
): void {
// 1. 取消上一次请求
if (httpRequestTask) {
httpRequestTask.destroy();
httpRequestTask = null;
}
const httpRequest = http.createHttp();
httpRequestTask = httpRequest;
// 2. 合并系统提示词 + 聊天历史
const fullMessages: ChatMessage[] = [
{ role: 'system', content: SYSTEM_PROMPT },
...messages,
];
// 3. 构建请求体
const requestBody: ChatCompletionRequest = {
model: 'deepseek-ai/DeepSeek-V3',
messages: fullMessages,
stream: true,
max_tokens: 2048,
temperature: 0.6,
top_p: 0.95,
frequency_penalty: 0,
thinking_budget: 2048,
};
// 4. 注册 SSE 监听
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. 发起 POST 请求
httpRequest.request(API_URL, {
method: http.RequestMethod.POST,
header: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
Accept: 'text/event-stream', // ← 要求 SSE 流式
},
extraData: JSON.stringify(requestBody),
connectTimeout: 30000,
readTimeout: 120000,
}, callback);
}
4.4 非流式回退机制
不同 HarmonyOS 设备对 dataReceive 事件的支持程度不同。为了确保兼容性,我们设计了「三层回退」机制:
typescript
// ---- 非流式回退 ----
if (!receivedAnyData && resp.result) {
const bodyStr = typeof resp.result === 'string'
? resp.result
: arrayBufferToString(resp.result as ArrayBuffer);
// 第 1 层:SSE 格式解析
const sseContent = parseFullSSEBody(bodyStr);
if (sseContent) {
callbacks.onData(sseContent);
} else {
// 第 2 层:非流式 JSON 格式
const jsonContent = parseNonStreamingBody(bodyStr);
if (jsonContent) {
callbacks.onData(jsonContent);
} else {
// 第 3 层:错误提示
callbacks.onError(`无法解析响应`);
}
}
}
回退层级:
- 流式 dataReceive(最佳体验,逐字显示)
- SSE 格式解析(从完整响应体解析)
- 非流式 JSON 解析(完整 JSON 格式)
- 错误提示(以上全部失败)
五、UI 层:修仙聊天界面
5.1 页面状态管理
typescript
@Entry
@Component
struct AICultivationPage {
@State messages: ChatMessage[] = []; // 消息列表
@State inputText: string = ''; // 输入框文本
@State isLoading: boolean = false; // 是否正在请求
@State currentAIResponse: string = ''; // 流式拼接中的回复
}
@State 工作原理解析:
用户输入 "如何突破筑基"
↓
sendMessage() 被调用
↓
this.messages.push(userMsg) → @State 触发 UI 重渲染 → 用户气泡出现
this.isLoading = true → @State 触发 UI 重渲染 → 加载动画出现
↓
queryAI() 发起请求
↓
onData 回调被触发
↓
this.currentAIResponse += "道" → @State 触发 UI 重渲染 → 文字逐字显示
this.currentAIResponse += "友" → @State 触发 UI 重渲染
this.currentAIResponse += "请" → @State 触发 UI 重渲染
... (每次收到 token 都触发)
↓
onDone 回调被触发
↓
this.messages.push(assistant) → @State 触发 UI 重渲染 → 完整气泡固定
this.isLoading = false → @State 触发 UI 重渲染 → 加载动画消失
5.2 聊天气泡组件
typescript
@Component
struct ChatBubble {
private msg: ChatMessage = { role: 'user', content: '' };
private isLoading: boolean = false;
build() {
Column() {
if (this.msg.role === 'user') {
// ── 用户消息:蓝色右对齐 ──
Row() {
Blank()
Column() {
Text(this.msg.content)
.fontSize(15).fontColor('#ffffff')
}
.padding(14)
.backgroundColor('#2d5f8a')
.borderRadius({
topLeft: 16, topRight: 4, // ★ "尾巴"效果
bottomLeft: 16, bottomRight: 16
})
.constraintSize({ maxWidth: '80%' })
}
.width('100%')
} else {
// ── AI 消息:灰色左对齐 + ☯ 头像 ──
Row() {
// ☯ 修仙头像
Column() { Text('☯').fontSize(20) }
.width(36).height(36)
.backgroundColor('#eef2f7').borderRadius(18)
.margin({ right: 8 })
// 气泡内容
Column() {
if (this.isLoading && !this.msg.content) {
// ★ 正在输入动画:三圆点渐隐 ★
Row() {
ForEach([0, 1, 2], (idx: number) => {
Circle().width(6).height(6).fill('#2d5f8a')
.opacity(0.4 + idx * 0.3)
.margin({ left: 2, right: 2 })
})
}
.padding(14).backgroundColor('#f0f4f8').borderRadius(16)
} else {
Text(this.msg.content)
.fontSize(15).fontColor('#1a1a2e')
}
}
.padding(14)
.backgroundColor('#f0f4f8')
.borderRadius({
topLeft: 4, topRight: 16, // ★ "尾巴"效果
bottomLeft: 16, bottomRight: 16
})
.constraintSize({ maxWidth: '70%' })
Blank()
}
.width('100%')
}
}
.margin({ bottom: 12 })
}
}
气泡设计要点:
| 元素 | 用户气泡 | AI 气泡 |
|---|---|---|
| 背景色 | #2d5f8a 蓝色 |
#f0f4f8 灰色 |
| 文字色 | #ffffff 白色 |
#1a1a2e 深色 |
| 对齐 | 右对齐(Blank 在左) | 左对齐(Blank 在右) |
| 头像 | 无 | ☯ 太极图标 |
| 左上圆角 | 16px | 4px(尾巴) |
| 右上圆角 | 4px(尾巴) | 16px |
| 最大宽度 | 80% | 70% |
5.3 发送消息流程
typescript
sendMessage(text: string): void {
if (this.isLoading || !text.trim()) return;
// 1. 添加用户消息
const userMsg: ChatMessage = { role: 'user', content: text.trim() };
this.messages.push(userMsg);
this.inputText = '';
this.isLoading = true;
this.currentAIResponse = '';
// 2. 构建历史(保留最近 20 条)
const history: ChatMessage[] = this.messages
.filter(m => m.content !== '')
.slice(-20);
// 3. 调用 AI
queryAI({
onData: (content: string) => {
this.currentAIResponse += content; // 流式拼接
},
onDone: () => {
if (this.currentAIResponse) {
this.messages.push({ // 固化为静态气泡
role: 'assistant',
content: this.currentAIResponse,
});
}
this.currentAIResponse = '';
this.isLoading = false;
},
onError: (errMsg: string) => {
this.messages.push({
role: 'assistant',
content: `😅 道友抱歉,遇到了问题:${errMsg}`,
});
this.isLoading = false;
},
}, history);
}
5.4 空白引导页
当用户第一次打开应用时,显示引导页而非空列表:
typescript
if (this.messages.length === 0) {
Column() {
Text('☯').fontSize(56)
Text('AI 修仙功法').fontSize(22).fontWeight(FontWeight.Bold)
Text('道友,欢迎来到修仙界').fontSize(14).fontColor('#666')
// 6 大修炼领域卡片
Row() {
ForEach([
{ icon: '📜', text: '功法心法' },
{ icon: '⚡', text: '突破进阶' },
// ...
] as FeatureItem[], (item: FeatureItem) => {
Column() { Text(item.icon), Text(item.text) }
})
}
// 4 个快捷问题
Text('💡 道友不妨问问:')
Row() {
ForEach(QUICK_QUESTIONS, (q) => {
Text(q).onClick(() => { this.sendMessage(q); })
})
}
}
}
快捷问题列表:
☞ 练气期如何突破筑基?
☞ 火属性功法推荐?
☞ 炼丹入门先学什么?
☞ 修炼心魔怎么化解?
六、从「万能手册」到「修仙功法」的主题切换
本次开发的一个核心实践是------同一个 AI 对话框架,通过更换 System Prompt 和页面 UI,实现了完全不同的产品体验。
6.1 变更清单
| 变更项 | 改前(万能手册) | 改后(修仙功法) |
|---|---|---|
| 页面标题 | 🤖 AI 万能手册 |
☯ AI 修仙功法 |
| 欢迎图标 | 🤖 机器人 |
☯ 太极 |
| AI 头像 | 🤖 |
☯ |
| 欢迎语 | 「解决你生活中的各种问题」 | 「道友,欢迎来到修仙界」 |
| 能力卡片 | 情感/职场/学习/健康/生活/心理 | 功法心法/突破进阶/丹药灵药/法宝法器/阵法符箓/秘境探险 |
| 快捷问题 | 同事分歧/学习效率/失眠/健身 | 练气突破筑基/火属性功法/炼丹入门/心魔化解 |
| System Prompt | 8 大生活领域 | 8 大修仙领域 |
| AI 语气 | 温和亲切 | 古朴典雅,可带文言文 |
| emoji 风格 | 😊📊🎯 | ☯✨🌊🔥🌪️ |
6.2 只改数据不改架构
整个切换过程中,AIChatService.ets 的核心逻辑(HTTP 请求、SSE 解析、回退机制)完全不变,只修改了两个部分:
- AIChatService.ets :替换
SYSTEM_PROMPT常量 - Index.ets:替换页面标题、欢迎语、能力卡片、快捷问题
这种「数据驱动 UI」的设计模式,使得同一个 AI 对话框架可以快速适配不同的主题场景。
七、ArkTS 编译错误排查全记录
在开发过程中,我们遇到了 7 类编译错误。以下是完整记录和修复方案。
7.1 类型推断:禁止 any
Error: Use explicit types instead of "any"
修复 :为 ForEach 的回调参数添加显式类型注解。
typescript
// ❌ 错误
ForEach(data, (item) => { ... })
// ✅ 正确
ForEach(data, (item: FeatureItem) => { ... })
// 或使用 as 类型断言
ForEach([
{ icon: '📜', text: '功法心法' },
] as FeatureItem[], (item: FeatureItem) => { ... })
7.2 属性不存在:maxWidth
Error: Property 'maxWidth' does not exist on type 'ColumnAttribute'
修复 :使用 constraintSize 替代。
typescript
// ❌ 错误
Column() { }.maxWidth('80%')
// ✅ 正确
Column() { }.constraintSize({ maxWidth: '80%' })
7.3 Flex 容器属性差异
Error: Property 'flexDirection' does not exist on type 'FlexAttribute'
Error: Property 'flexWrap' does not exist on type 'FlexAttribute'
Error: Property 'justifyContent' does not exist on type 'FlexAttribute'
修复 :在 API 24 中,Flex 组件属性有限,改用 Row 或 Column。
typescript
// ❌ 错误:Flex 没有 justifyContent
Flex() { }.justifyContent(FlexAlign.Center)
// ✅ 正确:Row 支持 justifyContent
Row() { }.justifyContent(FlexAlign.Center)
7.4 overlay 回调格式
Error: Argument of type '{ builder: () => TextAttribute }' is not assignable
修复 :overlay() 接受函数格式而非对象格式。
typescript
// ❌ 错误
Circle().overlay({ builder: () => Text('↑') })
// ✅ 正确
Circle().overlay((): void => {
Text('↑').fontSize(20).fontColor('#ffffff')
})
7.5 私有属性传参(警告)
Warn: Property 'msg' is private and can not be initialized through constructor
修复 :移除 private 修饰符。
typescript
// 有警告
@Component
struct ChatBubble {
private msg: ChatMessage = { role: 'user', content: '' };
}
// 无警告
@Component
struct ChatBubble {
msg: ChatMessage = { role: 'user', content: '' };
}
7.6 字符混淆:汉字 vs Unicode 符号
问题:Text("上") 显示汉字「上」,而非箭头「↑」
修复:使用 Unicode 箭头符号。
typescript
// ❌ 显示汉字
Text("上")
// ✅ 显示箭头
Text('↑')
7.7 INTERNET 权限
Warn: To use this API, you need to apply for: ohos.permission.INTERNET
修复 :在 module.json5 中声明网络权限。
json
{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" }
]
}
}
八、性能优化
8.1 流式渲染性能
SSE 流式的核心优势是「逐字显示」,用户无需等待完整回复。每次 onData 回调触发时:
currentAIResponse累加新 token@State触发组件重渲染- UI 层只更新 Text 组件的文本内容
ArkUI 的声明式框架会智能 diff 虚拟 DOM,最小化实际渲染范围。Text 组件的文本更新不会导致整个消息列表重新布局。
8.2 对话历史截断
// 保留最近 20 条,避免超出 token 上限
const history = messages
.filter(m => m.content !== '') // 过滤空消息
.slice(-20); // 保留最近 20 条
DeepSeek-V3 上下文窗口为 32K tokens。20 条对话 + System Prompt 大约消耗 3-5K tokens,留有充足余量。
8.3 请求取消
当用户快速发送多条消息时,需要取消上一次请求:
typescript
export function cancelAI(): void {
if (httpRequestTask) {
httpRequestTask.destroy();
httpRequestTask = null;
}
}
UI 层的「停止」和「清空」按钮都调用 cancelAI()。
8.4 超时配置
typescript
connectTimeout: 30000, // 30 秒连接超时
readTimeout: 120000, // 120 秒读取超时
AI 生成完整回复可能需要 10-60 秒,因此读取超时设置较长。
九、ArkTS 核心概念总结
9.1 @State 响应式数据流
用户操作 → @State 变量改变 → build() 重渲染 → UI 更新
↑ │
└────────── 框架自动追踪 ────────────────┘
9.2 组件化设计模式
Page(页面容器)
├── ChatBubble(聊天气泡组件)
│ ├── 用户气泡(蓝色、右对齐)
│ └── AI 气泡(灰色、左对齐、☯头像)
│ └── 加载动画(三点闪烁)
├── 空白引导页(欢迎卡片 + 能力卡片 + 快捷问题)
├── 输入区(TextInput + 发送按钮)
└── 标题栏(标题 + 停止 + 清空)
9.3 服务层设计模式
queryAI(callbacks, messages)
↓
取消上一次请求 ← cancelAI()
↓
合并 System Prompt + 历史消息
↓
创建 HTTP 请求 + 注册 SSE 监听
↓
发起 POST 请求
↓
成功 → dataReceive 逐块处理
↓
解析 SSE → onData(token)
↓
流结束 → onDone()
↓
失败 → request 回调 → 回退机制
↓
SSE 格式解析 → onData(fullText)
JSON 格式解析 → onData(fullText)
无法解析 → onError(errMsg)
十、总结与展望
本文从项目架构、提示词工程、SSE 流式解析、UI 组件设计、编译错误排查五个维度,完整呈现了鸿蒙原生 ArkTS AI 修仙功法对话应用的技术细节。
核心收获:
- System Prompt 是 AI 应用的灵魂------同一个 AI 框架,通过更换提示词可以实现完全不同的产品体验(万能手册 → 修仙功法)
- SSE 流式响应是 AI 对话的标配------ArkUI 的 @State + 回调模式天然适合逐字显示
- 三层回退机制保障兼容性------流式 → SSE 解析 → JSON 解析,确保在不同设备上都能工作
- 编译错误是学习 ArkTS 的最佳教材------7 类常见错误的排查路径和修复方法
可以扩展的方向:
- 接入 TTS(文本转语音),让 AI 引路人「开口说话」
- 加入修炼进度系统,跟踪道友的修行历程
- 支持多轮对话中的「功法推荐」算法
- 接入图片理解能力,识别灵药图谱、法器样式