1. 从一句标题说起:这个项目到底在做什么
国庆黄金周,八亿人次的出行洪流,抢票、订酒店、排行程、查攻略,每年都有人被堵在高速上刷手机骂导航,也有人到了目的地才发现博物馆约满了。这个项目的出发点特别朴素:能不能让大模型把"做攻略"这件事彻底接过去,用户只需要说一句"我十月一号从某地出发,带老人和小孩,玩四天,预算中等,不想太累",剩下的行程、交通、住宿建议、备选方案全部自动生成,而且是在手机本地就能跑得动的轻量交互。
技术栈上,这个项目用了两块东西:蓝耘元生代 MaaS 平台负责模型能力的托管与调用,**鸿蒙(HarmonyOS)**负责端侧的应用承载和系统级能力整合。MaaS 是 Model as a Service 的缩写,说白了就是把大模型当成水电一样按需取用,你不用自己买卡、搭环境、调推理框架,平台把模型部署好,你通过 API 或者 SDK 直接调。鸿蒙这边则是利用它的分布式能力和元服务卡片机制,把"AI 行程规划师"做成一个可以随时唤起、跨设备流转的轻应用。
适合谁来参考这篇内容?三类人。第一类是想做 AI 应用但不想碰底层推理的独立开发者,MaaS 这条路能让你一周内出原型。第二类是鸿蒙生态里的应用开发者,想知道怎么把大模型能力接进 HarmonyOS 的工程结构里。第三类是产品经理或者做旅游、本地生活方向的从业者,想理解"AI 行程规划"这个场景在工程上到底卡在哪、哪些是真需求哪些是伪需求。
我先把结论放前面:这个项目最难的地方不是调模型,而是行程规划的约束求解和端侧交互的响应体验。模型能写出一篇漂亮的攻略,但攻略不等于可执行的行程,中间差着一整套结构化校验和兜底逻辑。下面我把整个思路、实现细节、踩过的坑一层层拆开讲。
2. 整体架构设计与技术选型思路
2.1 为什么选 MaaS 而不是自己部署模型
自己部署一个大模型做行程规划,听起来很酷,实际算一笔账就清醒了。假设你要跑一个 7B 到 14B 参数量的模型,推理需要 GPU,一张消费级显卡勉强能跑量化版本,但并发一上来就崩。国庆这种场景,用户请求是脉冲式的,可能十分钟内涌进来几千次调用,你自己那台机器根本扛不住。而且模型版本迭代快,今天调好的 prompt,明天换个模型可能效果就变了,维护成本极高。
MaaS 平台解决的就是这个弹性问题。蓝耘元生代这类平台把模型推理封装成标准接口,你按调用量付费,流量高峰自动扩容,低谷不浪费资源。对独立开发者来说,这意味着前期零硬件投入,把精力全部放在业务逻辑和交互体验上。我实测下来,从注册到跑通第一个对话接口,大概二十分钟,比配本地环境快太多。
选型时我重点看了三个维度:模型能力是否覆盖中文行程场景、接口是否稳定、是否支持流式输出。流式输出对行程规划特别重要,因为用户不想盯着转圈等十秒,边生成边显示体验好很多。这三点蓝耘元生代都满足,所以定了它。
2.2 鸿蒙端为什么是合适的载体
鸿蒙在这个项目里的价值,不只是"又一个手机系统"。它有几个特性刚好戳中行程规划场景:
- 元服务卡片:用户不用打开完整 App,在桌面卡片上就能看到今天的行程摘要,点一下展开详情。出行场景里,用户是高频短时查看,卡片比 App 更合适。
- 分布式流转:手机上规划好的行程,可以流转到平板或者车机上继续看。一家人出行,副驾的人拿平板看路线,司机看车机导航,数据是打通的。
- 端云协同:轻量计算放端侧,重模型调用放云端,鸿蒙的这套机制天然适配 MaaS 的调用模式。
我试过纯 Web 方案,也试过纯原生安卓,最后选鸿蒙是因为它的卡片和流转能力是系统级的,不用自己造轮子。当然代价是要学 ArkTS 和鸿蒙的工程结构,这个后面会讲。
2.3 整体数据流是怎么走的
把整个链路画成文字版:用户在鸿蒙端输入需求 → 端侧做初步的意图解析和参数抽取 → 组装成结构化请求 → 调用 MaaS 平台的模型接口 → 模型返回结构化行程 JSON → 端侧做校验和补全 → 渲染成卡片和详情页 → 用户微调后本地缓存。
这里有个关键设计:模型返回的必须是结构化数据,不是自然语言。早期我让模型直接写攻略,结果前端没法解析,用户想改一个景点都改不了。后来改成让模型输出 JSON,字段包括每天的时间段、地点、交通方式、预计耗时、备选方案,前端拿到就能渲染成可交互的列表。这个改动是整个项目体验的分水岭。
3. 核心细节解析:行程规划到底难在哪
3.1 约束求解:模型不是万能的
很多人以为把需求丢给大模型就完事了,实际做下来会发现,模型在"生成内容"上很强,在"满足硬约束"上很弱。什么叫硬约束?比如"博物馆周一闭馆"、"某景点需要提前三天预约"、"老人不能走超过两公里的步行路线"、"预算不能超过三千"。这些是客观事实和硬性条件,模型可能会忽略,也可能会编造。
我的做法是把约束分成两类:软约束交给模型,硬约束交给规则引擎。软约束是"不想太累"、"喜欢自然风光"这种主观偏好,模型处理得很好。硬约束是闭馆时间、预约规则、预算上限这种,我用一个本地的规则表来校验模型输出,不满足就回退重生成或者自动修正。
举个例子,模型给第一天排了三个景点,我本地规则一算,三个景点之间的通勤时间加起来超过四小时,明显不合理,就触发重排。这个校验逻辑不复杂,但能挡掉大部分"看起来很美但没法执行"的行程。
3.2 Prompt 工程:怎么让模型稳定输出 JSON
让模型稳定输出结构化 JSON,是这类应用的必修课。我踩过的坑包括:模型偶尔加个"好的,以下是为您规划的行程"这种前缀,导致 JSON 解析失败;字段名有时用中文有时用英文;数组长度不稳定。
解决办法是三层保险。第一层,在 system prompt 里明确要求"只输出 JSON,不要任何解释文字",并给出完整的 schema 示例。第二层,在请求参数里设置较低的 temperature(我用的 0.3 左右),减少随机性。第三层,端侧做容错解析,遇到非法 JSON 就用正则提取第一个{到最后一个}之间的内容再试一次。
下面是我实际用的 prompt 骨架,脱敏后分享出来:
你是一个专业的行程规划助手。用户会给你出行需求,你需要输出严格的 JSON。 输出格式: { "days": [ { "date": "第1天", "items": [ { "time": "09:00-11:30", "place": "地点名称", "type": "景点/餐饮/交通/休息", "duration_min": 150, "transport": "步行/地铁/打车", "transport_min": 20, "note": "备注,如需要预约", "backup": "备选方案" } ] } ], "tips": ["整体提示1", "整体提示2"] } 要求: 1. 只输出 JSON,不要任何其他文字。 2. 每天安排不超过 4 个主要活动。 3. 考虑老人和小孩的体力,连续步行不超过 30 分钟。 4. 每个地点给出备选方案。这个 prompt 我迭代了十几版,核心经验是:schema 越具体,输出越稳定。字段类型、取值范围、单位都写清楚,模型就不容易跑偏。
3.3 端侧参数抽取:别让用户填表单
行程规划类应用最大的流失点就是表单。让用户填出发地、目的地、日期、人数、预算、偏好,填完一半人就跑了。我的做法是让用户用自然语言说一句话,端侧做轻量抽取。
鸿蒙端我用了一个小型的意图识别逻辑,把用户输入里的关键实体抽出来:日期("十月一号"→ 具体日期)、人数("带老人和小孩"→ 3 人以上)、偏好("不想太累"→ 节奏舒缓)、预算("中等"→ 映射到区间)。抽取不出来的字段用默认值兜底,然后在生成结果页让用户一键微调,而不是在开始前逼用户填完。
这个"先给结果再让改"的思路,比"先填表再给结果"的转化率高很多。用户看到一份具体的行程,改起来有参照,比面对空白表单容易得多。
4. 实操过程:从零到能跑通的完整步骤
4.1 第一步:在 MaaS 平台跑通模型调用
先在蓝耘元生代平台上创建应用,拿到 API Key 和接口地址。然后写一个最小的调用脚本验证链路。我用 Python 做的验证,因为调试方便:
import requests import json API_URL = "你的接口地址" API_KEY = "你的密钥" def plan_trip(user_input): headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } payload = { "model": "你选定的模型名", "messages": [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input} ], "temperature": 0.3, "stream": False } resp = requests.post(API_URL, headers=headers, json=payload, timeout=60) return resp.json() result = plan_trip("十月一号从某地出发,带老人小孩,玩四天,预算中等,不想太累") print(result)这一步的重点是先确认接口通、模型能返回内容,别急着写前端。我见过有人前端写了一大堆,结果接口调不通,排查起来两头乱。先把后端链路跑通,拿到稳定的返回,再动端侧。
注意:API Key 千万不要硬编码在客户端代码里。鸿蒙端调用要走你自己的后端中转,或者用平台提供的临时凭证机制。密钥泄露是这类项目最常见的安全事故。
4.2 第二步:搭建鸿蒙工程结构
鸿蒙的工程结构对新手有点门槛,我简单说一下关键目录。用 DevEco Studio 创建工程后,主要关注这几个地方:
entry/src/main/ets/:放 ArkTS 代码,页面和逻辑都在这。entry/src/main/ets/pages/:页面文件,每个页面一个.ets文件。entry/src/main/ets/model/:数据模型定义,行程的接口类型放这。entry/src/main/resources/:资源文件,字符串、颜色、图片。
我建了三个页面:输入页、结果页、详情页。输入页就一个输入框加一个按钮,结果页展示每天的行程卡片,详情页展示单个活动的细节和备选方案。页面跳转用鸿蒙的router机制,数据传递用AppStorage或者路由参数。
ArkTS 的语法和 TypeScript 很像,有前端基础的话上手很快。但要注意鸿蒙有自己的状态管理装饰器,比如@State、@Prop、@Link,用错了数据不刷新,这个坑我踩过好几次。
4.3 第三步:端云联调与流式渲染
端侧调模型接口,最影响体验的是等待时间。一次完整的行程生成,模型可能要跑五到十秒。如果等全部生成完再显示,用户会以为卡死了。所以要做流式渲染。
流式渲染的逻辑是:模型每返回一段内容,端侧就解析一段,能渲染的先渲染。但行程是 JSON 结构,流式返回的 JSON 是不完整的,没法直接解析。我的处理方式是分段生成:先让模型生成第一天的行程,渲染出来,再生成第二天,依次类推。这样用户能很快看到第一天的内容,心理等待时间大大缩短。
具体实现上,我把请求拆成多次,每次请求一天。虽然总调用次数变多了,但体验好很多。而且每次请求的 prompt 更聚焦,模型输出质量也更稳定。
4.4 第四步:本地缓存与离线兜底
出行场景网络不稳定是常态,高铁上、山区里信号差,用户打开应用不能白屏。我在端侧做了本地缓存,用户生成过的行程存在本地数据库里,没网也能看。鸿蒙的关系型数据库或者首选项存储都能用,行程数据量不大,首选项存储就够。
离线兜底还有一层:如果用户没网又想规划新行程,端侧可以给一个基于模板的简易方案,虽然不如模型生成的好,但至少能用。这个模板我准备了几个常见的出行场景,比如"城市三日游"、"周边两日游",用户选一个再微调。
5. 常见问题与排查技巧实录
5.1 模型输出不稳定怎么办
这是最高频的问题。表现是同样的输入,有时返回标准 JSON,有时带一堆解释文字,有时字段缺失。排查思路按顺序来:
先看 temperature 是不是太高,超过 0.7 随机性就明显了,行程规划这种任务建议 0.2 到 0.4。再看 prompt 里的 schema 是不是够具体,字段有没有给示例值。最后看模型本身,有些模型对 JSON 格式的遵循能力就是弱一些,换个模型可能就好了。
我整理了一个速查表:
| 问题表现 | 可能原因 | 解决方向 |
|---|---|---|
| 返回带解释文字 | prompt 约束不够 | 强化"只输出 JSON"指令 |
| 字段名中英文混用 | schema 未固定 | 在 prompt 里给完整字段示例 |
| 数组长度忽长忽短 | 未限制数量 | 明确"每天不超过 N 个活动" |
| 内容重复啰嗦 | temperature 偏高 | 降到 0.3 左右 |
| 忽略硬约束 | 模型能力限制 | 端侧规则引擎兜底 |
5.2 鸿蒙端数据不刷新
ArkTS 的状态管理是新手最容易翻车的地方。我遇到过一次,模型返回了新数据,但页面还是旧的。排查半天发现是用了普通变量存数据,没用@State装饰。鸿蒙里只有被状态装饰器标记的变量,变化时才会触发 UI 刷新。
还有一个坑是对象嵌套。如果@State修饰的是一个对象,改对象内部的属性,有时不会触发刷新。解决办法是重新赋值整个对象,或者用@Observed和@ObjectLink处理嵌套。这个细节官方文档里有,但容易忽略。
5.3 接口超时和限流
国庆期间流量大,接口超时和限流是必然要面对的。我的处理是三层:端侧设置合理的超时时间(我用的 30 秒),超时后给用户明确的提示和重试按钮;请求做防抖,用户连点按钮不会重复发请求;后端做队列,高峰期请求排队处理,而不是直接拒绝。
实操心得:限流不可怕,可怕的是用户不知道发生了什么。给一个"当前咨询人数较多,正在为您排队"的提示,比直接报错体验好十倍。
5.4 行程合理性校验的边界
规则引擎不是越严越好。我一开始设了很多校验规则,结果模型生成的行程经常被判定不合格,反复重生成,耗时很长。后来我调整了策略:只校验硬约束(闭馆、预算、体力上限),软约束(景点搭配是否最优)交给模型,不追求完美。
这个取舍很重要。行程规划没有标准答案,用户要的是"能用且合理",不是"数学最优解"。过度校验反而让体验变差。
6. 一些关于场景和边界的思考
做这个项目的过程中,我越来越觉得"AI 行程规划"这个场景的价值,不在于替代人做决策,而在于降低决策的启动成本。很多人不是不会规划,是懒得开始。给一个 70 分的初稿,用户改起来就有方向了,这比让用户从零开始强太多。
技术边界上,MaaS 加鸿蒙这套组合,适合的是中等复杂度、对端侧体验有要求、但不想投入大量运维的应用。如果你的场景需要极致的低延迟,或者数据完全不能出本地,那可能要考虑端侧小模型。如果只是做个 Demo 验证想法,MaaS 是最快的路径。
最后分享一个我在实际使用中的小体会:行程规划类应用,备选方案比主方案更重要。用户真正会用的,往往是"如果这个景点约满了,我去哪"的那个备选。模型生成主方案很容易,生成合理的备选才见功力。我在 prompt 里专门要求每个活动都给 backup 字段,这个细节让整个应用的实用度上了一个台阶。