
国庆假期带全家逛故宫在讲解器租赁柜台前排队时我就冒出一个念头与其等一个讲得死板的录音导览不如用鸿蒙 HarmonyOS 自己做个能对话的“AI 导游”。回来后我把这个想法落地了大脑部分接入蓝耘元生代 MaaS 平台手机端负责拍照、语音和播报从立项到在故宫实测完前后只花了一个假期。这篇文章不吹不黑把架构设计、鸿蒙端实现细节、MaaS 接入方式以及现场实测的数据和翻车过程全部记录下来给想做 AI 伴游、智能讲解类应用的开发者一个可以直接参考的样本。1. 起因在故宫的人海里现成导览为什么都接不上话1.1 现场痛点讲解器排队、导览内容僵化节假日逛故宫痛点非常具体。官方讲解器的租赁柜台在午门附近旺季上午十点就开始排队下午常常直接租完人工讲解需要提前预约、拼团热门时段根本约不上。就算顺利拿到了讲解器播放内容也是固定编号的录音围绕“这个建筑是哪年建的、用什么材料、有什么故事”展开你没法追问也没法换角度问。对孩子来说尤其不友好——他们的提问方式完全是跳跃的看到铜狮子会问“它怎么区分公母”看到脊兽会问“哪个是房顶上最小的那个”这些在固定导览里统统找不到答案。我当时站在太和殿广场上看着身边一个家长被孩子问得满头大汗就知道这种“提问式讲解”的需求是真实存在的而且现成产品一个都没接住。1.2 为什么选择自己动手做而不是直接买现成 App市面上的导览应用我也试过一轮。有内容的门槛高要么必须开会员要么内容质量参差不齐把野史当正史讲有交互的又太浅基本停留在“搜索景点名称看一段文字”的层面。最关键的问题是这些产品设计出来的时候大模型多模态能力还没有普及所以它们根本没有“把镜头对准文物、AI 自动回答”这种交互路径。现在这个能力已经变成 API 了把它接进一个手机应用里对开发者来说成本比两年前低了不止一个量级。对我来说自己做还意味着可以完全按自己的需求定制想追问就追问想让孩子听就换语气版本想离线就提前缓存这些都是买来的产品给不了的。1.3 动手前我把需求收成了三条硬指标任何工具类应用需求不收敛就做不完。我当时给自己立了三条标准拍照识别对准建筑、文物、匾额拍照尽量 3 秒内返回名称、年代、用途和一段能讲出口的解说。语音问答不用打字直接用语音问“太和殿是干嘛的”“这个狮子怎么分公母”系统能听懂、能回答、能朗读出来。离线兜底故宫里人群密集、建筑遮挡严重网络随时可能断Top 20 的必看展品必须能在无网状态下正常讲解。这三条硬指标直接决定了后面所有的技术选型也决定了哪部分用 MaaS、哪部分要做本地逻辑。后面的实测证明这三条指标的优先级排序是对的先保证“问得出口、答得出来”再考虑准确率和体验细节。2. 技术选型MaaS 负责大脑鸿蒙负责手脚2.1 为什么选 MaaS 而不是本地部署或微调模型当时摆在我面前有三个路线本地服务器部署开源模型、拿数据微调一个垂直模型、直接用 MaaS 的在线 API。我选了第三条。首要原因是故宫文物知识虽然体量大但通用大模型已经掌握得很扎实轮不到我一个小项目去专门训一个模型其次本地部署需要一张像样的 GPU电费和运维成本都不划算再者微调需要标注数据光收集“文物照片 标准解说”这个数据集就不是一个假期能搞完的。蓝耘元生代这类 MaaS 平台的价值恰恰在于把“多模态识别 对话生成”这些重活封装成了 API。我在鸿蒙端把照片传上去平台把图片理解的结果和文物解说文本一起返回我只需要做一层请求转发和结果解析相当于几分钟就把一个“懂文物的导游”接上了线。对个人开发者来说MaaS 最实在的一点是“按量付费、不用养服务器”我今天用就今天付费项目不跑了也不产生闲置成本这对假期突发奇想型项目尤其友好。2.2 为什么是鸿蒙 HarmonyOS而不是顺手做个 Android 双端这要从两个层面说。第一层是技术原因鸿蒙的 ArkUI 声明式语法写界面的效率确实高手机和平板可以共用一套代码以后做儿童版大屏模式可以直接复用另外鸿蒙对音频播放、摄像头调用的系统级封装比较完整开发一个“拍照 播报”的应用不需要堆太多第三方库。第二层是实际原因我想借这个项目完整走一遍鸿蒙应用从开发、打包到真机运行的过程看看它现在到底好不好用。结论是对我来说这是值得的虽然过程中也踩了一些权限和组件版本的坑但整体开发体验已经足够支撑这种中小型工具应用。2.3 整体架构和数据流向整个系统分三层层级职责关键技术点鸿蒙客户端拍照、录音、UI 展示、语音播报、本地缓存ArkTS / ArkUI、媒体服务、本地 JSON 缓存轻网关层转发请求、缓存热点问答、限流、打日志一个简单的服务端程序只做透传和缓存蓝耘元生代 MaaS多模态识别、文物知识问答、文本生成调用平台的多模态和对话接口数据流很直白用户拍照或者说话 - 鸿蒙端组装请求 - 网关层缓存命中就直接返回没命中转给 MaaS - MaaS 返回识别结果和解说文本 - 鸿蒙端渲染文字并调用 TTS 播报。网关层我特意保留的原因下面会细说它帮我省了不少 MaaS 调用费也让我能在不碰客户端的情况下调整 prompt。3. 鸿蒙端的工程实现从拍照识文物到连续语音问答3.1 权限申请鸿蒙的授权策略和两个容易踩的坑鸿蒙的权限申请涉及两处配置。第一处是module.json5文件里声明权限{ module: { requestPermissions: [ { name: ohos.permission.CAMERA, reason: 用于拍摄文物照片进行识别, usedScene: { abilities: [EntryAbility], when: inuse } }, { name: ohos.permission.READ_IMAGEVIDEO, reason: 用于读取相册中的文物照片进行识别, usedScene: { abilities: [EntryAbility], when: inuse } }, { name: ohos.permission.MICROPHONE, reason: 用于语音提问, usedScene: { abilities: [EntryAbility], when: inuse } } ] } }第二处是在代码里动态申请。这里有两个坑必须说。第一个坑权限弹窗被拒绝后系统不会自动二次弹窗。我一开始以为像某些系统那样“再次调用就会重新弹”结果发现拒绝之后系统直接返回 denied再也不弹了。正确做法是在业务层记住“用户曾拒绝”下次触发时引导到应用设置页手动开启否则用户会一脸懵。第二个坑READ_IMAGEVIDEO的授权时机。有的教程建议在启动页就把所有权限都申请了但实测下来鸿蒙对“使用场景说明”的校验比较在意如果权限声明里写了when: inuse最好真的在用到的时候才触发申请启动时全量申请反而容易被系统拒绝体验也更差。3.2 拍照识别链路一张铜狮子照片是怎么变成解说词的拍照识别的核心链路只有三步拿到图片 base64 字符串、拼装 MaaS 请求、解析返回结果。以下是一个 ArkTS 里的请求示例接口路径以平台文档为准这里用兼容格式示意import http from ohos.net.http; function recognizeRelic(imageBase64: string, prompt: string): Promisestring { const httpRequest http.createHttp(); return httpRequest.request( https://your-maas-endpoint.example.com/v1/chat/completions, { method: http.RequestMethod.POST, header: { Content-Type: application/json, Authorization: Bearer apiKey }, extraData: JSON.stringify({ model: multimodal-v1, temperature: 0.3, messages: [ { role: user, content: [ { type: image, image: data:image/jpeg;base64, imageBase64 }, { type: text, text: prompt } ] } ] }) } ).then((res: http.HttpResponse) { const data JSON.parse(res.result as string); return data.choices[0].message.content; }); }这段代码里值得解释的有三个点。第一图片用 base64 直接内联在 JSON 里方便但不适合大图。实测 2MB 以上的照片请求体变大不说上传耗时会明显增加所以在客户端先做了压缩统一压到最长边 1280 像素、质量 80%识别效果几乎不受影响体感速度快了不少。第二temperature压到 0.3。识别类任务的答案相对确定温度太高模型会忍不住“创作”把铜狮子说成石狮子。后面的翻车记录里我还会讲。第三prompt 是整个系统的灵魂。我用的 prompt 大致是这个样子你是一名熟悉故宫的资深导游。 请识别图片中最主要的建筑或文物按以下顺序回答 1. 名称 2. 年代 3. 用途 4. 一句适合讲给普通游客的亮点介绍 如果看不清请直接回答“我看不清换个角度再试一次”。 不确定的年代和数量请用“大约”“史料记载不一”等方式表述 不要猜测不要编造。这段 prompt 后来被我迭代了三版。第一版没加“看不清就直说”的时候模型经常对着反光照片强行编答案这个问题在下面的实测部分会详细展开。3.3 语音问答热词兜底 大模型对话 TTS 播报语音交互是体验上的关键也是最容易翻车的部分。我把它拆成三段。第一段是录音识别。方案是在鸿蒙端先做本地热词识别热词表里预置了“太和殿”“铜狮子”“石狮子”“斗拱”“藻井”“金水桥”等二三十个必看词汇。这样设计的考虑是现场环境嘈杂纯云端大模型语音识别容易听岔而本地热词识别在离线状态下也能命中命中后直接走本地缓存速度快且不花钱。如果热词没命中就把录音转文字请求发给语音识别接口识别成自由文本后再进 MaaS 问答。第二段是自由问答。用户问“太和殿是干嘛的”把文本和上文对话一起发给 MaaS连续对话保持上下文记录这样孩子可以一路追问“那它里面是什么样的”“皇帝在这里做什么”不用每次重新描述问题背景。第三段是播报。MaaS 返回的纯文本调用鸿蒙的语音合成能力转成音频播放。这里有个体验细节不要等整段文本全部生成完再播那样等待太久最好请求时开启流式输出拿到第一句话就开始合成播报让用户感觉“它已经在说话了”。第一个版本没开流式实测网络不好的时候从按下快门到听到声音经常要等 4 秒以上体验非常糟糕改成流式之后首字延迟降到了 1 秒左右。3.4 本地缓存设计没网的时候也不能哑火故宫的现场网络状况比想象中差。三大殿区域还好一旦走到东六宫的夹道、或者钻进一些建筑深处信号会直接掉到一两格。所以我把离线兜底做成了系统的一等公民出行前通过网关层把 Top 20 必看展品的图文、问答库、讲解音频一次性拉取到本地沙箱存成 JSON 和音频文件运行中所有请求先查本地缓存命中就秒回没有命中才走网络网络请求失败时自动降级先尝试本地热词匹配再尝试提示用户切换为文本输入模式。这里有一个判断逻辑值得说一下我用的是“先本地、后云端”的策略而不是“先云端、本地兜底”。原因是故宫里同一件文物用户可能反复问好几轮本地缓存能让这些高频问题完全不消耗 API 费用也完全不受网络波动影响。实测下来最拥挤、网络最差的中午时段靠本地缓存撑住了大概三分之一的查询。4. 故宫实测参数好看不等于现场好用4.1 我记录了一组现场数据整个假期我在故宫分两天做了实测一次走中轴线主线一次专门钻东西六宫和珍宝馆。以下数据是当时的真实记录项目次数成功平均延迟备注拍照识别3831另有 4 次文字辅助成功首字约 2.1 秒失败主要发生在反光和人群遮挡场景语音自由问答2218首字约 1.4 秒4 次失败集中在嘈杂环境本地热词命中15150.2 秒内完全离线体验最稳定半天持续使用耗电——从 100% 降至 53%全程亮屏 拍照 语音API 侧的花费单独算一笔账两天加起来 MaaS 端大概消耗了 12 万 token账单算下来不到 20 元人民币。这个成本对个人项目非常友好也说明这类应用在规模化之前“按量付费”是性价比最高的启动方式。4.2 三个翻车现场反光、遮挡和一本正经的胡说八道数据好看是结果过程其实翻了三次车。第一次翻车发生在珍宝馆。隔着玻璃展柜拍照玻璃反光把走廊里的游客轮廓清清楚楚地拍进了画面模型硬是把反光轮廓识别成了“浮雕纹饰”还自信地给出了介绍。修复方式分两层prompt 里加了“如果画面中存在玻璃反光、人影等干扰请提示用户重新拍摄”UI 层则在识别结果返回低置信度标记时提示用户“斜着约 30 度重新拍一张可减少反光”。第二次翻车在铜狮子跟前。节假日这里永远有人排队合影拍到的画面里全是后脑勺和自拍杆模型识别出来的东西五花八门。这次的教训是不要试图让模型去解决“拍摄角度”这种物理问题而是给应用加一个“文字搜索”兜底用户拍不到、识别不出的时候直接搜“铜狮子”三个字走本地资料库照样完成讲解。功能没变但用户不会卡死在“识别失败”这一步。第三次翻车最有意思也最值得警惕模型一本正经地胡说八道。它把铜狮子说成石狮子把太和殿屋顶的脊兽数量从官方口径的 10 个说成 9 个。多模态模型在“看图”时会产生幻觉尤其是细节数量这种容易混淆的信息。我最终的 prompt 里明确写了“不确定的年代和数量请用大约等方式表述”并且要求系统在输出尾部追加一句“以上信息请以现场官方讲解为准”。这样既降低了错误信息的置信度也规避了后续可能的知识准确性问题。4.3 救急设计把识别失败变成一次交互机会原型阶段识别失败就弹一个“识别失败”的提示实测时发现这条路走不通——游客在人群里本来就急躁看到失败提示只会觉得应用没用。后来我改成识别失败不再当作失败而是当作一次“求助信号”自动切换到文本输入模式并且根据画面中模型能读出的少量文字给出几个高频问题作为候选项。这个设计在体验上的差别非常大。识别失败后用户不是面对一个死胡同而是面对一个“引导你换种问法”的智能助手心理上会觉得是自己在问问题的方式不对而不是应用坏了。实测里那 4 次文字辅助成功的场景全部走的就是这条路径算是把“失败”转化成了“对话”。5. 复盘与复用这套“AI 导游”还能跑到哪里去5.1 成本与投入的全部账单很多开发者关心做这样一个项目到底要投入多少。按我这次的实际开销算MaaS API 费用两天不到 20 元人民币鸿蒙开发没有服务器成本唯一的服务端是个转发缓存程序跑在一台闲置机器上开发时间是假期里的三个整天中间还包含两轮重构。如果你熟悉 ArkTS单人三天做个最小可用版本完全可行。这里的隐性成本其实是“prompt 调试”和“现场实测”这两件事没法靠文档替代必须带着应用去真实场景里跑一遍才知道问题在哪。5.2 能马上复用的场景清单这套“拍照识别 语音问答 离线缓存”的组合本质上是把“对特定领域知识的随身问答”做成了一件商品。换个领域其实就是换 prompt、换知识库、换热词表的事其他博物馆和美术馆把故宫的展品库换成对应馆藏prompt 换成“你是一名熟悉本馆的讲解员”。高校校园和园区导览识别建筑照片、回答“这栋楼是哪个学院”技术路径一模一样。展会、科技周、开放日现场讲解员不够用的时候这完全可以当临时的“布展导游”。城市老街区、公园景区把固定讲解点做成二维码或图片识别扫到就播。5.3 下一版我想做的迭代方向这套东西我还会继续用目前排在迭代计划里的有三件事。第一是接入定位能力走到某个殿附近自动触发解说不用每次手动拍照但要注意故宫里 GPS 精度和信号都比较弱需要配合建筑识别做二次确认。第二是加强多轮记忆让孩子可以连续追问“为什么”“然后呢”这需要在对话上下文里保存更完整的游览轨迹。第三是把讲解音频做成可下载的离线专辑准备一个“家庭模式”和一个“深度模式”同样是 MaaS 生成只是 prompt 里的语气和篇幅不同。最后再分享一个我个人的体会。项目做到一半的时候我一度沉迷于打磨识别准确率后来在实测现场才想明白游客需要的不是一个永远正确的百科全书而是一个能接住“现场提问”的对话伙伴。识别失败没关系关键是失败之后怎么引导用户继续往下走。这个道理放到任何 AI 伴游类应用里都成立。如果你也想做一个类似的 AI 导游我的建议是别一上来就做全功能先把“拍照识别 语音问答”这组最小闭环跑通带着它去一次人多的景区你自然就知道下一版该改什么了。