做一个 AI 鸿蒙 App,最让我意外的不是 ArkTS 语法,也不是大模型接口的接入过程,而是做着做着我发现,过去这些年积累的 App 开发逻辑,有一大半用不上了。“逻辑变了”这四个字不是标题党,而是我把产品结构、交互方式、数据流全部推翻重来之后,真正体会到的一件事。这篇文章会把立项、设计、开发到踩坑的完整过程记录下来,重点说说那些让我觉得“再也回不去”的转变。如果你正准备做鸿蒙端 AI 应用,或者想把现有产品用 AI 重做一遍,这篇的内容应该对你有实际参考价值。
1. 立项那天,我差点用老套路做新东西
1.1 为什么选择“鸿蒙+AI”这个组合
先解释一下为什么非要选鸿蒙。我的判断很朴素:鸿蒙是目前少有的还在高速增长的操作系统,设备覆盖手机、平板、车机、智能家居,而 AI 要真正落到生活场景里,恰恰需要这样的多设备触点。你让用户单独下载一个 AI 应用,他大概率懒得装,但是 AI 能力如果能在手表上给你推送一句话、在车机上帮你规划路线,这种体验是传统单设备应用给不了的。另一方面,鸿蒙的开发范式也和过去不完全一样,ArkTS 的声明式写法、分布式数据管理、原子化服务这些概念,本身就适合承载以“任务”为单位的产品形态,而不是简单的页面堆叠。
选择 AI 的原因也很简单。过去 App 的能力边界是功能列表,用户要自己学、自己找;现在有了大模型,App 可以去理解用户的真实意图,代替用户完成一个个任务。鸿蒙的分布式能力和 AI 的多模态理解,某种程度上是天然互补的。所以我当时的定位很明确:做一个以 AI 为核心的鸿蒙 App,不是给旧应用加一个 AI 功能,而是让 AI 当主角。后来回头看,这个定位给我带来的麻烦和收获几乎一样多。
1.2 最初的方案:还是熟悉的“页面+按钮+接口”
刚立项那几天,我的第一版方案非常“传统”:首页放几个入口,智能问答、路线规划、攻略生成,每个入口对应一个页面,页面里放按钮和输入框,用户点了之后去调云端大模型接口,把返回结果渲染出来。技术上没有任何难点,ArkTS 写页面、封装 HTTP 请求、处理 JSON 返回,和我以前做过的所有 App 没什么两样。为了显得专业,我还画了完整的信息架构图:一级页面、二级页面、返回路径、空状态、加载占位符,一切看起来很严谨,甚至可以直接进入开发。
但这个方案我只花了一天就做出了 Demo,功能完全能跑。问题恰恰出在“能跑”上面——功能能跑,不代表产品逻辑是对的,更不代表用户愿意用第二次。Demo 跑通之后我拿给几个朋友试用,大部分人点了几下就退出了,只有一个朋友多问了一句“你这个智能问答,和我在浏览器里打开 AI 网页有什么区别?我为什么要装一个 App?”这句话直接把我问住了。
1.3 被用户的一句话打醒
朋友的问题让我反思了很久。如果 AI 只是一个聊天入口,用户确实完全不需要一个 App——网页、小程序、系统助手都能做到。App 的价值必须在于:它能替用户完成端到端的任务。用户说自己想去杭州玩两天,App 应该直接给出包含行程、天气、酒店建议、预约链接的完整方案,而不是只生成一段文字让他自己去搜。也就是说,用户要的不是一个“AI 按钮”,而是一份“AI 结果”。
这个认知直接推翻了我原来的全部设计。我开始意识到,做 AI 鸿蒙 App,不是在做“有 AI 功能的 App”,而是要做“AI 原生的 App”。后面所有逻辑上的调整,都是从这个点拉开的。产品经理视角的东西我原本不想写太多,但这次经历让我深刻明白:技术选型、架构设计、交互方案,全都是被这个产品定位牵着走的。定位先变了,后面的一切才跟着变。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第一个关键转变:产品信息架构从“页面树”变成“意图流”
2.1 传统 App 的信息架构,为什么在 AI 时代失效
传统 App 的信息架构,本质上是一棵“页面树”。用户进入首页,看到一个功能列表,点击一个功能,进入一个子页面;子页面里再分 Tab,再往里钻。每个页面负责一类功能,页面之间有清晰的层级关系和返回路径。这种结构的优点是稳定、可预测、开发成本低;缺点是用户必须“知道某个功能存在”,并且“愿意一层层找过去”。
拿旅行类应用举例,老产品的典型路径是:打开首页,找攻略频道,筛选目的地,阅读图文攻略,切换到酒店模块,搜索酒店,对比详情,最后再切回行程页自己串联。每一步都需要用户自己操作和判断,漏一步都不行。我做过的绝大多数 App 都是这么设计的,做得久了,甚至会下意识地认为“信息架构图 = 页面流程图”,但这个等式在 AI 时代不成立了。
2.2 鸿蒙意图框架带来的产品设计新思路
在 AI 原生应用里,用户不再需要知道你有多少个功能入口,他只需要说出自己要做什么事,系统去判断该调用哪些能力。原来的信息架构是“功能入口 → 页面 → 操作 → 结果”,新的信息架构是“用户意图 → 能力编排 → 执行 → 反馈”。页面不再是一等公民,能力才是一等公民,甚至很多能力不需要对应到独立页面,而是在对话流程中动态组装出来的卡片、表单、确认按钮。
鸿蒙的意图框架让这个变化变得更加顺理成章。系统层面本来就在做“意图分发”:用户表达想做什么事,系统把任务分发给合适的服务,而不是让用户去记每个应用的位置。所以我在写产品文档的时候,已经不是画页面流程图了,而是画“意图—能力—反馈”的链路图。刚开始很不习惯,但画了两版之后我发现,这才是 AI 应用该有的样子。
2.3 重写后的产品结构:一张对比表看清差异
我后来把原来的“五个入口页面”全部砍掉,只保留一个对话主界面,外加一个“任务看板”。对话界面负责理解用户意图,任务看板负责呈现 AI 正在执行和已经完成的任务。举个例子,用户说“我周五下班后去杭州,周六想逛博物馆和吃面,帮我排个两天行程”,系统会拆解这个请求:出发时间是周五晚、目的地是杭州、兴趣点是博物馆和面食、输出是一个两日行程。然后 AI 会按顺序调用天气数据、地图数据、餐饮推荐数据、博物馆预约数据,最后生成一张可交互的行程卡片。
用户在卡片上可以直接调整顺序、取消某个景点、重新生成,每一张卡片都是一个动态能力单元,而不是提前写死的页面。整个产品从“提供入口”变成了“执行意图”。我后来做了一份简单的对比表,方便团队快速理解两代产品的逻辑差异:
| 对比维度 | 传统 App | AI 原生 App |
|---|---|---|
| 信息架构 | 页面树:功能入口→页面→操作→结果 | 意图流:用户意图→能力编排→执行→反馈 |
| 交互模式 | 被动响应,等用户操作 | 主动提议,按需推送 |
| 核心数据 | 页面栈和页面参数 | 会话上下文和任务状态 |
| 反馈机制 | 点击后立即跳转 | 渐进式生成,分步反馈 |
| 状态管理 | 页面级 local state | 全局上下文流,跨设备同步 |
看这张表的时候我又确认了一遍:每一个环节的“主语”,都从“功能”变成了“用户”。
3. 第二个关键转变:交互逻辑从“用户找功能”变成“功能找用户”
3.1 从被动响应到主动提议
传统 App 是“被动响应式”交互,用户来了、点了、操作了,App 才动。而 AI 原生应用应该是“主动提议式”的:系统基于对用户意图和环境的理解,在合适的时机把能力推给用户。因为鸿蒙是分布式系统,应用可以感知到用户正在使用不同的设备,这给了我做主动提议的基础。
举一个我做过的实际功能:旅行助手检测到用户周五晚上还有未完成的行程确认,距离出发时间只剩两个小时,于是通过手表提醒一句“你的行程还未确认,是否现在就确认酒店”。这种主动触达,远比用户自己打开 App 找入口高效。但这里有个分寸问题:主动过头就是骚扰,所以我额外做了一个“打扰阈值”模块,把通知的紧急程度、用户当前所在场景(工作、休息、运动中)都纳入判断。这部分逻辑的复杂度,远超传统 App 里一个简单的 push 配置。
3.2 对话与界面的融合
另一个交互变化,是对话和界面的关系被改写了。以前对话是聊天框里的文字流动,界面是独立的操作区域,两者界限分明。在 AI 原生应用里,对话负责语义理解,界面负责精确操作,两者交替出现。用户说“把第二天的行程缩短两个小时”,这既是一个自然语言指令,也是对当前界面对象的操作请求。系统要先理解“第二天”指向哪张卡片,“缩短两个小时”要改动哪些行程项,然后生成新的界面状态。
AI 不是藏在某个输入框里,而是贯穿在每一次界面操作中。这种融合带来的工程要求是:UI 组件必须具备“可被 AI 操作”的能力。我开始给组件加语义化 ID,给状态加可解释字段,因为大模型需要知道界面上有什么、能改什么。这套做法在传统 App 里几乎没人会做,但 AI 原生应用里它是刚需。你甚至可以理解为,界面从一个给人看的成品,变成了一个“人和模型共享的操作面板”。
3.3 反馈时延和容错机制要重做
AI 生成结果需要时间,尤其是云端大模型流式输出时,可能好几秒都没有完整结果。传统 App 的反馈逻辑是“立即响应,马上跳转”,AI App 则要接受“不确定的等待”。我重新设计了反馈体系:用户发出指令后,界面立即进入“已理解意图”状态,然后显示解析过程中一个个关键节点,比如“正在查询天气数据”“正在生成行程”“等待酒店确认”。每完成一步,用户都能看到渐进式反馈,比一个转圈菊花要好太多。
容错方面也要重新想。传统页面操作出错,弹个 toast 说“请求失败,请重试”就完了,AI 应用不行。模型可能理解了意图但结果执行偏了,用户需要能纠正路径。所以我在每个生成结果后面都加了“重新生成”“修改条件”“人工纠偏”三个动作,这在传统 App 里是不存在的交互形式。本质上,AI 应用不能假设结果一定对,它需要给用户一个“和模型共同完成任务”的通道,而不是简单二分对与错。
4. 第三个关键转变:架构与数据流从“页面栈”变成“上下文流”
4.1 会话上下文成为 App 的“主数据”
传统 App 的页面栈,保存的是页面对象和页面间参数,数据流是“页面 A → 页面 B → 页面 A”的简单传参。AI 原生应用的架构重心变成了上下文:用户之前说过什么、做过什么、修正过什么,这些信息要跨页面、跨操作、甚至跨设备持续存在。
举一个我实际遇到的场景:用户先让 AI 规划了杭州两日游,然后又让 AI 帮忙写一封给同事的邮件。这两个任务看似无关,但在生成邮件的时候,用户说“提到我周五要去杭州,所以不方便参会”。如果系统没有保存之前的行程信息,这句话就无法正确执行。所以我把“会话上下文”提升到了架构核心层,专门设计了一个 SessionStore,负责保存多轮对话、用户画像、当前任务状态,以及这些状态之间的关系。传统 App 里每个页面维护自己的 local state,在这里根本不够用,全局状态管理成了第一优先级。
4.2 流式输出对 UI 状态的冲击
AI 接口返回是流式的,token 一个个往外蹦,UI 要跟着一帧帧更新。如果走传统页面局部刷新,高频更新会直接把主线程卡死。我第一版 Demo 就吃了这个亏,AI 文字输出的时候界面掉帧明显,用户体验很糟。
后来我做两个调整:一是流式增量数据先进队列,前端用帧回调合并更新,控制 UI 刷新频率,不再每个 token 都触发一次重绘;二是把“正在生成”状态从 UI 层抽离,放进单向数据流,UI 只在状态集合层面订阅更新。ArkTS 的响应式状态管理能力其实不弱,尤其是 @Observed 和 @State 的配合,能写出相对干净的状态流。但要注意,AI 流式场景和普通输入框绑定的更新频率完全不是一个量级,必须在设计状态模型时就把“高频增量更新”纳入考量,不然写着写着就全是补丁。
4.3 我采用的分层状态管理方案
我最终用的是一版比较务实的三层状态管理方案。第一层是会话原始层,保存完整的 Message 数组,包括用户输入、AI 输出、工具调用记录,这层只负责存储,不参与 UI 渲染。第二层是派生状态层,从会话中提取当前意图、正在执行的任务、可操作的卡片数据。第三层是视图状态层,把派生状态映射成具体 UI 需要的数据结构,比如列表项、加载状态、错误提示。核心思想是:不要让 UI 直接依赖 AI 的原始输出,而是让 AI 输出先沉淀成结构化数据,再喂给 UI。
代码上就是典型的分层 Store,结构大致这样:
arkts复制@Observed
class SessionStore {
messages: Message[] = []; // 原始会话数据
currentIntent: string = ''; // 当前意图
tasks: TaskInstance[] = []; // 正在执行/已完成的任务
progressMap: Record<string, number> = {}; // 任务进度
appendMessage(msg: Message) {
this.messages.push(msg);
this.deriveState();
}
deriveState() {
// 从 messages 中提取 currentIntent、tasks、progressMap
}
}
实际业务比这个复杂不少,但分层思想是一样的。这样做的好处是数据回溯、调试、多端同步都更方便:用户从手机切到平板时,只需要同步第一层原始数据,再由各端自行派生渲染,不同设备上看到的状态最终会收敛到一致,而不是每一帧都强绑在一起。
4.4 多设备上下文同步的取舍
鸿蒙的特色是多设备协同,但这也直接把上下文同步的复杂度拉高了。我一开始很天真地想做到“全设备实时同步”,后来发现完全没必要,成本高、收益低、功耗还不友好。最终我采用的是“按需同步”:重要的结构化上下文异步同步到分布式数据库,流式输出只保留在当前设备,等任务结束之后生成摘要,再把摘要同步过去。
用户从一个设备切换到另一个设备时,需要的是任务结果和进度,而不是中间过程的每一个 token。我实现了两种同步模式,一种是“即时快照”,适合短时频繁切换;另一种是“延迟摘要”,适合低功耗场景。这是我做 AI 原生开发后,思维转变最大的地方:不再追求所有状态实时一致,而是按用户价值决定同步粒度。
5. 踩坑实录:真正做起来,卡住你的都是这些细节
5.1 端侧模型加载的时机
做鸿蒙 AI App,绕不开的问题是“哪些能力放端侧、哪些走云侧”。小模型的意图识别、关键词抽取适合端侧,大模型生成能力走云侧。但端侧模型文件不小,加载时间可能达到秒级。我最初在应用启动时直接加载端侧模型,结果冷启动时间从 1 秒涨到 5 秒,用户还没看到界面就跑了。
后来改成“启动时只加载一个极小的意图分类模型,用户真正进入 AI 功能时才加载完整模型”,同时展示加载进度,体感好了非常多。这里有个关键经验:端侧模型必须有完整的生命周期管理,包括预加载、懒加载、内存回收。鸿蒙强调多设备任务驻留,一个 App 长时间占着大量内存,会导致系统频繁 GC,反而让你的 AI 功能更卡。建议用 profiling 工具盯着内存曲线调,别只看功能通不通。
5.2 上下文无限膨胀和 Token 裁剪
多轮对话最大的隐藏问题,是历史消息越积越多,最后直接把上下文窗口撑爆、接口响应变慢、费用翻倍。我第一版不管三七二十一,把所有历史文本拼进 prompt,结果第 20 轮对话之后明显迟滞。后来我实现了一套裁剪策略:按重要性给历史消息打分,系统指令、用户最新输入、关键结果这些必须保留,早期过程性对话能合并就合并;同时用滑动窗口限制上下文长度,超过阈值时就把更早的消息压缩成摘要,再塞回上下文。
Token 计算也得注意中英文差异。同一个意思,英文可能 100 个 token,中文可能不到 50 个,所以阈值不能只看字符数,要按实际 tokenizer 统计。我在日志里记录每次请求的 prompt token 数,用几百轮对话样本跑出适合自己场景的阈值,再加上 20% 缓冲区,才最终稳定下来。这个坑特别隐蔽,不真正跑个几百轮对话根本发现不了。
5.3 权限模型对 AI 能力的实际制约
AI App 对信息的渴望是天然的。要真正做到“懂用户”,最好能读取地理位置、日程、通知、健康数据。但鸿蒙和所有现代系统一样,权限管理越来越严格,用户一旦拒绝,AI 的感知能力就残了一条腿。这个问题必须在设计阶段就考虑好。
我为此做了一个“能力感知”组件:启动时检查各项权限状态,动态决定哪些 AI 能力可用。比如没有日历权限时,AI 不能自动把行程写进日历,那就改成输出一个“手动添加到日历”的按钮;没有位置权限时,不启动自动定位,而是让用户手动输入城市。一开始我觉得这是体验降级,后来发现它反而是一种信任建设。用户愿意给多少权限,就享受多少智能,这种透明的梯度设计,比一上来吓人的权限弹窗更让人接受。同时我在界面角落放了一个 AI 能力开关状态的提示卡片,用户随时能知道当前哪些能力可用、为什么不可用。
5.4 调试工具链和 AI 场景的摩擦
实事求是地说,鸿蒙开发工具这两年进步很大,但面对 AI 应用这种强异步、高 IO 的场景,调试体验依然有摩擦。最折磨人的是流式回调里打断点——断点一打,消息连接滞积,恢复执行后消息顺序直接乱掉,报错都复现不了。我的应对方式很直接:所有 AI 调用都带 traceId,把用户意图、prompt 摘要、响应耗时、token 数完整打印进日志面板。排查问题先看日志链路,确认到具体环节再决定要不要断点,断点也只加在纯函数计算位置,不放在流式回调里。
这套日志系统,我建议所有做 AI App 的人都提前搭好,别等出问题再补。另外提醒一点,ArkTS 对动态类型和 unknown 的管控比较严格,AI 接口返回的 JSON 结构又变化多端,前期在类型转换上我踩了不少坑。建议做一个统一的 JSON 解析层,把所有模型返回先映射成强类型结构体,再进业务逻辑,能省下大量后续排查时间。
做完这个 AI 鸿蒙 App,我最大的认知变化是:真正“变了”的不是技术栈,不是 Kotlin 换成了 ArkTS,也不是工程里多了一个模型 SDK,而是整个思考方式变了。以前做 App,我总在想“我提供什么功能,用户怎么学会用”;现在做 App,我只会想“用户想要什么结果,我怎样帮他把路走完”。这种由“功能中心”到“意图中心”的迁移,是我觉得未来几年做应用开发都躲不开的课题。
如果你也想做类似的方向,我的建议是:别急着把一个模型接进老 App 里假装转型,先找一个真实的任务场景,从零走一遍“意图—能力—反馈”的完整链路。哪怕功能少一点,也要让它成为浑然一体的 AI 原生体验。做完之后你就会明白,那种设计和给旧应用加一个 AI 按钮,完全是两码事。
