鸿蒙 AI 应用开发实战:结构化大模型驱动智能穿搭与衣物护理

关键词:HarmonyOS、ArkTS、ArkUI、应用智能化、智能体、结构化输出、端侧数据、本地持久化、衣物护理、多端协同

本文以“衣见倾心”这一真实可运行的鸿蒙应用为例,记录从生活痛点、产品取舍到 ArkTS 工程实现的完整过程,并讨论如何将大模型能力从一句泛泛的建议,转化为用户可以直接执行的穿搭方案。

项目总览

一、问题从来不是“缺衣服”,而是缺少一套决策系统

“今天穿什么”是一件极其日常、却并不简单的事。早晨打开衣柜时,用户往往同时面对温度、降雨、通勤或约会场合、已有衣物、刚洗的衣服、已经连续穿过的单品等约束。传统衣橱 App 通常解决的是“记录”:拍照、分类、列表展示;传统天气 App 提供的是“信息”:气温、体感、降雨。二者之间仍然缺了一层真正的决策:在我的衣橱里、面对今天的天气当前场景,我到底应该穿哪几件,以及为什么。

“衣见倾心”将这个问题重新定义为一个轻量级的个人生活决策场景。它不追求生成一张不可购买、不可穿着的“时尚概念图”,而是从用户已拥有、状态可用的衣物中选择组合;不只给出“温柔风”“学院风”等抽象标签,而是返回衣物 ID、推荐理由和护理提示;也不把 AI 当成唯一事实来源,而是使用本地规则限制温度、状态等硬约束,再让模型完成风格协调、场景表达与语言解释。

这正契合鸿蒙应用智能化实践的核心:AI 不是附在页面顶部的聊天框,而是能读取业务上下文、遵守业务规则、驱动业务对象状态变化的能力。对于本项目而言,业务对象是衣物,业务规则是适温、清洁状态与护理流转,AI 的职责是在规则允许的范围内做个性化组合与解释。
在这里插入图片描述

二、项目定位与征文方向的对应关系

本项目选择“AI 穿搭与衣物护理”这一生活化主题,覆盖了多个征文方向。

第一,鸿蒙应用智能化实践。应用将天气、场景和衣橱数据组织为结构化上下文,通过兼容 OpenAI 协议的模型服务生成严格 JSON,再映射回本地衣物实体。模型输出不直接拼到页面上,而是经过校验后驱动页面展示。

第二,鸿蒙智能体与 Skill 实践。当前版本已将能力边界划分为“生成搭配、添加衣物、查询衣橱、更新护理状态”。后续可将其映射为小艺可调用的意图:用户说“今天十八度去约会穿什么”“哪些衣服该洗了”“把这件外套加入衣橱”,系统便可将自然语言转化为应用内动作。

第三,鸿蒙应用开发与工程实践。项目使用 ArkTS 严格类型定义模型,用 ArkUI 的声明式组件构建四个业务页面,用 Preferences 保证本地数据可恢复,用异步网络请求处理模型调用,并通过服务层隔离 UI 与 AI 实现。

第四,跨设备协同与系统能力。手机是拍摄、确认和即时决策的入口;平板或 PC 更适合批量整理衣柜、回看穿搭日历;穿戴设备可在出门前显示下一套搭配、天气突变时提醒加衣。当前 MVP 已将数据模型和服务边界设计为可扩展形态,后续可通过分布式 KV 同步衣橱与穿搭记录。
在这里插入图片描述

三、体验设计:先把“能用”做完整,再把“聪明”做可信

应用底部由“今日、衣橱、护理、我的”四个 Tab 组成。这样的信息架构不是按照技术模块切分,而是按照用户一天中的行为路径切分:早晨在“今日”做选择;空闲时在“衣橱”维护资产;洗衣、晾晒时进入“护理”;偏好、提醒和未来能力统一在“我的”管理。

生成中的智能穿搭

首页先展示城市、天气、温度和衣橱状态,再让用户选择通勤、约会、休闲、运动场景。场景标签是一个重要的产品取舍:与其让用户输入一大段描述,不如先给高频且低成本的选择。用户点击标签或“重新生成我的穿搭”后,按钮会进入“正在生成”状态,并显示反馈文案,避免异步网络调用在视觉上像“没有反应”。

