鸿蒙原生 ArkTS 修仙功法 AI 对话应用:从 System Prompt 工程到 SSE 流式响应的完整实现

一、引言

修仙小说中的「引路人」------一位修为深不可测的老前辈,在关键时刻为迷茫的修行者指点迷津。这种「AI 引路人」的概念,天然适合用大语言模型来实现。

本文将以「AI 修仙功法」应用为案例,详细讲解在 HarmonyOS NEXT 上使用 ArkTS 原生框架构建 AI 对话应用的完整技术路径。与通用生活助手不同,修仙功法应用需要:

  1. 角色化 System Prompt:让 AI 扮演修仙引路人而非通用助手
  2. 领域专业术语:练气、筑基、金丹、元婴、化神等境界体系
  3. 风格化回答:古朴典雅的语气 + 修仙 emoji(☯✨🌊)
  4. 结构化提示词: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 工程的最佳实践

  1. 角色先行:第一句话定义角色,让 AI 从一开始就「入戏」
  2. 范围明确:列出 8 大修仙领域,避免 AI 跑题到现代话题
  3. 结构约束:四段式结构(道友→分析→建议→提示)确保回答完整性
  4. 风格锚定:「古朴典雅」「千年老前辈」等锚定语引导 AI 的语气
  5. 安全护栏:「量力而行」提示词作为安全阀门
  6. 示例暗示:虽然没有显式 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 / assistant
  • AICallbacks 使用回调而非 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;
}

解析流程

  1. 去掉 data: 前缀(前 5 个字符)
  2. 检查是否为结束标记 [DONE]
  3. 尝试将剩余部分解析为 JSON
  4. 提取 choices[0].delta.content(流式格式)
  5. 如果 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(`无法解析响应`);
    }
  }
}

回退层级

  1. 流式 dataReceive(最佳体验,逐字显示)
  2. SSE 格式解析(从完整响应体解析)
  3. 非流式 JSON 解析(完整 JSON 格式)
  4. 错误提示(以上全部失败)

五、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 解析、回退机制)完全不变,只修改了两个部分:

  1. AIChatService.ets :替换 SYSTEM_PROMPT 常量
  2. 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 组件属性有限,改用 RowColumn

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 回调触发时:

  1. currentAIResponse 累加新 token
  2. @State 触发组件重渲染
  3. 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 修仙功法对话应用的技术细节。

核心收获

  1. System Prompt 是 AI 应用的灵魂------同一个 AI 框架,通过更换提示词可以实现完全不同的产品体验(万能手册 → 修仙功法)
  2. SSE 流式响应是 AI 对话的标配------ArkUI 的 @State + 回调模式天然适合逐字显示
  3. 三层回退机制保障兼容性------流式 → SSE 解析 → JSON 解析,确保在不同设备上都能工作
  4. 编译错误是学习 ArkTS 的最佳教材------7 类常见错误的排查路径和修复方法

可以扩展的方向

  • 接入 TTS(文本转语音),让 AI 引路人「开口说话」
  • 加入修炼进度系统,跟踪道友的修行历程
  • 支持多轮对话中的「功法推荐」算法
  • 接入图片理解能力,识别灵药图谱、法器样式