鸿蒙 Agent 开发实录 Day 2:赋予 AI 记忆,手搓系统级“沉浸光感” UI
“好的技术不仅要解决痛点,还要让人感觉不到它的存在。UI 不只是外壳,它是用户与智能体建立信任的第一道桥梁。”
大家好!在上一篇博客中(Day 1),我跑通了鸿蒙原生环境下的 SSE 流式对话。但那个只会一问一答、界面像十年前的“套壳聊天框”显然无法满足我对一个原生 Agent 的要求。
今天是我项目开发的第二天,目标非常硬核:打破单轮对话失忆症,并彻底重构 UI,纯手写实现媲美鸿蒙系统级的“沉浸光感(Glassmorphism)”质感。
在动手写代码前,我给自己定下了一条设计信条:应用不光要长在鸿蒙生态里,还要看起来就是鸿蒙应用。
这篇文章,记录了我是如何把 AI 的记忆打通,如何硬核 Debug 解决“流式文字隐身”的玄学问题,以及在 ArkUI 中迭代四个版本,最终死磕出完美毛玻璃与动态呼吸光晕的过程。
🧠 架构演进:如何让 AI 摆脱“金鱼记忆”?
大模型的 API 本质上是无状态的。你觉得它失忆,是因为你每次只发了当前那句话。要让它拥有记忆,就必须在客户端手动维护一个“上下文数组”。
我将原本接收 userMessage: string 的方法,改造成了接收 messages: ChatMessage[] 数组。每次用户提问,前端就会把历史记录 + 当前问题全部打包发给云端。
🐞 硬核 Debug 实录:流式文字“隐身”与状态刷新黑洞
在跑通记忆功能后,我遇到了一个极其折磨人的底层 Bug:
现象:发送消息后,后台日志显示文字正一个个接收。但手机界面上什么都不显示,一直卡在“正在思考中”。直到我把手机系统切换为深色/浅色模式,文字才瞬间弹出来!
经过排查,我定位到了两个叠加的原因:
- ArkUI 深层状态监听失效:直接修改数组内部对象的属性(this.messages[i].content += delta),ArkUI 的 @State 有时无法捕捉。破局方案:放弃直接修改属性,改用 .map() 返回全新的对象数组,并在 ForEach 的 Key 中绑定文字长度,逼迫框架强制重绘:
this.messages = this.messages.map((m, i) => { if (i === aiIndex) return { role: m.role, content: m.content + delta }; return m; }); - 深色模式导致文字颜色“隐身”:因为没给文字强制指定颜色,系统深色模式下文字变成了白色,与未渲染完的背景融为一体。破局方案:在 EntryAbility.ets 入口处强制覆盖系统渲染模式(this.context.getApplicationContext().setColorMode(1)),彻底杜绝反色干扰。
🎨 UI 重构的“四世同堂”:从能用,到极致原生
记忆打通后,我开始死磕 UI。为了贯彻“看起来就是鸿蒙应用”的信条,我翻看开发相册,四个版本的截图完美记录了我死磕原生视觉的过程。
📸 版本 1.0:能跑就行的“裸奔”时代
· 视觉特征:纯黑背景,没有任何气泡包裹。用户的提问是个巨大的灰色方块,AI 的回答直接生硬地打印在下方,只有一个简陋的蓝色椭圆形“发送”按钮。

· 技术状态:刚跑通网络请求和流式解析,连最基础的聊天布局都没有。只追求“字能蹦出来”。
📸 版本 2.0:气泡诞生,深陷“Bug 泥潭”
· 视觉特征:引入了基础的左右气泡(右侧用户蓝,左侧 AI 白),底部用上了带有圆角的输入框。

· 技术状态:记忆功能初步跑通(AI 能记住用户之前的问题了),但遭遇了前文提到的底层 Bug——发送消息后,界面上只显示一个白色的圆点,文字死活渲染不出来,必须切换手机系统模式才能“解冻”。
📸 版本 3.0:妥协于系统的“大众脸”
· 视觉特征:切换到白天模式后,界面终于看清了。但整体质感非常平庸,输入框像极了十年前的安卓默认控件。

