本系列:从 0 到 1 入门智元灵犀 X2 二次开发
本篇产出:一个修复后的端云联合工程,以及一套可复用的缺陷定位方法。
前置:第 03 篇(端侧接口)+ 第 04 篇(云端 SDK)。本篇需要真机。
一、职责边界
端云接缝处的代码该写在哪一层,直接决定系统是否可维护。判据如下:
| 维度 | 端侧(AIMDK / ROS 2) | 云端(AgentSDK + LLM) |
|---|---|---|
| 职责 | 实时控制、安全急停、感知、音频采集 | 语义理解、规划、知识、长期记忆 |
| 时延量级 | 毫秒级闭环 | 百毫秒 ~ 秒级 |
| 离线能力 | 必须可降级运行 | 可降级为规则兜底 |
| 典型能力 | 走跑、避障、关节 / 末端、灵创动作、表情灯带 | 意图仲裁、RAG、任务编排、多轮对话 |
| 迭代成本 | 高(需真机发版) | 低(服务端灰度) |
判据:需要毫秒级反应的放端侧,需要”理解”和”决策”的放云端。迭代频率是第二判据——需要频繁调优的逻辑放云端,否则每次改动都要过一遍真机发版流程。
一次完整对话的时序
一次对话的整个生命周期由
event_id关联——跨多次on_request与多次response.on_*调用共用同一个event_id。
本系列的选型
采用 asr2llm:ASR 在端侧完成,云端只处理文本。理由是链路最短、延迟最低、调试最直观。代价是云端拿不到音频原始特征(情绪、语速、停顿)——若业务需要这些信息做情绪判断,应改用 audio2llm,并接受链路变长。
二、on_state:让 Agent 看得见
官方 demo 完全没实现 on_state,这是它离”智能”最远的一步。机器人通过 agentsdk.state_request.meta 事件上报自身状态:
1 | PassiveCallback.on_state(agent_id, event_id, state_name, state_value) |
⚠️
state_value的类型不统一:可能是简单字符串、数字、对象或对象数组。结构化字段需自行json.loads(state_value)解析——但robot_form是裸字符串,对它调用json.loads会抛异常。
22 个状态模块
state_name |
主题 | 频率 |
|---|---|---|
map_info |
地图基础信息与导航点位 | 低频 / 事件触发 |
map_2d_grid |
二维栅格地图快照 | 极低频 |
current_location |
当前语义位置 | 事件触发 |
robot_pose |
几何位姿 | 高频(≤10Hz) |
planned_path |
当前算路路径 | 事件触发 |
locomotion_status |
导航执行状态 | 事件触发 |
pnc_status |
规控避障状态摘要 | 事件触发 |
task_status |
任务状态列表 | 事件触发 |
exhibit_tasks |
展厅讲解任务列表 | 事件触发 |
silisoon |
业务模式与状态扩展 | 事件触发 |
robot_form |
机器人形态 | 一次性 / 偶尔 |
manipulation |
机械臂 / 抓取状态 | 事件触发 |
supported_calligraphy_list |
书法可写文字列表 | 一次性 / 偶尔 |
tts_status |
TTS 运行态 | 事件触发 |
vad_status |
VAD 运行态 | 事件触发 |
dialog_status |
对话状态机 | 事件触发 |
action_status |
动作 / 技能任务状态 | 事件触发 |
listening_status |
端侧聆听 / 唤醒状态 | 事件触发 |
wakeup_behavior_status |
唤醒表现覆盖配置状态 | 事件触发 |
vision_perception |
视觉感知摘要 | 高频(≤10Hz) |
speaker |
当前发言人信息 | 事件触发 |
settings |
端侧开关状态面板 | 事件触发 |
三条使用纪律:
- 频率敏感。
robot_pose与vision_perception默认按模块 ≤10Hz 限频,不要在回调里做重 I/O(写数据库、发 HTTP)。 - 整体替换语义。
planned_path/exhibit_tasks/supported_calligraphy_list等是整数组替换,不要做点级 merge。 - 空态显式上发。无任务时约定上发
task_status = [{"state":"idle","task_name":""}]。
World Model
把所有状态聚合成一个按 agent_id 分桶的世界模型:
1 | import json |
有了 World Model,一批”看起来需要额外开发”的能力变成查表:
| 需求 | 依赖字段 |
|---|---|
| 避免重复下发导航 | locomotion_status.state |
| 回答”还有多久到” | planned_path.eta_sec / remaining_length_m |
| 判断能否接新动作 | action_status.state |
| 判断是否正在播报、能否打断 | tts_status.state |
| 区分服务对象 | speaker.speaker_id + vision_perception |
| “去前台”解析为坐标 | map_info.navi_points |
三、语义点位:把地名变成坐标
map_info 携带 navi_points:
1 | { |
1 | def resolve_poi(agent_id: str, name: str): |
取舍:把坐标硬编码在代码里的成本,在地图变更时会一次性暴露——地图重采后所有点位失效,而代码里没有任何地方能提示这一点。走
navi_points后,地图变、代码不变。唯一需要写死的只有地图 ID 与初始位姿,这两项也应外置到配置文件。
另一种导航方式是云端任务流提供的 pilot_task(定点导航 / 动线讲解),支持 pilot_start_task / pilot_resume_task / pilot_stop_task,配合 target_name 直接使用点位名。第 06 篇展开。
五、Demo
整体架构如下:
1 | #!/usr/bin/env python3 |
六、调试与排障
| 环节 | 排查手段 |
|---|---|
| 云端连不上 | 看 on_auth_state 的 code;确认网关与凭证是 v1.4.0 |
| 收不到语音 | 确认机器人绑定的智能体 ID 与 SDK 注册的回调类型匹配 |
on_state 无数据 |
确认端侧已开启状态上报;先打印 state_name 看实际收到哪些模块 |
| 端侧调用失败 | 看 service 返回的 header.header.code 与 header.message |
| 机器人不响应后续指令 | 检查 on_request 内是否有同步阻塞 |
| 状态解析抛异常 | state_value 类型不统一;robot_form 是裸字符串 |
经验:接
on_state的第一步不是写业务逻辑,而是在回调里加一行开关式日志,把 22 个模块实际收到的原始载荷打出来。文档给的是字段定义,真机上报的内容与频率往往有出入,先看真实数据再决定用哪些字段。
结论
- 端云判据:毫秒级反应放端侧,理解和决策放云端;迭代频率是第二判据。本系列选
asr2llm,链路最短。 on_state是让 Agent 看得见的关键,最该先接的是map_info/locomotion_status/robot_pose/tts_status。- 用
map_info.navi_points把地名解析为坐标,地图变更时无需改代码。 - 官方 demo 的 5 个缺陷:
navHandler作用域、_parse_nav分支顺序、缺on_interrupt、on_request阻塞、凭证硬编码。 - 规则式意图解析的原则是先排除后命中——否定类规则必须排在正向规则之前。
- 端侧调用异步化是安全的:
response.on_*可在任意线程调用,但受 120 秒 Response TTL 约束。
下一篇是完结篇:从”语音遥控器”升级为真正的 Agent——主动交互任务流、四层闭环架构、任务状态机与任务栈、记忆层,以及两个完整的复杂场景案例。
声明
本文首发智元AIMA开放平台,并在博客同步