生成完成的搭配建议

推荐卡展示标题、天气与场景、选中的衣物、原因和提示。卡片只展示模型已经映射到本地实体的衣物,因此不会出现“建议穿一件你没有的蓝色羊毛大衣”这种断裂体验。若网络异常、服务超时或模型输出无法解析,应用会退回本地推荐策略,保证首页始终有结果可用。这种“可用优先、智能增强”的策略,比完全依赖网络更符合移动应用的可靠性要求。

四、ArkUI 页面搭建:声明式 UI 如何承载生活化信息

Index.ets 中,应用使用 Tabs 组织四个页面,并通过 @State current 保持当前选中项。页面主题集中在 Theme.ets 中维护,例如背景色、卡片色、主色、圆角和统一边距,避免颜色散落在每个页面里。柔和的粉白色背景与卡片化布局不是单纯的视觉装饰,它让“衣橱”“护理”这类高频生活信息保持低压力、易浏览的阅读节奏。

首页 TodayTab 的状态定义如下:

@State clothes: Clothing[] = [];
@State weather: WeatherInfo = OutfitAI.mockWeather();
@State plan: OutfitPlan | null = null;
@State scene: string = '通勤';
@State loading: boolean = false;
@State feedback: string = '';

这里的关键是让状态与用户可见内容一一对应。clothes 影响衣橱统计和推荐候选;scene 影响提示词和本地策略;plan 驱动推荐卡;loading 控制按钮文案与点击保护;feedback 则为异步过程提供可感知的反馈。状态粒度不要过粗,否则页面一处变化会引起大量无关刷新;也不要把所有临时状态放入全局,否则会失去页面封装性。

点击生成按钮的处理逻辑如下:

async generate(): Promise<void> {
  if (this.loading) { return; }
  this.loading = true;
  this.feedback = '正在根据你的衣橱生成新搭配…';
  try {
    this.plan = await OutfitAI.recommend(this.clothes, this.weather, this.scene);
    this.feedback = '已更新一套新的搭配建议';
  } finally {
    this.loading = false;
  }
}

finally 的意义非常重要。无论成功、解析失败还是网络抛错,loading 都会被复位,用户不会陷入永久禁用的按钮状态。对 AI 场景而言,失败不是异常路径,而是必须被产品设计覆盖的常规路径。

五、衣橱不是图片墙:建立可计算的数据模型

衣橱分类与状态

如果衣物只保存一张照片和一个名称,AI 无法可靠完成温度匹配、风格筛选和护理提醒。因此项目定义了 Clothing 接口:

export interface Clothing {
  id: string;
  name: string;
  category: string;
  color: string;
  season: string[];
  style: string[];
  temperatureMin: number;
  temperatureMax: number;
  emoji: string;
  status: ClothingStatus;
  wornCount: number;
  lastWorn: string;
  careTip: string;
}

其中 id 是模型输出与本地实体之间的稳定主键;category 用于保证一套搭配至少包含上装、下装或裙装、鞋履等必要角色;temperatureMin/temperatureMax 是硬约束;stylecolor 是模型做审美判断的上下文;statuswornCountlastWorn 让应用从静态衣柜进入动态衣物生命周期管理;careTip 则将衣物知识沉淀在每个对象上。

状态机采用 clean | worn | washing | drying 四种状态。它虽然简单,却覆盖了用户真正关心的关键事实:这件衣服能不能穿、是不是该洗、是否还在晾晒。更复杂的状态并不一定更好;MVP 阶段要优先保证状态可解释、流转可预测。护理页面中,“去清洗—去晾晒—已收纳”按钮恰好对应这条有向链路。

AI 衣物识别入库入口

衣橱页支持分类筛选和 AI 识别入库演示。当前版本以模拟识别完成从“拍摄入口”到“标签化衣物档案”的交互闭环,后续替换为视觉模型时,界面与数据层无需重写:只需将识别结果转换为 Clothing。这是先稳定领域模型、再替换能力实现的工程思路。

六、本地持久化:把用户衣橱保留在用户手里

