国庆那天上午,我在故宫午门外排队时,旁边一位游客正对着讲解器皱眉,屏幕上的语音讲解卡在一个“清代宫廷礼仪”的节点上,怎么划都划不过去。我掏出手机打开自己写的一个测试版 App,对着午门拍了一张照,然后问了一句:“这座门楼上凸出来的木条是什么用的?”大约三秒后,屏幕上出现一段图文并茂的回答。这篇博文就是想把那天前后做的所有事完整记录下来:一个跑在鸿蒙 HarmonyOS 手机上、靠云端 MaaS 平台提供推理能力的 AI 导游 Demo,从需求拆解、技术选型、功能落地到现场踩坑的全过程。
我写这个项目的原因很朴素:景区讲解器本质上是一台“定时播放的收音机”,不能追问,不能基于游客当前看到的实物做回应,更不能根据你只有两个小时、还带着孩子这种约束条件帮你重新规划路线。而大模型天然擅长这三件事。问题在于,大模型能力要落到“游客手机里”,需要一套轻前端加一个足够弹性的云端推理底座。我选择了鸿蒙 + 蓝耘元生代 MaaS 的组合,所有 AI 能力都放在云端,端上只做拍摄、提问和渲染。这篇文章不吹技术牛,只讲真实落地时遇到的东西,适合正在做文旅类 AI 应用、想把大模型接到鸿蒙端、或者单纯好奇“景区 AI 导游到底怎么跑起来”的开发者阅读。
1. 起念:拥挤故宫里的讲解器,为什么让人越用越累
1.1 国庆故宫的第一痛点:人从众状态下的“讲解体验断层”
国庆期间的故宫,游客密度几乎可以让任何地图 App 崩溃。但比物理拥挤更让人疲惫的,是信息获取的断层。官方讲解器需要排队租用、费用按台计算、耳机公用,讲解内容固定在某个时间节点上,你站在太和殿广场西侧想了解屋脊上的脊兽,得先听完前面五分钟的“太和殿整体介绍”。等讲到脊兽时,你可能已经被人流推到了中和殿门口。
我试过最典型的场景:孩子指着太和殿屋顶问“为什么屋顶角上有那么多小动物”,成年人现场很难答得准确又生动。讲解器答不了这种临时且具体的问题,百度也不是为这种“实时视觉+语音交互”设计的。那一刻我确认,景区导游类产品真正缺的不是“更多讲解词”,而是能围绕游客眼前实物、随时追问的交互能力。这也正是大模型最擅长的事。
1.2 为什么不做“离线语音包”,而要做 AI 问答
如果只是把讲解词做成离线语音包,技术上没有任何门槛,市面上大部分“电子导览”就是这么做的。但语音包有一个致命问题:它是单向的。游客听完一段、产生一个追问,产品就哑火了。要解决“追问”,就必须让机器理解当前语境、结合游客位置或所拍照片,再生成一段新的回答。这只能靠大模型或多轮对话系统完成。
我也评估过端侧小模型方案,比如在手机本地跑一个几十亿参数的语言模型。结论是:通用知识储备、图片理解能力、回答的细腻度,和小规模端侧模型的真实表现存在明显差距,尤其面对故宫这种知识密度极高的场景,端侧模型经常会把“脊兽”解释成“神兽”后就泛泛而谈,无法给出数量和排列规则这类精确信息。更关键的是,端侧模型不能天然访问我整理好的景区知识库。因此项目从一开始就确定:端上只做采集和展示,推理、检索、问答全部放到云端 MaaS 平台。
2. 选型定案:鸿蒙做前端壳,MaaS 做云端大脑,这样拆的理由
2.1 鸿蒙端的真实优势:一套代码覆盖多设备,卡片让游客免安装
选择鸿蒙 HarmonyOS 一方面出于技术考察,另一方面也是因为它的多设备流转特征和景区场景非常契合。我实际使用的是 HarmonyOS NEXT 的工程模板,用 ArkTS 写页面逻辑,DevEco Studio 做编译调试。模块拆成了三个部分:相机拍摄模块、对话问答模块、路线展示模块,整体结构并不复杂。
对我个人而言,鸿蒙真正有价值的是原子化服务卡片。传统的景区导览 App,游客必须下载安装、注册、授权,这一系列流程在故宫午门外的人流里足以劝退一半用户。而原子化服务可以让用户在不完整安装应用的情况下,通过扫码或碰一碰直接拉起服务卡片,卡片内完成“拍一拍、问一问”的核心闭环。虽然这次国庆测试我还是用了完整 App 包,但骨架是按原子化服务的思路拆的,后面要发卡片版本只是改入口的问题。
2.2 MaaS 平台的价值:不建 GPU 集群,也能在国庆高峰期弹性扩容
很多人会问:直接用公有云的 GPU 服务器不行吗?为什么非要多一层 MaaS?我的选择理由非常现实。国庆这种场景,流量是爆发性的:上午十点到下午三点可能是平时的十倍以上,之后又迅速回落。如果自己租 GPU 裸金属,要么按峰值配机器,承担下午空闲时的成本,要么按均值配,高峰期直接系统过载。
我在蓝耘元生代 MaaS 平台上托管模型推理,核心看中的是三点:第一,平台已经把模型 API 化和高并发调度做好了,我不用自己处理推理服务扩缩容;第二,平台提供知识库托管,可以直接把故宫主题资料上传,平台自动做切片、向量化、检索,省去单独搭向量数据库的工作;第三,它支持把微调和知识库绑定到同一个模型服务上,后续更新知识内容时,不用重新部署推理环境。对个人开发者或小团队来说,这几项省掉的工作量非常可观。
2.3 自建 GPU、IaaS 裸机、MaaS 三选一的对比复盘
很多项目选型时喜欢直接抄方案,我把自己这段时间的真实对比整理成了表格,供参考。
| 对比维度 | 自建 GPU 集群 | IaaS 裸金属 GPU | MaaS 托管模型 |
|---|---|---|---|
| 初始成本 | 极高,硬件采购、机房、散热 | 中等,按小时租赁 | 低,按调用量付费 |
| 弹性扩容 | 需要自己运维调度 | 可以扩机器,但应用层要自己写 | 平台自动处理 |
| 知识库能力 | 自己搭向量库 | 自己搭向量库+检索链路 | 平台自带,上传即用 |
| 运维负担 | 算法+运维都要管 | 至少需要运维和推理编排 | 绝大部分由平台承担 |
| 上手速度 | 以周计 | 以天计 | 当天可用 |
最终我选择 MaaS,本质上是把时间花在“导游功能”本身,而不是花在“怎么把模型服务稳定跑起来”。尤其一个人做项目时,时间就是最大的成本,能外包给平台的能力我没必要自己造轮子。
3. 功能落地的三个关键动作:拍照识别、检索问答、路线推荐
3.1 拍照识别:先让 App 学会“看”,再让模型回答“是什么”
拍照识别是 AI 导游最直观的入口。用户在午门前拍一张照片,App 要把这张照片转成一个可以被模型理解的问题。我的实现方式比较直接:调用 HarmonyOS 的相机能力和媒体库权限,获取图片后先在端上做压缩,把最长边压到 1024 像素、质量压到 60%,然后通过 HTTP 请求将图片和用户的问题一起发送给 MaaS 平台的多模态对话接口。
压缩这一步特别重要。故宫场景下 4G/5G 信号往往被高密度人群挤满,一张 3MB 原图上传可能需要十几秒,压到 200KB 左右后通常能控制在两秒内。核心代码逻辑比较简单,就是请求层的封装:
// ArkTS 简化示例:压缩后发起多模态问答请求 import http from '@ohos.net.http'; import image from '@ohos.multimedia.image'; async function askWithImage(uri: string, question: string) { // 1. 解码图片并压缩 let source = image.createImageSource(uri); let packed = await source.createPixelMap(); let target = await packed.scale(1024, 1024, { scaleMode: image.ScaleMode.FIT }); let compressed = await target.compress(image.CompressFormat.JPEG, 60); // 2. 组装多模态请求体 let requestBody = JSON.stringify({ image_base64: compressed, question: question, stream: false }); // 3. 调用 MaaS 平台多模态接口 let request = http.createHttp(); let response = await request.request('https://your-maas-endpoint/v1/chat/multimodal', { method: http.RequestMethod.POST, header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key' }, extraData: requestBody }); return response.result; }实测发现,大模型对建筑整体样式的识别比较准,但一旦遇到相似的屋顶形制就容易翻车。比如故宫很多门的形制接近,模型可能会把“太和门”和“午门”混淆。我在提示词里加了一条硬约束:“如果对具体建筑不能确定,请说明可能的选项并描述特征,不要直接编造一个名称。”这条约束对控制幻觉非常有效。
3.2 检索问答:先让 AI 有故宫的知识底座,而不是靠记忆硬扛
做大模型应用的人都清楚,模型本身的知识截止日期和通用训练语料,不足以应付故宫这种细节密度极高的场景。直接用通用模型问“故宫太和殿屋脊上为什么有十只脊兽”,它可能给你一个差不多但不够严谨的答案。要让回答准确,必须给模型接入知识库。我在蓝耘元生代 MaaS 平台里建了一个名称为“故宫主题知识库”的数据集,把公开的故宫介绍、古建常识、文物说明等资料整理成 Markdown 文档上传,平台自动完成切片和向量化。问答请求发起时,平台先做语义检索,把相关片段拼进上下文,再交给大模型生成回答。
这种做法也就是常说的 RAG(检索增强生成)。我刻意没有选择微调模型,原因是知识库内容需要频繁更新,比如临时加入某个临时展览的说明,微调一次成本太高,而且微调模型同样会产生幻觉。知识库则可以做到“随时改、随时生效,回答附参考来源”。
我给问答系统设计的提示词模板大致如下:
你叫“宫喵导游”,正在故宫现场为游客讲解。 请仅依据提供的知识库内容回答用户问题;如果知识库没有相关信息, 请明确说“这个我还不确定”,并给出一个最相近的开放性问题建议。 回答要求: - 先直接回答,再补充一句可观察的现场细节 - 控制在150字以内,口语化,适合边走边听 - 如果用户上传了图片,优先结合图片内容回答这个提示词的好处是把“不确定时说不知道”写在了规则里。国庆实测过程中,凡是模型遵守这条规则的场景,游客体验都好过它硬着头皮编一个答案。
3.3 路线推荐:大模型负责理解人话,规则引擎负责保底
路线推荐我没有让大模型直接生成完整走法,而是让大模型做“意图转结构化”,再由一个简单的规则引擎去算路线。原因是路线规划涉及开放时间、步行距离、单向流线等约束,大模型直接输出路线容易忽略这些硬性限制,而规则引擎恰恰适合处理这类确定性问题。
具体流程是:用户说“我只有两个小时,想看钟表馆,带着一个五岁的孩子”,大模型先抽取意图,输出 JSON 结构化字段:
{ "interests": ["钟表馆"], "duration_minutes": 120, "with_kid": true, "start_point": "午门" }然后我把这份结构化参数交给规则引擎。规则引擎里预置了故宫主要开放区域之间的步行时间、单向通行规则和儿童友好设施位置,最终输出一条带时间戳的简易路线。这个方案的好处是:大模型不擅长精确计算,但擅长把一句口语变成机器能读取的参数;规则引擎不擅长理解自然语言,但擅长保证路线“能走通”。二者结合,效果远好于让模型直接编一条路线。
4. 实测现场的真实反馈:信号、时延、并发这些躲不掉的坑
4.1 场地信号与弱网降级:图片传不上去的时候怎么办
国庆故宫的人流密度,让我第一次对“弱网环境”有了切肤之痛。实测中,午门广场和太和殿广场的 5G 信号时好时坏,尤其在人最密集的中午时段,一张 200KB 的图片可能也要重试三四次。刚开始我的 App 是同步等待图片上传成功再发起问答,结果就是用户眼睁睁看着转圈圈,体验很差。
后来我做了两级降级方案。第一级:图片压缩参数动态调整,如果检测到连续两次上传超时,自动把图片最长边压到 768 像素、质量压到 40%;第二级:把图片识别结果“文本化”。先用端上的轻量分类器给建筑图片打一个粗标签,比如“宫门”“大殿”“屋顶”“狮子”,再把标签作为文本参数传给大模型。这虽然丢失了一些视觉细节,但至少保证了在极弱网环境下问答依然可用。实测下来,这个文本兜底方案的响应成功率明显高过图片方案。
4.2 高并发把免费额度打爆:429 限流之后的退避与重试
国庆第三天上午,我把 App 分享给同行的五位朋友试玩,结果直接把 MaaS 平台的免费调用额度打满,接口开始返回 429 状态码。这反而提醒我一个很重要的问题:真实景区场景下,并发不是“可能的问题”,而是“必然的问题”。几十个游客同时提问,请求会瞬间到达平台,如果应用层不做限流退避,用户看到的只会是一排报错。
我在鸿蒙端实现了一个简单的请求队列和指数退避逻辑:当收到 429 时,第一次等待 1 秒重试,第二次等待 2 秒,第三次等待 4 秒,最多重试三次。同时在 UI 上明确显示“当前游客较多,AI 正在排队中”,把技术报错“翻译”成人话。这个细节非常重要,因为游客完全无法理解“429”是什么,但能理解“前面有十几个人在问问题”。
4.3 端云延迟与流式输出:首 token 2 秒是门槛,逐字输出才是王道
大模型生成完整回答通常需要 2 到 5 秒,这在景区现场是非常漫长的等待。我一开始用非流式接口,整个回答生成完一次性返回,用户盯着空白屏幕干等四秒,体验堪称灾难。后来切换到流式输出,模型生成一点就传一点,鸿蒙端收到后逐字渲染,用户在第一秒就能看到文字开始出现,感知等待时间明显缩短。
我记录了一次相对理想的实测数据:图片上传加压缩耗时约 0.4 秒,MaaS 平台检索知识库加模型生成首字耗时约 1.8 秒,整段回答流式输出完成约 3.2 秒,总计约 5.4 秒。虽然不是秒回,但在流式输出的加持下,用户普遍觉得“还挺快”。这个体验差距完全来自同一条普普通通的技术决策:把一次性返回改成流式返回。
| 环节 | 耗时 | 优化方式 |
|---|---|---|
| 图片压缩与上传 | 约 0.4 秒 | 自动压缩、弱网分级降质 |
| 知识检索+首字生成 | 约 1.8 秒 | 流式返回、缓存热门知识片段 |
| 完整回答输出 | 约 3.2 秒 | 逐字渲染、UI 动画缓冲 |
| 总计 | 约 5.4 秒 | 用户感知明显优于非流式 |
5. 回来后的复盘:数据告诉我该改哪里,以及给同路人的几条建议
5.1 实测数据复盘:哪些问题答得好,哪些问题答穿了
那天一共发起问答 47 次。其中关于建筑形制类的问题回答质量最好,比如“这个石头上的花纹代表什么”这类问题,模型结合知识库给出的回答准确率很高。关于开放时间、展览位置这类实时信息则表现很差,因为我的知识库没有实时数据源,模型只能基于训练数据猜,这让我意识到 AI 导游必须接入景区开放数据或实时公告接口,否则时效性就是硬伤。
我最满意的一个问答是:朋友在九龙壁前问“为什么这里的龙数量是九的倍数”,模型不仅回答了“九是阳数之极”,还结合现场指出了壁面上龙纹的排列规律。这类回答无法靠固定语音讲解覆盖,因为它不是按顺序播放的,而是真正围绕游客当前目光所及的细节生成的。这是我认为 AI 导游相比传统讲解器最本质的差异。
5.2 给想做 AI 导游的开发者几条非常实际的建议
第一,不要相信大模型能直接干好所有事。摄影识别时要加“不确定就承认”的提示词,路线规划时要用规则引擎兜底,实时信息要对接数据源,层层组合才靠谱。第二,知识库建设一定要重视版权,只使用公开渠道可获取的资料,避免把书籍扫描件或付费内容直接丢进向量库。第三,鸿蒙端开发时,动态权限申请一定要给足理由说明,否则游客在嘈杂环境下根本不知道是否该允许 App 使用相机和定位。第四,务必给 MaaS 调用设置预算上限和单日配额,我当时差一点因为重复请求刷爆额度,幸好及时加了队列。
这次项目下来,我最大的体会是:把大模型接进 App 只是最表层的工作,真正花时间的全在那些“看不见的地方”——知识库的整理方式、弱网下的降级策略、并发请求的兜底机制、回答不确定时的表达策略。AI 导游看起来是一个很酷的演示,但做下来才知道,它本质上是一个“野外环境下的端云协同工程”。
最后再分享一个小技巧:在鸿蒙端可以用原子化服务卡片把 AI 的回答直接推送到手机桌面,游客不需要每次打开 App 就能看到上一次的导览结果。我在国庆测试结束后把它加进了原型,下次再去故宫,我打算把它扩展到手表提醒上,让路线规划和问答卡片一起流转到手腕上。当然,那就是下一篇实战记录的素材了。