先抛一个问题:你上一次在车里因为导航“听不懂”而烦躁是什么时候?
“导航去未来科技城。” “找到多个相关地点,请问您要去哪一个?” “去昨天那个。” “抱歉,没有找到昨天那个地点。”
这段对话看起来非常常见,但它暴露了一个产品断层:地图数据再新、路线算法再快,只要系统记不住上下文,用户就仍然要用一种“精确、单调、反人类”的方式与机器沟通。华为鸿蒙座舱近期发布的 HarmonySpace 暑期版本,把“导航 Agent”和“地址主动记忆”作为核心更新点,正是想去解决这个断层。
我的判断很明确:车机导航的竞争,正在从“地图数据”和“路径算法”转向“意图理解”和“场景记忆”。谁能让用户在车内少说一句、少点一步,谁就掌握座舱体验下一阶段的主动权。这篇文章不打算写成新闻稿,而是按技术拆解的方式展开:先说明 HarmonySpace 在鸿蒙座舱里到底处于什么位置;再拆解导航 Agent、地址主动记忆分别解决了什么问题、带来了什么新约束;最后给出一份可参考的鸿蒙车机端地址记忆实现方案,包括项目结构、数据模型、权限配置、数据库设计和核心代码。读完你至少能建立一套“车机 Agent 功能从设计到落地”的完整思路。
1. 为什么这次发布值得关注
先从宏观视角看。智能座舱行业过去几年的主线是“屏幕变大、芯片变快、应用变多”。但这些东西做到一定程度后,用户会遇到新的瓶颈:应用多了,反而不知道用什么;语音助手能回答,但记不住上一句话;导航知道路线,但不知道你为什么走这条路。
HarmonySpace 暑期版本选择以“导航 Agent”和“地址主动记忆”作为更新重点,说明华为在座舱用户体验上的取舍已经发生变化:不再只追求车机“能跑更多 App”,而是追求车机“更懂当前的人和当下的场景”。这两个能力放在导航场景,能发挥的价值是最高的,因为导航在车机上用量最大、操作路径最长、用户耐心最低。任何一个能减少用户操作步骤的升级,都会被高频场景放大成明显的体验差异。
从技术代际来看,这也是一次交互范式的转换。过去用户要“命令”机器:说出准确地址,加上限定词,慢慢等它执行。Agent 化之后,人只需要表达意图,系统负责补全上下文。这个转变对车机体验的改善,比单纯提升语音识别率要直观得多。所以这次版本更新,真正值得关注的不是“新增了几个功能”,而是它验证了一条新的交互路径:座舱系统开始把复杂度和上下文负担,从用户一侧转移到系统一侧。
2. 先理清 HarmonySpace 在鸿蒙座舱里的位置
在讨论代码之前,先把概念对齐,否则很容易把 HarmonySpace 理解成“一个普通车机应用的升级”,这显然低估了它的定位。
2.1 HarmonyOS 智能座舱不是“车机安卓”
传统车机系统的核心是“在车上跑一堆应用”:地图是一个应用,音乐是一个应用,设置是一个应用,应用之间互相独立。HarmonyOS 智能座舱延续了 HarmonyOS 的分布式设计理念,强调的是设备协同与场景联动。车机不是孤立的硬件,而是手机、手表、智能家居等设备网络里的一个节点。
举个例子,手机上的导航任务可以无缝流转到车机;车机上的日程可以读取手机中的会议信息;用户下车后,还没走完的路线可以继续在手机上接力。这套“跨端流转”能力,恰恰是地址主动记忆能成立的基础之一:地址数据既来自车机,也来自手机,最后还能回到手机端继续为用户服务。
2.2 HarmonySpace 可以理解为座舱的“空间体验底座”
从公开信息来看,HarmonySpace 可以理解为鸿蒙座舱面向车内空间体验的一套能力体系。它不是某一个 App,而是承载多屏交互、服务分发和场景识别的基础层。HarmonySpace 暑期版本的更新重点落在导航场景,相当于在底座上先长出一个最常用、最能体现 Agent 价值的应用形态。
这样理解起来就顺了:HarmonySpace 负责的是“空间内如何组织服务”,导航 Agent 负责的是“导航服务如何被智能化调度”,地址主动记忆负责的是“服务如何记住用户的真实生活”。三者叠加,车机才从“被动打开工具”升级为“主动理解场景”。
2.3 传统车机与 HarmonySpace 的本质差异
用下面的表格可以一眼看出差异:
| 对比维度 | 传统车机系统 | HarmonySpace 的鸿蒙座舱 |
|---|---|---|
| 交互入口 | 触屏为主,语音为辅 | 语音、手势、触控等多模态 |
| 应用形态 | 独立车机应用 | 原子化服务、卡片、应用服务协同 |
| 设备关系 | 手机投屏到车机 | 手机、车机、手表无感协同 |
| 服务触发方式 | 用户主动打开应用 | 场景和意图驱动主动服务 |
| 记忆能力 | 单机存储,弱记忆 | 分布式数据,跨设备延续 |
| 导航体验 | 听指令,查地图,给路线 | 理解意图,补全上下文,编排任务 |
这个差异决定了开发者写代码的方式也会变化。以前只需要做好一个 App 的逻辑,现在要更多考虑“服务如何被系统识别、调度和组合”。
3. 导航 Agent:从“语音查询”到“导航管家”
3.1 一次传统导航的完整链路
过去车机语音导航的工作流程可以拆成五步:
- 用户说出地址,例如“导航去人民公园”;
- 语音识别把声音转成文字;
- 文本匹配地图 POI(兴趣点)列表;
- 用户从候选结果里手动选择;
- 系统开始规划路线并导航。
这套流程有三个明显的痛点。第一,多轮对话没有记忆,如果你说“去刚才那个公园”,系统只能重新查一遍;第二,模糊表达无法处理,比如“去上次加油的加油站”,系统不知道“上次”是什么;第三,服务之间没有串联,导航就是导航,充电、停车、到点提醒都是独立操作,用户要一步一步自己安排。
3.2 Agent 版导航的变化
引入导航 Agent 之后,工作流会变成这样:
- 意图提取:从用户语音中提取“动作 + 对象 + 约束条件”。比如“今天下班后去接孩子”,动作是“导航”,对象是“学校”,约束条件是“下班后”“接孩子”。
- 上下文融合:结合当前时间、当前位置、历史地址、最近搜索记录,判断最可能的候选。
- 澄清确认:如果候选仍不唯一,用一句话让用户确认,而不是抛出一列表让用户在驾驶中分心选择。
- 任务编排:根据任务类型联动其他能力。如果目的地需要充电,Agent 同时推荐沿途充电站;如果到达时间晚,Agent 提醒是否设置到家提醒。
- 动态调整:行驶途中根据实时路况自动重规划,并对话式告知用户,而不是突然改变路线然后不做解释。
这意味着,导航不再是一个“检索动作”,而是一个持续运行的任务状态。用户不是发起一次请求就结束,而是随时可以追加约束、修改目标、询问进度。Agent 的核心价值是把这个状态完整地维持住。
3.3 “少问一句”的设计差别
导航 Agent 体验好不好,很大程度上取决于它是否能“少问一句”。这里的差别在于对用户历史数据的利用层次。
普通语音助手的逻辑是“你说了地址,我帮你搜”;导航 Agent 的逻辑是“我知道你工作日早晨大概率去公司,你刚说‘导航去上班’,我就直接把公司设为目的地,并推荐一条避开早高峰路线”。前一句是功能,后一句是服务。从产品层面看,这背后的关键指标不是“语音识别准确率”,而是“平均交互轮数”和“从发起到导航开始的操作时间”。HarmonySpace 把地址主动记忆和导航 Agent 放在一起发布,用意也在这里:Agent 负责理解意图,记忆负责提供上下文,两者结合才能实现少问一句。
4. 地址主动记忆:技术上有四个关键层
“地址主动记忆”这六个字听上去简单,真实落地时要拆成四层来看:数据层、意图层、场景层、边界层。每一层都有各自的难点。
4.1 数据层:地址不只是经纬度
地址记忆的第一个难点,是确立数据模型。一个地址不能只存经纬度,它还需要包含来源、语义标签、访问频次、最近访问时间、关联场景等信息。例如“家”这个地址,对应的数据结构应该包含:
- 经纬度坐标;
- 来源是用户手动设置、语音识别、还是从聊天记录中提取;
- 标签,比如“家”“公司”“孩子学校”;
- 使用时间分布,比如工作日早晨使用频率高;
- 访问频次,用于排序召回。
只有把地址建模成“带语义的实体”,而不是一串坐标,Agent 才能在用户说出模糊表达时,从记忆库里快速定位正确的候选。这一层做不好,后面的意图层和场景层都是空谈。
4.2 意图层:区分“一次性指令”和“值得记忆的点”
不是用户说过的每个地址都值得被记忆。比如“导航到北三环中路某商场”可能是临时查一次;“导航到女儿的幼儿园”则是高复用的固定地址。系统需要自动判断一个地址的“记忆价值”。
这个判断通常依赖几个信号:地址出现的频率、时间分布是否稳定、用户是否使用了固定称呼(比如“家”“公司”“学校”)、以及用户在后续对话中是否重新提到它。更稳妥的做法是“先短期记住,再逐步强化”。第一次出现时只记入短期缓存,当同一个语义地址在近一个月内反复出现,才提升为长期记忆。这样可以避免把一次偶然去过的地址永久塞进记忆库。
4.3 场景层:何时“主动”才不打扰
主动记忆最容易产生的副作用是“过度主动”。如果车机在用户每次上车都弹出“我猜你要去公司”,用户很快会厌烦。好的场景触发一定带有强烈的时机特征。例如:
- 工作日早上 8 点到 9 点,车辆启动,且历史记录显示用户经常在这个时段去公司;
- 周五下班时间,用户上车后犹豫没有输入目的地;
- 电量低于 20%,且目的地距离较远,主动提示沿途充电站;
- 用户说出“去接孩子”这类强语义短语,直接调用记忆中对应地址。
主动推荐的频率和位置也很关键。推荐不应该占满屏幕,而是以一张轻量卡片、一句话语音询问的方式出现。用户接受则执行,不接受则轻扫关闭,整个过程不能产生强烈的被打断感。
4.4 边界层:隐私与可控性是底线
地址记忆涉及用户的移动轨迹和家庭住址,这是高度敏感的个人信息。技术实现上必须做到几个基本要求:记忆数据本地优先,默认不上传;用户可以在设置里查看全部已记忆地址;支持单条删除和全部清空;支持“记忆开关”和“使用场景开关”;如果功能调用云端能力,必须在用户授权后按最小必要原则上传,并提供清晰的权限说明。
这一层不是附加项,而是功能能否安全上线的先决条件。从工程角度看,地址记忆模块必须设计成“默认可关闭、数据可导出、记录可审计”的形式,否则在隐私合规上很容易出问题。
5. 鸿蒙开发者怎么接住这波变化
HarmonySpace 发布导航 Agent 和地址主动记忆,短期看是产品迭代,长期看会影响鸿蒙车机应用的设计方式。开发者应该预判到这种变化,提前调整自己的技术方案。
5.1 从“应用为中心”到“服务为中心”
过去做车机应用,重点是做好一个独立 App:有自己的界面,有自己的数据,有自己的一套交互。Agent 化之后,应用的一些核心能力会变成可被系统调度的“服务”。比如加油应用不再只等待用户打开,而是把“查找附近加油站”“对比油价”“一键导航”抽象成服务,交给座舱的 Agent 在用户需要时去调用。
这就对应用架构提出了新要求:业务逻辑不能全部绑死在 UI 层,核心能力需要用独立的 Service 模块暴露出来,同时要有清晰的对外接口和权限控制。否则当座舱系统想调用你的能力时,发现只能打开一个页面,就无法实现轻量调度。
5.2 适合优先尝试的几类服务
从座舱场景看,有三类应用最适合率先与 Agent 能力结合。
第一类是高德地图、百度地图等导航地图类应用,可以直接把“搜索地点”“路线规划”“多轮导航”封装成 Agent 可调用的服务。第二类是充电、加油、停车类应用,和“电量不足”“到达停车场”等事件强相关,适合被 Agent 在场景触发时调用。第三类是通勤和日程类应用,比如会议日历、通勤播报,能够为地址记忆补充时间维度信息。
如果你正在做这三类应用,建议在鸿蒙 Next 或 HarmonyOS 车机版的工程里,提前把核心功能做成可独立验证的原子化服务或卡片,而不是把一切都堆进一个巨型 App。
5.3 需要提前构建的三种能力
从技术准备角度,建议优先构建三种底层能力。
一是地址语义化能力:把用户习惯的称呼(“我家”“公司”“孩子学校”)与标准地址、经纬度做映射,并维护一张“别名表”。二是行为时序能力:记录地址访问的时间戳、频次、星期分布,为后续主动推荐提供特征输入。三是权限与隐私管理能力:建立完整的地址数据生命周期管理,从采集、存储、使用到删除都有一套可回溯的流程。这三项能力不依赖 HarmonySpace 是否开放 API,现在就可以在自己的工程里开始积累。
6. 可参考的地址记忆实现代码示例
下面给出一份基于 ArkTS 的参考实现。需要先说明:HarmonySpace 底层的语音 Agent 和座舱服务接口会由官方逐步开放,本文的代码主要用于演示“地址记忆”模块的数据建模、权限声明、存储设计和意图定义。你可以在自己的鸿蒙应用中先按这套结构搭出基础模块,等官方接口开放后再接入。
6.1 工程目录结构
推荐采用如下分层结构,把模型、存储、服务与页面分离:
entry/ ├── src/main/ │ ├── ets/ │ │ ├── entryability/ │ │ │ └── EntryAbility.ets │ │ ├── model/ │ │ │ └── AddressModel.ets │ │ ├── service/ │ │ │ ├── AddressMemoryService.ets │ │ │ └── NavIntentService.ets │ │ └── pages/ │ │ └── Index.ets │ ├── resources/ │ │ └── base/ │ │ ├── element/ │ │ └── media/ │ └── module.json5 └── build-profile.json5model目录放地址实体,service目录放地址记忆和意图解析逻辑,pages目录只放界面代码。这种结构方便后续把 service 层暴露成更通用的能力接口。
6.2 地址数据模型
下面的代码定义了一个带语义信息的地址实体。它不只是经纬度,还包含来源、标签、访问频次和最近访问时间。
// 文件路径:entry/src/main/ets/model/AddressModel.ets /** * 智能地址数据结构 * 用于构建“地址主动记忆”的基础数据模型 */ export class SmartAddress { addressId: string; // 地址唯一标识 displayName: string; // 用户可读的名称,例如“公司” latitude: number; // 纬度 longitude: number; // 经度 source: string; // 来源:voice | map | message | manual isHome: boolean; // 是否标记为家 isCompany: boolean; // 是否标记为公司 visitCount: number; // 访问次数 lastVisitTime: number; // 最近访问时间(时间戳) tags: string[]; // 自定义标签,例如“孩子学校”“健身房” constructor() { this.addressId = ''; this.displayName = ''; this.latitude = 0; this.longitude = 0; this.source = 'voice'; this.isHome = false; this.isCompany = false; this.visitCount = 0; this.lastVisitTime = 0; this.tags = []; } }这个模型的关键点在于:source字段记录了地址的来源,便于后续分析哪些地址来自用户主动设置、哪些来自 Agent 自动识别;visitCount和lastVisitTime则是判断“是否值得长期记忆”的重要特征。
6.3 权限声明与模块配置
在鸿蒙应用中,访问位置、访问网络都需要在module.json5里声明权限。以下配置演示了地址记忆模块最基础的权限申请方式。
// 文件路径:entry/src/main/module.json5 { "module": { "name": "entry", "type": "entry", "requestPermissions": [ { "name": "ohos.permission.APPROXIMATELY_LOCATION", "reason": "用于获取当前位置,辅助导航地址记忆", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } }, { "name": "ohos.permission.INTERNET", "reason": "用于地图服务与云端地址同步" } ] } }需要强调一点:这里的权限名称是基于常见鸿蒙应用开发的通用写法,具体权限声明要以你使用的 SDK 版本和官方文档为准。位置权限属于敏感权限,运行时会弹出授权弹窗,你的应用必须给用户清晰的授权理由,并在用户拒绝后提供降级方案。
6.4 本地数据表结构
地址记忆建议使用鸿蒙的 RelationStore 在本地保存,表结构可以这样设计:
-- 智能地址记忆表结构(示意) CREATE TABLE IF NOT EXISTS address_memory ( id INTEGER PRIMARY KEY AUTOINCREMENT, address_id TEXT NOT NULL, display_name TEXT NOT NULL, latitude REAL NOT NULL, longitude REAL NOT NULL, source TEXT DEFAULT 'voice', visit_count INTEGER DEFAULT 0, last_visit_time INTEGER DEFAULT 0, home_flag INTEGER DEFAULT 0, company_flag INTEGER DEFAULT 0, tags TEXT ); CREATE INDEX idx_address_memory_last_visit ON address_memory(last_visit_time);单独建一个last_visit_time索引,是为了支持“最近使用地址”的快速召回。在车机场景里,用户说“去上次那个地方”时,你需要按最近访问时间倒序取候选,这个索引能明显降低查询耗时。
6.5 记忆服务核心逻辑
下面是一个简化版的AddressMemoryService,演示如何把上面的模型写入数据库。这里的写法用于演示模块职责,API 名称在当前版本可能有差异,请以手上的 SDK 为准。
// 文件路径:entry/src/main/ets/service/AddressMemoryService.ets import { relationalStore } from '@kit.ArkData'; import { BusinessError } from '@kit.BasicServicesKit'; import { SmartAddress } from '../model/AddressModel'; const STORE_CONFIG: relationalStore.StoreConfig = { name: 'address_memory.db', securityLevel: relationalStore.SecurityLevel.S1 }; export class AddressMemoryService { private store: relationalStore.RdbStore | undefined = undefined; async init(context: Context): Promise<void> { this.store = await relationalStore.getRdbStore(context, STORE_CONFIG); } async saveAddress(item: SmartAddress): Promise<void> { if (!this.store) { throw