衣物数据是强个人化数据,默认应可离线使用。项目通过 @kit.ArkData 的 Preferences 封装 WardrobeStore,将存储细节从 UI 中移除。初始化时先获取 Preferences 实例,如果首次运行没有任何数据,则写入示例衣物;查询、添加、状态更新都经由 Store 完成。

static async updateStatus(id: string, status: ClothingStatus): Promise<void> {
  const list = await WardrobeStore.list();
  list.forEach((item: Clothing) => {
    if (item.id === id) {
      item.status = status;
      if (status === 'worn') {
        item.wornCount += 1;
        item.lastWorn = '今天';
      }
    }
  });
  await WardrobeStore.save(list);
}

持久化方法在写入后调用 flush(),使数据及时落盘。这样做的好处是:即使应用在后台被系统回收,衣橱数据也不会因为只存在内存中而丢失。对于将来分布式协同的版本,本地 Preferences 仍然可以作为离线缓存和冲突合并前的基础层。

七、模型调用:让大模型只做它擅长的事情

模型服务层位于 OutfitAI.ets。它的输入不是一段随意拼接的自然语言,而是 WeatherInfo + scene + WardrobePromptItem[] 组成的 JSON。WardrobePromptItem 保留 ID、名称、分类、颜色、风格、适温、状态与最近穿着时间,避免发送无关 UI 字段。

private static buildPrompt(clothes: Clothing[], weather: WeatherInfo, scene: string): string {
  const wardrobe: WardrobePromptItem[] = [];
  for (const item of clothes) {
    wardrobe.push({
      id: item.id,
      name: item.name,
      category: item.category,
      color: item.color,
      style: item.style,
      temperature: [item.temperatureMin, item.temperatureMax],
      status: item.status,
      lastWorn: item.lastWorn
    });
  }
  return JSON.stringify({ weather, scene, wardrobe });
}

系统提示词要求模型只返回以下 JSON:

{
  "title": "搭配标题",
  "itemIds": ["衣物id"],
  "reason": "推荐理由",
  "tips": "穿着或天气提醒"
}

为什么必须返回 itemIds,而不是衣物名称?因为名称不是可靠主键,用户可能拥有两件“白色衬衫”;而 ID 能稳定映射回本地对象。为什么要把提示词限制为 JSON?因为 UI 需要的是可渲染字段,而不是不可预测的长文本。为什么还要在客户端检查映射结果?因为模型可能输出不存在的 ID,或因网络、限流、格式问题返回异常内容。

服务请求使用 @kit.NetworkKithttp.createHttp(),设置 JSON 请求头、认证头、连接超时与读取超时。无论请求是否成功,客户端都在 finally 中释放 Http 实例。对于移动端资源管理来说,这个细节不可忽略。

try {
  const response = await client.request(AIConfig.endpoint, options);
  if (response.responseCode !== 200) {
    return OutfitAI.localRecommend(clothes, weather, scene);
  }
  // 解析 JSON 并映射 itemIds
} catch (e) {
  return OutfitAI.localRecommend(clothes, weather, scene);
} finally {
  client.destroy();
}

八、为何需要本地规则兜底

“接了大模型”不等于“所有决策都要交给大模型”。项目中的 localRecommend 先筛选状态为 clean 且适温范围覆盖当前气温的衣物,然后寻找上衣、外套、下装/裙装、鞋履。这是确定性规则,适合处理不应被模型随意突破的条件。

模型调用失败时,本地策略仍能产生一套基本可用的搭配;模型调用成功时,模型可以在候选之间选择更符合场景与色彩语义的组合,并给出自然语言解释。两者关系不是替代,而是分工:规则负责可靠性,模型负责个性化和表达力。

这样的分层还能带来可观测性。当用户认为推荐“不合适”时,我们能判断问题是天气数据错误、衣物标签不完整、本地硬规则太严,还是提示词策略不佳;如果所有逻辑都藏在一段模型对话里,就很难定位和迭代。

九、衣物护理:把一次穿搭变成完整生活闭环

衣物护理状态流转

许多穿搭产品只关心“穿之前”,而忽略“穿之后”。但对用户而言,今天穿了什么、是否该清洗、是否仍在晾晒,直接影响明天能穿什么。因此“护理”并不是附属页,而是让衣橱数据保持真实可信的关键模块。

