2025年2月28日-周报
本次周报也是对于鸿蒙原生大模型对话应用开发项目的一个总结
往期内容:
使用鸿蒙原生进行开发的大模型对话应用
项目开发记录
一、OpenAI 库调用与多轮对话实现
1.1 OpenAI 库调用
- 使用鸿蒙的
openai库实现与 OpenAI API 的交互。openai-arkts - 初始化配置:
const { Configuration, OpenAIApi } = require("openai"); const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY, }); const openai = new OpenAIApi(configuration);
1.2 多轮对话实现
- 多轮对话通过将本地对话记录塞进
messages实现,但可能会消耗大量 token。 - 示例代码:(下面并非项目实际代码)
let conversationHistory = []; async function getResponse(userInput) { conversationHistory.push({ role: "user", content: userInput }); const response = await openai.createChatCompletion({ model: "gpt-4", messages: conversationHistory, }); const assistantMessage = response.data.choices[0].message; conversationHistory.push(assistantMessage); return assistantMessage.content; }
二、页面设计与组件
2.1 页面设计参考
2.2 简单实现目标
- 基本功能:
- 对话界面和对话列表。
- 支持复制对话内容。
- 提供终止按钮。
- 实现多轮对话记录存储。
- 编辑对话记录(加入删除按钮)。
- 次要功能:
- 编辑上一次对话内容。
- 模型管理与切换。
2.3 页面组件划分
- 对话界面(Chat Interface):
- 对话气泡(Chat Bubble)。
- 输入框(Input Box)。
- 发送按钮(Send Button)。
- 终止对话按钮(End Conversation Button)。
- 对话列表(Conversation List):
- 新建对话按钮(New Conversation Button)。
- 历史对话项(Conversation Item)。
2.4 状态管理
- 当前对话状态:
- 当前对话的所有消息内容。
- 当前对话的唯一标识符(ID)。
- 当前对话的状态(进行中或已终止)。
- 对话列表状态:
- 所有对话的列表,每个对话包含其唯一标识符(ID)、标题、创建时间等信息。
- 当前选中的对话 ID。
2.5 组件间交互
- 对话列表:
- 新建对话时,更新对话列表状态。
- 点击历史对话项时,更新当前对话状态。
- 删除对话时,从对话列表中移除该对话。
- 对话界面:
- 用户发送新消息时,更新当前对话状态。
- 用户终止对话时,更新当前对话状态。
三、实体设计
3.1 实体类型
- Model:模型相关的信息,包括调用 API 的相关信息。
- Message:单次对话,按照 OpenAI API 提供的结构实现。
{ role: "user", content: userInput } - Conversation:创建一个 Conversation 时自动生成一个 UUID。
interface Conversation { id: string; messages: Message[]; createdAt: Date; updatedAt: Date; } - ConversationList:由 Conversation 构成的列表。
四、存储与读取对话记录
4.1 存储设计
- 以 Conversation 为单位存储,key 为 UUID,value 为 Conversation 构成的 JSON。
- 每轮对话完毕后,将 Conversation 插入 KV 存储,更新同理。
4.2 读取设计
- 读取所有记录,按照默认顺序构成 ConversationList 对应的 JSON 结构体。
4.3 删除记录
- 按照 UUID 删除记录。
五、状态管理与组件间通信
5.1 状态管理
- 使用状态管理(如 V2)实现组件间通信。
- 使用
@Local、@Param、@Event实现双向绑定。
5.2 组件间通信
- 使用
@Event处理回调,实现子组件向父组件通信。 - 示例:
- 点击删除按钮时,通知父组件删除对应对话。
六、页面组件构建
6.1 组件结构
- Sidebar 子组件:
- ConversationList。
- 添加 Conversation 的按钮。
- ConversationList:
- 滚动列表,由不定数量的子组件组成。
- 子组件:
- ConversationTabBar:按钮 + 文本,支持删除操作。
- ConversationInfo。
- DeleteConversationButton。
- Chat 子组件:
- ChatWindow:滚动列表,由不定数量的子组件组成。
- Brick:包含 UserAvatar 和 TextBubble。
- MessageInputBox:包含 MessageInput、SendButton 和 StopButton。
6.2 页面渲染逻辑
- 使用条件渲染实现页面切换。
- 渲染逻辑:
- 本地值 !== 全局值时,更新并触发重新渲染。
七、数据流动与展示
7.1 数据流动
- 父组件将数据传递给子组件。
- 子组件通过事件冒泡通知父组件数据变更。
7.2 数据存储与读取
- 使用 KV 存储对话列表和对话内容。
- 使用全局常量共享 key,所有组件通过 key 共享数据。
八、问题与解决方案
8.1 AppStorageV2 问题
- AppStorageV2 不支持异步操作,无法同步深层结构。
- 解决方案:使用 KV 存储,不依赖 AppStorageV2。
8.2 子组件通信问题
- 子组件无法直接通知父组件删除操作。
- 解决方案:使用事件冒泡,子组件触发事件,父组件监听并处理。
8.3 页面渲染问题
- 初始化时,静态数据优先级高于异步数据,导致渲染异常。
- 解决方案:在
onPageShow()中先赋值触发渲染,再建立数据连接。
九、下一步计划
9.1 功能实现
- 实现列表刷新和对话记录的本地存储与读取。
- 测试对话记录的存储与读取是否正常工作。
9.2 优化与重构
- 重构 Chat 数据部分,使其与 ChatContent 兼容。
- 实现对话列表与真实对话记录的一一对应。
9.3 页面切换
- 实现点击侧边栏时,Chat 界面重新读取数据并渲染。
- 实现删除侧边栏时,对应的 ChatContent 被删除。
9.4 数据对接
- 确保数据成功对接,实现全局状态共享。

浙公网安备 33010602011771号