美食生活助手:基于 HarmonyOS ArkTS 的 AI 菜谱推荐应用开发实践
美食生活助手:基于 HarmonyOS ArkTS 的 AI 菜谱推荐应用开发实践
一、项目背景与问题定义
1.1 厨房里的"选择困难症"
每个人在做饭前都经历过这样的时刻:打开冰箱,看着零零散散的食材,脑子里一片空白——"今天吃什么?"这个看似简单的问题,却困扰着无数家庭厨师。
从技术角度看,这个问题背后有三个核心难点:
第一,决策疲劳。现代人每天面临大量决策,到了做饭时间,大脑的处理能力已经接近耗尽。面对冰箱里有限的食材,用户需要的是一个能快速给出建议的"军师",而不是一个需要自己主动搜索的搜索引擎。
第二,信息过载。下厨房、美食杰等传统菜谱平台,虽然菜谱数量庞大,但用户需要先"知道自己想吃什么"才能搜索。当用户处于"不知道吃什么"的状态时,这些平台提供的价值有限——它们解决的是"怎么做"的问题,而不是"做什么"的决策。
第三,菜谱质量参差不齐。互联网上的菜谱大多来自用户投稿,食材用量模糊(“适量”“少许”),步骤描述不清晰,缺少关键的烹饪技巧提示。对于烹饪新手来说,按这样的菜谱操作,成功率很低。
针对这三个痛点,我们用 HarmonyOS ArkTS 开发了「美食生活助手」——一款专注于"帮你决定吃什么"和"教你做好一道菜"的 AI 对话应用。它覆盖六类美食场景,根据用户输入动态切换"推荐模式"和"菜谱模式",提供从决策到执行的完整烹饪指导。
1.2 技术选型:为什么选择 HarmonyOS
美食助手是一个典型的"AI 对话 + 内容展示"应用,理论上可以用任何前端框架开发。我们选择 HarmonyOS + ArkTS 的技术栈,基于以下四个考量:
第一,声明式 UI 对长文本内容的天然友好。菜谱推荐和菜谱详情都是长文本内容,需要良好的排版支持。ArkTS 的Text组件原生支持 Markdown 风格的富文本渲染(粗体、换行、emoji),不需要额外引入富文本库,就能实现清晰的排版效果。
第二,零依赖的纯原生实现。整个应用约 500 行代码,没有引入任何第三方 npm 包。安装包体积不足 5MB,冷启动时间低于 300ms。对于工具类应用来说,轻量本身就是竞争力。
第三,冷启动场景的体验要求。用户打开美食助手的场景通常是"在厨房里,马上要做饭了"。这个场景下,应用慢一秒打开,用户的耐心就少一分。HarmonyOS 原生应用的启动速度远优于 WebView 方案,能在用户打开冰箱的瞬间完成加载。
第四,多端扩展的前瞻性。HarmonyOS 的分布式能力为未来在平板、智慧屏等设备上运行预留了空间。想象一下,在厨房的智慧屏上打开美食助手,跟着步骤一步步做菜——这个场景的体验会远好于在手机上切换应用。
1.3 项目概览
| 维度 | 数据 |
|---|---|
| 开发语言 | ArkTS(声明式 UI) |
| 架构模式 | MVVM(Model-View-Service) |
| 核心代码量 | 约 500 行 |
| 美食分类 | 6 个(随便推荐/中式/西式/素食/低卡/烘焙) |
| 回复模式 | 2 种(推荐模式 + 菜谱模式) |
| 具体菜谱 | 3 道精确到克的详细菜谱 |
| 第三方依赖 | 0 个 |
| 页面数量 | 1 个(单页对话) |
| 安装包大小 | < 5MB |
| 冷启动时间 | < 300ms |
二、产品设计:两种回复模式的智能切换
2.1 推荐模式 vs 菜谱模式
美食助手的产品设计核心在于两种回复模式的智能切换。这是它区别于传统菜谱应用的关键差异。
推荐模式:当用户选择话题标签(如"随便推荐"“中式料理”)或输入类似"不知道吃什么"的内容时,AI 返回 4-5 道菜的推荐列表。每道菜的推荐包含四个要素:
菜名 → 推荐理由 → 简易做法概要 → 所需食材清单这种结构化的推荐格式,让用户快速浏览多道菜,在比较中做出选择。推荐理由给用户一个"为什么选这道菜"的心理锚点,做法概要让用户判断"这道菜我做得了吗",食材清单让用户检查"我有没有这些食材"。
菜谱模式:当用户输入具体的菜名(如"番茄炒蛋"“红烧肉”“牛排”)时,AI 返回完整详细的菜谱,包含:
食材清单(精确到克)→ 分步做法 → 烹饪技巧提示 → 营养价值说明菜谱模式的特点是精确、可执行、有深度。食材用量精确到克,不做"适量"“少许"这种模糊描述;每个步骤标注了预计时间,让用户有心理预期;烹饪技巧提示是"菜谱之外的加分项”,是经验丰富的厨师才知道的细节;营养价值说明为用户提供健康参考。
2.2 系统提示词:产品灵魂的代码化
美食助手的系统提示词是产品设计文档的代码化表达。它用不到 200 字定义了产品的核心行为:
你是一个美食生活助手,精通中西方烹饪,了解各地饮食文化。 你的任务是根据用户需求推荐菜品或提供菜谱: 1. 如果用户说"不知道吃什么",根据用户口味偏好、食材库存或季节推荐3-5道菜 2. 如果用户点明具体菜名,提供详细的做法步骤 3. 考虑 dietary restrictions(素食、过敏、低卡等) 回答结构: - 推荐菜品时:菜名 → 推荐理由 → 简易做法概要 → 所需食材清单 - 提供菜谱时:食材清单(精确到克)→ 分步做法 → 烹饪技巧提示 → 营养价值说明 回复风格:热情、生动,让用户感受到美食的诱惑,但步骤要清晰可执行。这段提示词的设计有几个值得注意的决策:
第一,角色定义精准。“美食生活助手"而非"AI 厨师”——这个定位意味着应用是"助手"而非"替代者"。助手帮助你做决策、提供参考,但最终做菜的人还是你。这个定位避免了用户对 AI 产生不切实际的期望。
第二,任务边界清晰。两条核心任务(推荐 + 菜谱)用 if-else 逻辑明确定义,没有模糊地带。这确保了回复的一致性——无论用户以什么方式表达"不知道吃什么",都能被引导到推荐模式。
第三,考虑了特殊需求。第三条明确提到了 dietary restrictions(饮食限制),这意味着素食者、过敏者、减脂者也能获得适合自己的推荐。这是一个容易被忽略但实际影响很大的设计点。
第四,格式要求结构化。两种回复模式都有明确的输出格式定义。这不是为了限制 AI 的创造性,而是为了确保用户无论何时发起请求,都能获得一致的、可预期的回答结构。格式的一致性直接转化为用户的可信度。
第五,风格要求平衡了"热情"和"可执行"。这是最难把握的平衡——太热情显得浮夸,太干巴显得冷漠。提示词用"热情、生动,但步骤要清晰可执行"来约束,既给了 AI 发挥的空间,又划定了底线。
2.3 六类美食场景的覆盖策略
六个话题标签的设计不是随机的,而是经过分类组合的:
| 话题 | 覆盖场景 | 用户画像 |
|---|---|---|
| 🎲 随便推荐 | 选择困难、无偏好 | 所有用户 |
| 🥢 中式料理 | 日常家常菜 | 中式饮食习惯用户 |
| 🍝 西式料理 | 换口味、周末大餐 | 追求新鲜感的用户 |
| 🥬 素食 | 饮食限制、健康选择 | 素食者、弹性素食者 |
| 🥗 低卡健康 | 减脂、健身、轻食 | 健身人群、减脂用户 |
| 🍰 烘焙甜品 | 甜品、烘焙兴趣 | 烘焙爱好者 |
这六个话题的设计遵循了MECE 原则(Mutually Exclusive, Collectively Exhaustive,即相互独立、完全穷尽):
- "随便推荐"覆盖了"无明确偏好"的场景
- "中式/西式"按菜系覆盖了地域偏好
- "素食/低卡"按饮食限制覆盖了特殊需求
- "烘焙甜品"覆盖了非正餐场景
六个话题的组合,基本覆盖了"一个人在家做饭"可能遇到的所有决策场景。用户不需要思考"我该选哪个分类",因为分类本身就是对用户需求的引导。
2.4 菜谱内容设计:从"能看"到"能用"
菜谱的质量决定了应用的核心价值。我们为三道经典菜谱(番茄炒蛋、红烧肉、香煎牛排)设计了精确到克的详细做法,每条菜谱包含四个模块:
食材清单:精确到克,附带"约X汤匙""约X小勺"的日常换算,让没有厨房秤的用户也能操作。
分步做法:每步标注预计时间,关键的步骤有 ⚠️ 警告标记(如"炒糖色不要过头"“热水不能用冷水”),这些是新手最容易犯错的地方。
烹饪技巧提示:4-5 条经验性建议,每条都是"菜谱上不会写但老厨师都知道"的干货。比如"番茄选熟透的"“焯水后用温水洗肉”“煎牛排后静置5分钟再切”。
营养价值说明:热量、蛋白质、微量元素等关键数据,帮助用户了解这道菜的营养构成。
以红烧肉为例,这条菜谱揭示了几个关键烹饪原理:
- 焯水后必须用温水洗肉(冷水会导致肉质收缩变硬)
- 炖肉必须加热水(冷水会让蛋白质凝固,炖不烂)
- 小火慢炖 1 小时是底线,急不得
- 收汁时不能离开,最后阶段最容易糊锅
这些技巧不是 AI 从菜谱网站上"复制"来的,而是烹饪经验的总结。在接入真实大模型后,这些内容将由 AI 根据菜名动态生成,而不需要手动编写。
三、架构设计:MVVM 在美食场景中的实践
3.1 整体架构
美食助手延续了我们在 HarmonyOS 项目中积累的 MVVM 简化架构——Model-View-Service 三层结构。这种架构在中小型应用中经过验证,在开发效率和代码可维护性之间取得了良好平衡。
┌─────────────────────────────────────────┐ │ View (视图层) │ │ Index.ets — 声明式 UI + 状态管理 │ │ 304 行,包含 5 个 @Builder 方法 │ ├─────────────────────────────────────────┤ │ Service (服务层) │ │ FoodAssistantService.ets — 业务逻辑 │ │ - 系统提示词管理(200 字) │ │ - 6 类话题回复 + 3 道详细菜谱 │ │ - Map 关键词匹配引擎 │ │ - AI 调用封装(降级策略) │ ├─────────────────────────────────────────┤ │ Model (模型层) │ │ FoodAssistantModel.ets — 数据结构 │ │ - ChatMessage / TopicOption │ │ - MessageRole / FoodTopic 枚举 │ │ - RecipeRecommendation 菜谱推荐模型 │ ├─────────────────────────────────────────┤ │ Constants (常量层) │ │ Constants.ets — 全局配置 │ │ - 暖橙美食配色系统 │ │ - 6 个话题常量 │ │ - 欢迎文案 / 加载文案 │ └─────────────────────────────────────────┘这种分层的核心优势在于关注点分离:
- View 层只关心 UI 渲染和用户交互,不包含任何业务逻辑
- Service 层封装了所有业务规则(提示词、回复匹配、AI 调用),完全不涉及 UI
- Model 层定义了各层之间的数据"契约"
- Constants 层集中管理所有可配置项,避免散落在代码中的魔法值
当未来需要接入真实大模型时,只需修改 Service 层的callAI()方法。当需要调整配色时,只需修改 Constants 中的颜色值。每一层的变化不会影响其他层。
3.2 为什么不用更复杂的架构
在项目启动阶段,我们也考虑过几种常见方案。但最终选择 MVVM 简化架构的原因很简单:架构的复杂度应该与项目的复杂度匹配。
对于大约 500 行代码的单页面应用,引入 Redux 状态管理、Clean Architecture 多层抽象、或者 RxJS 响应式编程,都属于"过度设计"。这些架构在大型项目中能显著提升可维护性,但在小型项目中反而会增加理解和修改的成本。
我们遵循一个原则:先让代码跑起来,再在需要的地方引入抽象,而不是一开始就构建一个"完美"的架构。随着项目的发展,如果后续需要多页面管理、登录鉴权、数据持久化等功能,可以逐步引入更复杂的架构模式。这种"渐进式"的架构演进,比"一步到位"的重型架构更适合中小型项目。
3.3 目录结构
entry/src/main/ets/ ├── common/ │ └── Constants.ets # 全局常量:配色、文案、话题定义 ├── models/ │ └── FoodAssistantModel.ets # 数据模型:消息、话题、菜谱推荐 ├── services/ │ └── FoodAssistantService.ets # 业务服务:系统提示词、回复匹配、AI 调用 └── pages/ └── Index.ets # 主页面:对话界面 + 全部交互逻辑目录结构刻意保持扁平,只有四个文件。每个文件的职责单一且明确,打开任何一个文件都能立刻知道它的作用。对于这个规模的项目,深层嵌套的目录反而会增加导航成本。
四、Model 层:数据模型的设计
4.1 为什么用 class 而不是 interface
在 ArkTS 中,定义数据结构有两种方式:interface和class。我们选择了class,原因有三:
第一,构造函数带来的便利。消息对象需要在创建时自动记录时间戳,用 class 的构造函数可以一行代码完成:
exportclassChatMessage{role:MessageRole content:stringtimestamp:numberconstructor(role:MessageRole,content:string){this.role=rolethis.content=contentthis.timestamp=Date.now()}}调用方不需要关心时间戳的生成逻辑,new ChatMessage(...)即可。如果用 interface,每次创建都要手动写{ role: ..., content: ..., timestamp: Date.now() },重复且容易遗漏。
第二,类型安全。class 创建的实例支持instanceof运行时检查,虽然当前项目中没有用到,但保留这个能力成本很低,未来可能有用。
第三,风格一致性。ArkTS 是面向对象的语言,Service 是 class,UI 组件是被@Component装饰的 struct,数据模型用 class 保持整体风格统一。
4.2 枚举的三大价值
项目中有两个枚举:MessageRole和FoodTopic。用枚举而不是字符串字面量的好处在之前的项目中已经验证过,这里只做简要总结:
防拼写错误。MessageRole.USR编译不通过,'usr'编译通过但运行时才发现问题。编译期拦截 > 运行时排查。
IDE 智能提示。输入MessageRole.之后,IDE 自动列出所有可用值,不需要记忆。
重构友好。改枚举定义一处,全局自动生效。散落的字符串改起来需要全局搜索替换,容易漏改误改。
4.3 RecipeRecommendation:为菜谱推荐定制的数据模型
美食助手特有的一个数据模型是RecipeRecommendation:
exportclassRecipeRecommendation{dishName:stringreason:stringsummary:stringingredients:stringconstructor(dishName:string,reason:string,summary:string,ingredients:string){this.dishName=dishNamethis.reason=reasonthis.summary=summarythis.ingredients=ingredients}}这个模型对应了"推荐模式"回复的四个要素:菜名、推荐理由、做法概要、食材清单。虽然当前版本中,推荐内容是以纯文本形式嵌入在回复字符串中的,但定义了RecipeRecommendation类为未来的结构化数据处理预留了空间。
假设未来要接入真实大模型,AI 返回的推荐内容很可能是一个 JSON 数组,每条推荐包含菜品名称、推荐理由、做法步骤、食材用量等结构化字段。到那时,RecipeRecommendation这个模型就能直接承载这些数据,Service 层将其转换为富文本卡片,View 层渲染出更美观的菜品卡片——而不是现在的一大段纯文本。
这就是面向未来设计的含义:今天多加一个类,未来少改很多代码。
五、Service 层:核心匹配引擎与 AI 调用封装
5.1 Map 匹配引擎的设计
美食助手的 Service 层使用Map<string, string>实现关键词匹配。这个设计看似简单,但有几个关键决策:
第一,匹配顺序决定优先级。Map 的遍历顺序是插入顺序,因此可以通过调整initMockResponses()中set的调用顺序来控制匹配优先级。例如,具体菜名(“番茄炒蛋”“红烧肉”“牛排”)放在话题分类(“中式料理"等)之后插入,但话题分类又放在"默认"之前。这个顺序确保了:如果用户输入"番茄炒蛋”,优先匹配到详细菜谱,而不是"中式料理"的推荐列表。
第二,first-match 策略。找到第一个匹配就返回,不继续搜索。这个策略简单、高效、可预测。如果每次都遍历所有 key 找"最佳匹配",不仅增加复杂度,而且"最佳"的定义本身就模糊——是"最长匹配"还是"最多匹配"?first-match 避免了这种模糊性。
第三,默认兜底。如果所有关键词都不匹配,返回一个引导性的默认回复。这个回复不是"我听不懂"这种挫败感反馈,而是列出了用户可以尝试的几种方式——点击话题标签、说出具体菜名、描述口味偏好。这种设计把"我不知道"变成了"我帮你试试这些方法",保持了对话的延续性。
5.2 关键词匹配的局限性
关键词匹配有明显的局限性——它不理解语义,只做字面匹配。比如用户输入"我想吃番茄炒鸡蛋",不会匹配到"番茄炒蛋"(因为字符串不完全包含)。用户输入"做份红烧排骨",不会匹配到"红烧肉"。
这些局限性在当前版本中是可以接受的,因为:
- 话题标签提供了确定性的入口——用户点击标签不会受到匹配问题的影响
- 默认兜底回复引导用户说出更明确的需求
- 未来接入真实大模型后,关键词匹配将作为降级方案保留,主流走 AI 语义理解
5.3 降级策略:从 Mock 到 AI 的平滑过渡
generateReply方法使用了 try-catch 降级策略:
asyncgenerateReply(userMessage:string):Promise<ChatMessage>{try{constreplyContent=awaitthis.callAI(userMessage)returnnewChatMessage(MessageRole.ASSISTANT,replyContent)}catch(e){returnthis.generateMockReply(userMessage)}}这个设计有两个作用:
开发阶段:callAI方法目前直接返回模拟回复,让整个应用不依赖外部服务就能跑起来。UI 开发、交互调试、功能验证都不受 AI 接口进度的影响。
生产阶段:当接入真实大模型后,如果 AI 服务出现网络错误、限流、超时等异常,会自动降级到本地模拟回复。用户不会看到错误页面,体验是连贯的。
这种"前后端分离 + Mock 驱动"的开发模式在 Web 开发中已经非常成熟,但在移动端开发中还没有被普遍采用。我们把这种实践引入到 HarmonyOS 开发中,显著提升了开发效率。
5.4 系统提示词的管理
系统提示词存储在 Service 类的私有属性中,而不是 Constants 文件中。这个决策有一个考量:提示词是业务逻辑的一部分,不是界面常量。提示词的内容、结构、措辞直接影响 AI 的回复质量,应该和业务逻辑放在一起,方便开发者在修改 Service 代码时同步调整提示词。
如果未来需要支持多语言或 A/B 测试不同的提示词版本,可以将提示词抽象为独立的配置,但当前项目规模下,放在 Service 类中是最简洁的选择。
六、View 层:声明式 UI 的实践
6.1 页面结构概览
整个应用只有一个页面 [Index.ets](file:///c:/Users/l/DevEcoStudioProjects/MyApplication/entry/src/main/ets/pages/Index.ets),约 300 行代码,分为四个区域:
┌───────────────────────────────────────┐ │ 🍳 美食生活助手 [清除对话] │ ← Header │ 精通中西烹饪 · 让每顿饭都成为享受 │ ├───────────────────────────────────────┤ │ 🎲随便推荐 🥢中式 🍝西式 🥬素食 ... │ ← TopicBar(横向滚动) ├───────────────────────────────────────┤ │ │ │ 🍳 美食助手 │ ← 消息列表 │ 你好!我是你的美食生活助手... │ │ │ │ ┌──────────────┐ │ │ │ 番茄炒蛋 │ │ │ └──────────────┘ │ │ │ │ 🍳 美食助手 │ │ 🍳 番茄炒蛋 · 详细菜谱 │ │ 📋 食材清单(精确到克):... │ │ │ │ [正在为你准备美味方案...] │ ← 加载状态 │ │ ├───────────────────────────────────────┤ │ [不知道吃什么?...] [发送] │ ← InputArea └───────────────────────────────────────┘四个区域对应四个@Builder方法,build()方法作为总目录,清晰展示页面结构。
6.2 @State:响应式状态管理
页面使用四个@State装饰的变量管理所有动态数据:
| 状态变量 | 作用 | 影响的 UI |
|---|---|---|
messages: ChatMessage[] | 对话消息列表 | 消息气泡数量与内容(核心状态) |
inputText: string | 输入框文本 | 输入框内容、发送按钮颜色和启用状态 |
isLoading: boolean | 加载状态 | 加载气泡显示/隐藏、发送按钮禁用 |
selectedTopic: string | 选中话题 | 话题标签的选中/未选中样式 |
数据修改 → UI 自动更新,这是 ArkTS 声明式 UI 的核心范式。开发者只需关注数据逻辑,不需要手动操作 DOM 或调用刷新方法。
6.3 暖橙配色:美食主题的视觉表达
美食助手的配色方案以暖橙色(#EA580C)为主色调,配合浅米色背景(#FFF7ED)和橙色边框(#FED7AA),整体呈现温暖、诱人的视觉感受。
配色选择遵循了美食类应用的视觉惯例:
暖色系激发食欲。橙色和红色是餐饮行业最常用的颜色,研究表明暖色系能刺激食欲。这不同于我们之前开发的生活管家(蓝色系,传达信任感)和健康顾问(绿色系,传达安全感),每个应用都有自己独特的色彩语言。
60-30-10 法则。60% 浅米色背景(#FFF7ED),30% 白色卡片和深色文字,10% 橙色强调(按钮、选中态、标题)。这种比例让界面看起来温暖但不刺眼,层次分明但不杂乱。
高对比度保证可读性。深色文字(#1E293B)在浅色背景上有足够的对比度,确保菜谱长文本的可读性。辅助文字(#64748B)降低对比度,用于不重要的信息。
6.4 话题标签的交互设计
话题栏使用横向滚动的Scroll+Row组合,包含 6 个话题标签。选中态从浅米色变为橙色,文字从灰色变为白色,视觉反馈清晰明确。
话题标签的交互流程是:点击标签 → 自动填入话题关键词 → 自动发送 → 匹配对应回复。用户不需要手动输入文字,一键即可获得推荐。这个设计降低了使用门槛,也在无声地告诉用户:“我擅长这些领域,你可以这样问我。”
6.5 消息气泡的细节打磨
消息气泡是对话应用的核心 UI 元素,看似简单,实则有很多细节:
左右分布。用户消息在右侧(橙色),AI 回复在左侧(白色),符合行业通用设计模式。
最大宽度 85%。避免气泡撑满屏幕,两边留出呼吸空间。
圆角 12vp + 阴影。圆角让气泡柔和,轻微阴影(radius: 4, offsetY: 2, color: rgba(0,0,0,0.06))增加层次感。
行高 22 + 内边距 12。在视觉舒适度和信息密度之间找到平衡点,确保菜谱长文本的阅读体验。
6.6 输入区的交互细节
按钮状态联动。发送按钮的颜色和启用状态与输入框内容绑定——空输入时灰色不可点击,有内容时橙色可点击。额外绑定!isLoading条件,AI 回复中禁止发送新消息。
字数限制。maxLength(500)限制输入长度。500 字足以描述清楚一个菜名或口味偏好。
圆角 20vp。与整体设计语言保持一致,柔和现代。
6.7 自动滚动到最新消息
发送消息后和 AI 回复完成后,各触发一次scrollEdge(Edge.Bottom),确保用户始终看到最新内容。使用setTimeout延迟 200ms,给 UI 渲染留出时间,避免在气泡还没渲染出来时就滚动导致位置不对。
七、核心技术亮点
7.1 双模式回复的智能切换
美食助手最核心的技术亮点是"推荐模式"和"菜谱模式"的智能切换。这不是两个独立的页面或功能,而是同一个对话流中,根据用户输入动态切换的两种回复策略。
实现原理很简单——关键词匹配。当用户输入包含"随便推荐"“中式料理"等话题关键词时,返回推荐列表;当用户输入包含"番茄炒蛋”"红烧肉"等具体菜名时,返回详细菜谱;当用户输入不匹配任何关键词时,返回引导性默认回复。
这个看似简单的匹配逻辑,支撑了一个完整的用户决策流程:
用户感到选择困难 → 点击"随便推荐" → AI 返回 5 道菜推荐 → 用户对其中一道感兴趣 → 输入"番茄炒蛋" → AI 返回精确到克的详细菜谱 → 用户跟着步骤做菜每一步都有明确的引导,用户不会感到迷茫。这种"由粗到细、由推荐到执行"的渐进式对话流,是产品设计中最重要的决策。
7.2 零第三方依赖的纯原生实现
整个应用没有引入任何 npm 包、第三方组件库或网络请求框架。所有功能都用 HarmonyOS SDK 自带 API 实现。
收益:
- 安装包不足 5MB,用户秒下载
- 冷启动低于 300ms,打开即用
- 零兼容性风险,不依赖第三方版本更新
- 零维护成本,没有
package.json要管理
代价:
- 某些高级功能需要自己实现(如 Markdown 解析、网络请求封装)
- 但在这个项目规模下,代价几乎为零
7.3 设计系统与常量管理
所有颜色、文案、话题定义集中在 [Constants.ets](file:///c:/Users/l/DevEcoStudioProjects/MyApplication/entry/src/main/ets/common/Constants.ets) 中统一管理。改主色调只改一个值,全局自动生效。这个实践在多个项目中持续验证了其价值——好的工程实践是跨项目复用的。
7.4 完整的降级策略
generateReply的 try-catch 降级机制,不仅在开发阶段实现了前后端并行开发,在生产阶段也保障了用户体验的连续性。AI 服务不可用时,自动降级到本地模拟回复,用户不会感知到故障。
7.5 精确到克的菜谱设计
三条详细菜谱(番茄炒蛋、红烧肉、香煎牛排)的食材用量精确到克,附带了日常换算(“约 1 汤匙”“约 半小勺”)。这种"精确 + 直观"的双重表述,兼顾了严谨性和可操作性。
八、开发经验与踩坑总结
8.1 坑一:Map 的遍历顺序依赖
问题现象:在某些情况下,Map.keys()返回的迭代器顺序与预期不一致,导致匹配结果不符合预期。
原因分析:ArkTS 的 Map 在规范上保证遍历顺序为插入顺序,但forEach和keys()迭代器的行为略有不同。我们使用了keys()迭代器 +next()手动遍历,确保了顺序的确定性。
解决方案:显式使用keys()迭代器遍历,而不是forEach,确保 first-match 策略的确定性。
经验教训:在依赖顺序的场景中,使用显式的迭代器比使用forEach更可控。forEach的回调函数执行顺序虽然也遵循插入顺序,但显式迭代器的语义更清晰。
8.2 坑二:Markdown 在 Text 组件中的渲染差异
问题现象:菜谱回复中使用了**粗体**、---分隔线、>引用等 Markdown 语法,在不同版本的 HarmonyOS 上渲染效果不一致。
原因分析:ArkTS 的Text组件对 Markdown 的支持程度因系统版本而异。部分旧版本不支持---分隔线或>引用块的渲染。
解决方案:使用 emoji 和空行代替 Markdown 分隔线,用视觉符号(如📋、👨🍳、💡、📊)代替 Markdown 的标题层级。这样即使 Markdown 渲染不完整,纯文本的视觉效果也不会差。
经验教训:在跨版本的移动端开发中,不要过度依赖富文本渲染能力。用 emoji 和符号构建的"伪富文本"在兼容性上远优于 Markdown。
8.3 坑三:emoji 的跨平台一致性
问题现象:不同设备上,同一个 emoji 的显示效果不同(颜色、大小、甚至形态)。
原因分析:emoji 的渲染依赖于系统的字体和渲染引擎,不同厂商、不同版本的 HarmonyOS 可能有不同的 emoji 实现。
解决方案:选择最通用的 emoji 符号(如 🍳 🥢 🍝 🥬 🥗 🍰),避免使用较新的、可能不被支持的 emoji。在关键位置(如话题标签),emoji 的作用是辅助识别,即使显示效果有差异,文字标签仍然能保证功能的可用性。
经验教训:emoji 是"增强体验"而非"核心功能"。不要依赖 emoji 传达关键信息,始终保留文字标签作为备选。
8.4 坑四:ForEach 的 key 稳定性
问题现象:在消息列表中插入新消息时,旧的 key 可能因为索引变化而失效,导致列表重新渲染。
解决方案:使用${index}_${msg.timestamp}作为 key,既保证唯一性(时间戳毫秒级),又保持稳定性(同一条消息的时间戳不变)。
经验教训:ForEach的 key 生成函数是性能优化的关键。好的 key 能显著减少不必要的重新渲染,尤其在消息量大的场景下。
九、完整对话示例
场景一:选择困难,寻求推荐
用户点击「🎲 随便推荐」
系统自动发送:“随便推荐”
AI 回复(推荐模式):
🍳 今天为你推荐这几道美味: --- 1. 番茄炒蛋 🥚🍅 > 推荐理由:国民家常菜第一名!酸甜开胃、营养均衡,10分钟就能上桌。 > 做法概要:鸡蛋打散炒熟盛出 → 番茄切块炒出汁 → 鸡蛋回锅翻炒 → 加盐糖调味出锅 > 食材清单:鸡蛋3个、番茄2个、葱花少许、盐、糖、食用油 2. 蒜蓉西兰花 🥦 > 推荐理由:清爽脆嫩,蒜香浓郁,是一道完美的低卡配菜。 > 做法概要:西兰花焯水一分钟 → 蒜末爆香 → 下西兰花快炒 → 加盐蚝油调味 > 食材清单:西兰花1颗、大蒜5瓣、蚝油、盐、食用油 3. 可乐鸡翅 🍗 > 推荐理由:甜咸适口、肉质嫩滑,新手也能做出惊艳效果。 > 做法概要:鸡翅划刀腌制 → 煎至两面金黄 → 倒入可乐和酱油 → 大火收汁 > 食材清单:鸡翅中8个、可乐1罐、生抽、老抽、姜片、料酒 4. 酸辣土豆丝 🥔 > 推荐理由:酸辣脆爽,下饭神器!成本不到5块钱。 > 做法概要:土豆切丝泡水去淀粉 → 干辣椒花椒爆香 → 土豆丝大火快炒 → 加醋盐调味 > 食材清单:土豆2个、干辣椒、花椒、醋、盐、蒜 5. 紫菜蛋花汤 🍜 > 推荐理由:简单快手,清淡鲜美,搭配任何菜都合适。 > 做法概要:水烧开 → 放入紫菜 → 淋入蛋液 → 加盐香油葱花 > 食材清单:紫菜、鸡蛋2个、葱花、盐、香油 🍽️ 以上五道菜可以搭配成一顿丰盛的家常晚餐!想了解哪道菜的详细做法,直接告诉我菜名就好~场景二:获取具体菜谱
用户输入:“番茄炒蛋”
AI 回复(菜谱模式):
🍳 番茄炒蛋 · 详细菜谱 --- 📋 食材清单(精确到克): - 鸡蛋:3个(约150g) - 番茄:2个(约300g,选熟透的) - 葱花:5g - 食用油:15ml(约1汤匙) - 白糖:5g(约1小勺,提鲜用) - 盐:3g(约半小勺) - 水淀粉:淀粉3g + 水10ml(可选,让汤汁更浓稠) --- 👨🍳 分步做法: Step 1:备料(3分钟) 番茄洗净,顶部划十字,用开水烫30秒后去皮,切小块。鸡蛋打入碗中,加一小撮盐,充分搅打至起泡。 Step 2:炒鸡蛋(1分钟) 大火热锅,倒油,油温七成热时倒入蛋液。看到蛋液边缘开始凝固,用筷子快速划散,炒至八成熟(还有点湿润)立刻盛出。 Step 3:炒番茄(2分钟) 锅中余油,倒入番茄块,中火翻炒,用铲子按压番茄帮助出汁。炒到番茄变软、汤汁浓稠。 Step 4:合炒调味(1分钟) 鸡蛋回锅,加白糖和盐,大火快速翻炒均匀。如果喜欢汤汁浓稠,淋入水淀粉勾芡。 Step 5:出锅 撒上葱花,装盘出锅! --- 💡 烹饪技巧提示: - 番茄选熟透的,汁水多、酸味够,炒出来才浓郁 - 鸡蛋不要炒太老,八成熟盛出,回锅后刚好嫩滑 - 加一点白糖可以中和番茄的酸味,让口感更柔和 - 番茄去皮口感更好,如果懒可以不去,但会影响口感 --- 📊 营养价值说明: - 热量:约280大卡(两人份) - 蛋白质:约18g(鸡蛋提供优质蛋白) - 番茄红素:番茄富含番茄红素,加热后更易吸收 - 维生素C:番茄提供丰富的维生素C 🍽️ 配上一碗热米饭,就是最温暖的家常味道!场景三:特殊饮食需求
用户点击「🥗 低卡健康」
AI 回复(推荐模式,每道菜标注热量):
🥗 低卡健康餐推荐(每道菜均低于400大卡): --- 1. 鸡胸肉藜麦沙拉碗 🥗(约350大卡) > 推荐理由:高蛋白+优质碳水+膳食纤维,健身减脂的完美一餐。 > 做法概要:藜麦煮熟 → 鸡胸肉煎熟切片 → 蔬菜混合 → 淋油醋汁 > 食材清单:鸡胸肉150g、藜麦50g、混合生菜、小番茄、黄瓜、柠檬汁、橄榄油、黑胡椒 2. 清蒸龙利鱼配西兰花 🐟(约280大卡) > 推荐理由:龙利鱼低脂高蛋白,清蒸最能保留营养,鲜嫩无比。 > 做法概要:龙利鱼抹盐姜丝 → 上锅蒸8分钟 → 西兰花焯水 → 淋蒸鱼豉油和热油 > 食材清单:龙利鱼柳200g、西兰花100g、姜丝、蒸鱼豉油、橄榄油 3. 番茄菌菇豆腐汤 🍅(约180大卡) > 推荐理由:低卡饱腹,番茄的酸和菌菇的鲜完美融合,晚餐喝一碗刚好。 > 做法概要:番茄炒出汁 → 加水煮开 → 加入菌菇和豆腐 → 煮5分钟 → 加盐和葱花 > 食材清单:番茄2个、嫩豆腐半盒、金针菇100g、香菇3朵、盐、葱花 4. 虾仁滑蛋 🍤🥚(约300大卡) > 推荐理由:虾仁高蛋白低脂肪,鸡蛋嫩滑,5分钟快手菜。 > 做法概要:虾仁料酒腌制 → 鸡蛋打散加盐 → 虾仁炒至变色 → 倒入蛋液推炒至凝固 > 食材清单:虾仁150g、鸡蛋3个、料酒、盐、葱花、橄榄油 💪 健康饮食不等于吃草!想知道哪道菜的详细做法,尽管问我~十、文件清单与代码统计
10.1 文件清单
| 文件路径 | 行数 | 作用 |
|---|---|---|
common/Constants.ets | 36 | 全局常量:暖橙配色、6 话题文案、欢迎语 |
models/FoodAssistantModel.ets | 51 | 数据模型:ChatMessage、TopicOption、RecipeRecommendation、枚举 |
services/FoodAssistantService.ets | 331 | 核心服务:系统提示词、6 类话题回复、3 道详细菜谱、Map 匹配引擎、降级策略 |
pages/Index.ets | 304 | 主页面:完整对话界面 + 交互逻辑 |
| 合计 | 约 722 行 |
10.2 代码特点总结
- 零第三方依赖:全部使用 HarmonyOS 原生 API,包体小、启动快
- 双模式回复引擎:推荐模式(4-5 道菜概览)和菜谱模式(精确到克的详细菜谱)智能切换
- Map 关键词匹配:first-match 策略,顺序可控,确定性高
- 暖橙美食配色:60-30-10 法则,专业但不冰冷的视觉风格
- 完整降级策略:AI 不可用时无缝降级到本地模拟回复
- 精确到克的菜谱:3 条经典菜谱,食材精确克数,步骤带时间预估
- 6 类话题覆盖:MECE 原则设计,覆盖日常烹饪全场景
- 类型安全:class + enum 替代字符串和接口,编译期拦截错误
10.3 与之前项目的对比
| 维度 | AI 生活管家 | 健康生活顾问 | 美食生活助手 |
|---|---|---|---|
| 主色调 | 蓝色(信任) | 绿色(安全) | 橙色(食欲) |
| 话题数 | 6 个 | 5 个 | 6 个 |
| 回复模式 | 单一模式 | 多模式 | 双模式(推荐+菜谱) |
| 内容深度 | 中等 | 深度 | 深度(精确克数) |
| 核心资产 | 五条原则 | 症状分析框架 | 精确菜谱数据 |
十一、如何运行与扩展
11.1 运行环境
- DevEco Studio 5.0 及以上
- HarmonyOS SDK API 12 及以上
- HarmonyOS 4.0 及以上设备或模拟器
11.2 运行步骤
- 用 DevEco Studio 打开项目根目录
- 等待同步完成
- 连接设备或启动模拟器
- 点击运行按钮(Shift+F10)
11.3 扩展方向
方向一:接入真实大模型。修改FoodAssistantService.callAI()方法,接入大模型 API。推荐从华为盘古大模型或通义千问开始尝试。接入后,用户输入任意菜名,AI 都能生成精确到克的详细菜谱,不再局限于预设的 3 道菜。
方向二:食材识别。利用 HarmonyOS 的图片识别能力,用户拍摄冰箱里的食材,AI 自动识别并推荐可以做的菜。这是"食材库存 → 推荐菜品"的完整闭环。
方向三:语音交互。接入 HarmonyOS 语音识别,用户在厨房里可以一边做菜一边语音询问下一步操作,不用湿手去操作手机。
方向四:用户偏好学习。记录用户的历史查询和偏好(喜欢的口味、常做的菜、饮食限制),逐步优化推荐结果。同一个用户用久了,AI 越来越懂他的口味。
方向五:多端协同。利用 HarmonyOS 分布式能力,手机和平板/智慧屏协同。在手机上选好菜谱,智慧屏上显示步骤,手机作为遥控器控制翻页。
十二、结语
美食生活助手是一个专注于"吃"的 HarmonyOS AI 应用。它用约 700 行 ArkTS 代码,实现了从"不知道吃什么"到"做出一道菜"的完整烹饪辅助链路。核心的双模式回复引擎(推荐模式 + 菜谱模式)让用户在一个对话流中完成从决策到执行的过渡,无需切换应用或页面。
从技术角度看,这个项目验证了几个关键实践:
- MVVM 简化架构在美食类 AI 应用中的适用性:Model-View-Service 三层结构清晰、扩展性好
- Map 匹配引擎的可行性:在无 AI 接入阶段,用关键词匹配模拟智能对话,前后端并行开发
- 降级策略的工程价值:从 Mock 到真实 AI 的平滑过渡,开发者体验和用户体验都得到保障
- 精确数据的价值:菜谱中精确到克的食材用量,是"能用"和"好用"的分水岭
从产品角度看,美食助手的价值在于解决了"做什么"和"怎么做"两个核心问题。它不是传统菜谱平台的替代品,而是一个专注于"决策辅助"的轻量工具。它帮用户迈出"下厨房"的第一步——当你站在冰箱前犹豫不决的时候,打开它,点一下,答案就有了。
对于 HarmonyOS 开发者来说,这个项目是一个很好的学习参考——它涵盖了数据建模、状态管理、UI 构建、用户交互、AI 集成(预留)等应用开发的核心环节,代码量适中(约 700 行),结构清晰,零编译错误,可直接作为学习参考和二次开发的基础。
希望这个项目不仅能帮你学会 HarmonyOS 开发,也能帮你在"今天吃什么"这个问题上,少一点纠结,多一点享受。