护理页按状态汇总待清洗、晾晒中、洁净可穿数量,并将非洁净衣物显示在待办清单中。用户每点击一次操作,WardrobeStore.updateStatus 就会更新实体并重新拉取列表,页面自动刷新。由于状态和护理建议都被绑定到同一份 Clothing 数据,今日推荐和护理列表天然保持一致。

未来可以在此基础上增加材质、洗涤方式、晾晒时长、季节收纳、防蛀提醒;也可以让模型根据用户的衣物材质与天气湿度生成更个性化的建议。但必须坚持一个原则:涉及衣物护理的建议应明确为辅助信息,用户仍应以衣标为准,避免以生成内容替代专业洗护说明。

十、关于智能体、Skill 与跨设备协同的下一步

个人偏好与能力入口

当前项目已经具备将业务能力开放为智能体工具的条件。可以设计如下意图:RecommendOutfit(scene, temperature)AddClothing(image)QueryWardrobe(style)UpdateCareStatus(clothingId, status)QueryCareList()。用户对小艺说“今天下雨,帮我选一套通勤穿搭”,意图框架提取场景和天气参数,调用 RecommendOutfit,再把结果展示为卡片或拉起应用首页。

跨设备协同同样不应只是“把页面放大”。手机承担拍摄、快速确认和外出提醒;平板可用双栏布局管理分类、批量编辑标签;PC 可提供穿搭日历、衣物统计和胶囊衣橱规划;穿戴设备可以在出门前显示简短的“18℃,带外套”提示。共享的核心不是 UI,而是衣物实体、状态流转、用户偏好与推荐历史。后续使用分布式 KV 存储同步时,应将这些数据按用户授权、设备可信关系和最小必要原则分层处理。

十一、工程目录与可维护性

工程目录结构

项目目录按职责划分:pages 只关心显示与交互;model 定义领域对象和持久化;service 负责天气、模型或未来系统能力;common 放置主题与配置;entryability 处理应用生命周期。这种分层在小项目中也很必要,因为 AI 功能极易扩张:今天是文本推荐,明天可能增加视觉识别、语音意图、云端同步。若从一开始把网络请求写在页面点击事件里,后续维护成本会迅速上升。

项目构建已使用 DevEco Studio 与 Hvigor 验证通过。实际发布时,应将模型密钥放到服务端代理或受控凭证体系,而不是暴露在客户端;客户端只请求自有服务,由服务端完成鉴权、限流、审计和模型调用。本文展示的直接请求方式适合原型验证,不能等同于生产安全方案。

十二、复盘:从“AI 功能”走向“可信的智能体验”

这次实践最重要的收获,不是完成了一个能调用模型的页面,而是明确了生活类 AI 应用的三个判断标准。

第一,AI 输出必须能落在业务对象上。穿搭结果应返回本地衣物 ID,而不是一句无法执行的风格描述。第二,AI 必须有边界。温度、清洁状态、用户是否拥有某件衣服等事实约束,应由确定性数据和规则守住。第三,AI 必须可失败。网络异常、接口限流、输出格式波动都不应该让用户失去基本功能,因此本地兜底与清晰反馈同样是智能化体验的一部分。

“衣见倾心”仍是 MVP,但它已经形成了完整闭环:衣物被结构化记录,状态随使用而变化,天气与场景触发推荐,模型在受控上下文中产生建议,结果回写到用户可理解的卡片,护理任务再让衣物回到可穿状态。未来接入视觉识别、实时天气、小艺意图、服务卡片和跨设备数据流转后,这个闭环会从“一个 App 的功能”成长为“鸿蒙全场景生活服务”的一个具体切面。
在这里插入图片描述

结语

智能化不应该让用户多学习一套复杂操作,而应该减少用户在真实生活中做重复决策的成本。把“今天穿什么”做成一项可调用、可解释、可恢复、可协同的服务,正是鸿蒙应用智能化实践的价值所在。希望这份实现能为 ArkTS、ArkUI、结构化大模型输出以及生活场景智能体设计提供一份可复用的参考。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