· 技术状态:为了在浅色模式下强行看清文字,牺牲了高级感。这让我意识到:必须强制统一 App 的主题色,不能任由系统反色机制乱搞。 也是在这个版本,我决定彻底走向“暗黑沉浸风”。
📸 版本 4.0:纯血鸿蒙的沉浸光感与动态生命(当前形态)
· 视觉特征:完全蜕变。深空灰背景加上底层的“呼吸极光”漫反射;AI 的气泡变成了带有极细高光描边的“半透明磨砂玻璃”;底部的输入区变成了一个带有弹性按压反馈、高斯模糊的“悬浮岛”。

· 技术状态(材质与动效的双重爆发):
- 纯手写毛玻璃:尝试调用 API 26 的 uiMaterial 失败后(系统仅支持特定组件),我直接手写:多层 Stack 叠加 + backdropBlur(60) 模糊 + linearGradient 光线折射 + onTouch 物理微形变。
- 注入灵魂的“动态呼吸光晕”:静态毛玻璃再美也会有“死寂感”。为了实现原生系统那种有生命的背景,我在 aboutToAppear 生命周期里加入了 setInterval 定时器,配合 animateTo 实现平滑过渡。让底层两个巨大的、极度模糊的彩色光球(鸿蒙蓝 + 紫罗兰),每 5 秒随机游走、缩放,就像在屏幕深处呼吸一样。
⚠️ 性能细节:高频动画极耗 GPU,必须在 aboutToDisappear 中 clearInterval 销毁定时器,并给背景容器加上 .clip(true) 防止光晕溢出卡顿。
💻 阶段成果与核心代码
下面是实现“高级光感输入岛”与“动态呼吸极光”的核心代码:
1. 动态呼吸背景(让死寂的深色背景活过来)
// 在 aboutToAppear 中开启 5 秒一次的平滑游走动画
this.timerId = setInterval(() => {
animateTo({ duration: 5000, curve: Curve.EaseInOut }, () => {
this.glow1X = Math.random() * 100 - 200;
this.glow1Y = Math.random() * 200 - 100;
this.glow1Scale = 0.8 + Math.random() * 0.4;
// 光晕 2 同理...
});
}, 5000);
2. 毛玻璃折射输入岛(模拟光线漫反射)
Stack() {
// 顶层:高光折射层
Row()
.width('100%').height('100%').borderRadius(29)
.linearGradient({
direction: GradientDirection.RightBottom,
colors: [['#33FFFFFF', 0.0], ['#00000000', 0.4], ['#00000000', 1.0]]
})
// 内容层...
}
.backdropBlur(60) // 高斯模糊底座
.backgroundColor('#4D000000') // 30% 纯黑玻璃基底
.borderRadius(29)
.border({ width: 0.8, color: '#80FFFFFF' }) // 极细的玻璃切面高光
.shadow({ radius: 40, color: '#CC000000', offsetX: 0, offsetY: 15 }) // 深邃悬浮阴影
.scale({ x: this.isInputPressed ? 0.97 : 1.0, y: this.isInputPressed ? 0.97 : 1.0 }) // 按压微形变
.animation({ duration: 150, curve: Curve.EaseOut })
🚀 下一步计划:让 AI 长出“双手”
现在,我的 Agent 已经拥有了完美的记忆系统和极具未来感的交互界面。但目前的它,依然只是一个“会聊天的壳子”。
接下来的开发中,我们要真正触及 Agent 的灵魂:Tool Calling(工具调用)与 ReAct 循环。
· 在沙箱中搭建显式的 Map<string, ToolHandler>。
· 实现第一个本地工具(如获取时间、读取沙箱文件)。
· 跑通核心闭环:用户提问 -> 模型判断意图并返回 JSON -> 本地执行工具 -> 将结果回传给模型 -> 模型生成最终回复。
🌟 写在最后
我希望当用户打开这个应用时,第一眼就能感受到那种纯正的鸿蒙基因。应用不光要长在鸿蒙生态里,还要看起来就是鸿蒙应用。 只有视觉上足够原生、足够安全、足够高级,用户才敢把自己的隐私和日常任务,放心地交给这个端侧 AI。
这条路还很长,我会持续在 Gitee 更新开发进度与源码。如果你也对鸿蒙原生开发、端侧 AI 感兴趣,欢迎关注我,我们评论区交流!
更多推荐



所有评论(0)