做车载 HMI 测试这几年我最深的体会是用例本身不是瓶颈怎么把“跑用例”这件事组织起来才是。早期我们靠人肉盯屏后来上了自动化框架但每次回归仍然要测试人员在工位上守着车机、盯着日志、等结果再手工整理一份报告丢到群里。直到我们把测试入口搬进飞书群再叠一层 AI Agent 做任务编排整个流程才算真正松绑。这套平台解决的核心问题很直接测试人员在飞书群里发一句话AI Agent 自动拆解需求、调度车载 HMI 执行引擎跑用例、收集截图和日志、最终回传一份带失败分析和修复建议的测试报告。它不是简单挂个脚本机器人而是把“测试任务”当成一项可以被理解、被调度、被反馈的服务来设计。文章主要面向正在做车载系统测试、又想引入 AI 能力的团队。不管你是测试开发、质量基建负责人还是对 AI Agent 架构感兴趣的后端工程师下面这套设计思路和踩坑记录应该都能给你一些参考。1. 方案选型与整体设计思路先聊清楚为什么做和怎么做。任何平台类项目如果一开始没把需求想透后面大概率会变成工具功能的堆砌。1.1 传统车载 HMI 测试的痛点拆解车载 HMI 的自动化测试跟普通 App 自动化有本质区别。车机系统版本杂、屏幕分辨率不统一、硬件平台从高通到瑞萨都有再加上方向盘按键、旋钮、语音等多模态交互测试环境本身就比手机端复杂。传统做法通常是这样的测试人员手写 Python 脚本或使用 QTest、TestComplete 这类工具把用例固化成代码然后在固定的测试台架上跑。这套模式有几个反复被吐槽的痛点。第一脚本维护成本极高。HMI 界面只要换个布局、改个文字颜色、调整控件 ID脚本就要跟着改。一个中控屏的冒烟测试脚本平均生命周期可能只有两三个版本。第二执行结果反馈慢。自动化跑完了报告散落在本地或 CI 平台里测试人员要主动去翻日志、看截图、对比基线。发现问题后还要手动截图、录屏、整理时间线再通过 IM 发给开发沟通链路长且容易丢上下文。第三任务调度刚性。传统 CI 平台的任务触发方式大多是定时执行或代码提交触发测试人员想在群里临时发起一轮“特定分辨率的回归测试”往往要等环境、改参数、重新排队效率很低。所以我们的目标非常明确把任务入口做得足够轻把执行结果做得足够闭环。1.2 为什么选“飞书机器人 AI Agent”这套组合为什么入口选飞书而不是自研一套 Web 平台不是因为飞书有多炫而是团队日常协作本来就在飞书里。测试人员在群里讨论需求、开发在群里同步进度、项目经理在群里看报表把测试任务直接做成一个可以 的机器人学习成本几乎为零。我们之前也试过自建 Web 平台结果很真实测试人员需要打开浏览器、登录账号、进入项目页面、找到任务模板、填参数、启动任务。这一套操作在忙碌的时候根本没人愿意做。而飞书群里“机器人 跑一下极简仪表盘的冒烟”这个动作顺手到不需要思考。AI Agent 在这套平台里的角色不是取代测试框架而是充当任务理解和编排大脑。它能帮我做三件事从自然语言里提取执行意图、把复杂任务拆解成可执行的步骤序列、在结果出现异常时结合截图和日志做初步根因判断。说白了飞书机器人负责“人和系统对话”AI Agent 负责“让对话变成可执行动作”。1.3 架构分层设计总览整个平台按职责分成四层每层只做自己该做的事层与层之间通过标准接口通信。接入层是飞书机器人服务处理消息订阅、指令解析、任务回执和报告推送。上层体验全在这里完成。智能层是 AI Agent 编排模块负责意图理解、技能路由、失败分析和报告生成。这一层不直接操作车机只做决策。执行层连接车载 HMI 自动化引擎管理被测设备、执行用例、采集截图和日志。它是整个平台里离硬件最近的一层。数据层负责存储用例、任务记录、执行日志、报告文件和测试基线。这个分层设计看起来很“标准”但真正落地的时候每层都可能因为车载环境的特殊性而出问题。比如执行层的设备连接池跟普通安卓真机集群有很大差异后面我会详细说。2. 飞书机器人交互层把测试入口变成群聊飞书机器人这块我们花了不少力气在“消息怎么设计”上。机器人的代码逻辑不难难的是消息形态能不能让用户一眼看懂任务状态。2.1 机器人接入方式自建应用还是自定义机器人飞书提供了两种常见的机器人接入方式很多团队一开始会混淆。自定义机器人最简单群里添加一个 Webhook 地址通过 POST JSON 就能发消息。但它只能发不能收无法处理用户回复也无法感知群里的 消息。这种模式适合做纯通知比如定时报告推送。自建应用机器人需要走飞书开放平台创建应用开通机器人能力配置事件订阅。它可以接收用户消息、监听 机器人事件、发消息卡片、上传文件甚至读取消息回复。我们最终用的是自建应用方式因为测试任务往往需要双向交互。这里有一个经验机器人接入前先把飞书的事件订阅 URL 配好并且要正确处理 URL 验证请求。飞书在配置事件订阅时会向你的服务端发送一个带 challenge 的请求如果你不回包配置永远无法生效。对应我们用的是 FastAPI 做回调服务核心代码就几行from fastapi import FastAPI, Request from fastapi.responses import JSONResponse app FastAPI() app.post(/feishu/event) async def feishu_event(request: Request): payload await request.json() # 飞书 URL 验证 if payload.get(type) url_verification: return JSONResponse({challenge: payload[challenge]}) # 事件回调处理 if payload.get(type) event_callback: event payload.get(event, {}) if event.get(type) message: handle_message(event) return JSONResponse({code: 0})需要注意自建应用的事件订阅有 IP 白名单限制如果服务端出口 IP 经常变建议在飞书后台配置弹性 IP否则回调会直接被拒。这是我们踩过的一个很隐蔽的坑。2.2 命令协议设计自然语言为主结构化命令兜底命令协议是整个交互层的地基。我们设计了两种命令模式并行。自然语言模式针对大多数测试人员。比如“机器人 跑一遍主界面冒烟用 1280x720 分辨率”。AI Agent 会解析出测试范围“主界面冒烟”、目标分辨率“1280x720”、执行方式“跑一遍”。结构化命令模式针对自动化脚本编写者。用户直接指定技能名称和参数例如 /hmi test --suitesmoke --deviceHH3 --screen1280x720。这种模式的好处是绕过 LLM 解析直接进入技能路由减少了 Agent 幻觉的干扰。实际落地中自然语言模式的使用频率高达八成以上但结构化命令模式必须保留。因为它既是高级用户的高效工具也是自然语言解析失败时的降级方案。我建议所有意图解析结果都先落一份结构化 TaskSpec再进入任务队列。这样后续审计、重试、复现都有据可查。一个典型的 TaskSpec 结构长这样dataclass class TaskSpec: task_id: str suite: str # 技能名称 device: str # 目标车机标识 resolution: str # 屏幕分辨率 retry: int 1 # 失败重试策略 notify_to: list # 结果要通知的人2.3 任务回执与报告推送卡片消息比纯文本靠谱任务启动后要给用户一个明确回执。我们的习惯是发一张 interactive 卡片展示任务 ID、执行套件、目标设备、预计时长。这张卡片任何时候都比纯文本信息醒目用户扫一眼就知道任务已经起来了不用反复问“到底跑没跑”。任务结束时推结果卡片卡片内容包含通过率、失败用例列表、关键截图缩略图以及 AI 生成的失败原因摘要。摘要控制在三到五句话能直接转发给开发避免开发再来问“日志在哪里”。另外飞书机器人发送表格的能力我们也用上了。最终报告会附带一个上传到会话的文件格式是 Excel 或 CSV包含每条用例的详细结果、耗时、失败阶段和截图链接。这里有个小细节飞书 webhook 方式发送消息有限制但通过自建应用上传文件走的是上传素材接口没有 webhook 那么容易被限流。卡片消息 JSON 大概长这样{ msg_type: interactive, card: { config: {wide_screen_mode: true}, header: { title: {tag: plain_text, content: HMI 冒烟测试已完成}, template: green }, elements: [ {tag: div, text: {tag: lark_md, content: **通过率** 92%23/25\n失败用例 2 条已完成 AI 分析}} ] } }2.4 长任务异步化阻塞只会引出超时灾难这是最初踩得最痛的一坑。车载 HMI 的回归测试动辄二十分钟到半小时。第一次实现时我们同步等任务跑完再回传结果。结果飞书回调超时机器人一直不回消息以为没收到指令用户重复触发任务队列直接炸了。打那之后长任务全部改成异步模型收到指令立即回执“任务已受理”然后通过任务队列异步执行执行完成后通过主动消息推送结果。飞书机器人不是实时对话系统异步响应完全符合预期。3. AI Agent 编排中枢从“执行脚本”升级到“任务调度”上面聊了交互层现在讲最核心的 AI Agent 层。这块决定了平台的“智能”到底是真的有用还是只是把 LLM 塞进去凑数。3.1 AI Agent 在平台中的定位我们的 AI Agent 不是聊天机器人而是一个任务编排器。它接收 TaskSpec拆解执行计划调用执行层完成操作再汇总分析结果。传统自动化平台里一个任务直接对应一段脚本脚本写死就执行什么。加了 Agent 后任务不再是脚本的简单映射而是一个可动态规划的流程。举例来说用户说“跑一下通话界面的稳定性测试”。传统做法是找预先写好的通话稳定性脚本直接跑。Agent 的做法是拆解成几个步骤先检查当前车机是否支持通话功能再选择对应分辨率的 UI 控件映射然后执行通话界面的反复进入退出和连接断开操作最后对过程中产生的异常日志和 ANR 进行归类。这套能力的本质是 ReAct 风格的思考循环。Agent 根据任务目标查询可用的技能列表调用技能观察执行结果再决定下一步。3.2 技能注册与工具调用设计为了让 Agent 能调用自动化能力我们抽象了一套技能注册接口。每个技能就是一个可执行的测试套件包含执行入口、参数 schema、适用设备列表和超时时间。技能列表本身也通过函数返回给 LLM让 Agent 知道平台有哪些能力可用。类似 OpenAI Function Calling 的机制。比如冒烟测试技能、深色模式检查、蓝牙连接专项、语音打断稳定性等。技能描述信息在这里极其重要。写技能描述时不能只写“冒烟测试”要写清楚“包含主界面、媒体、设置、导航四个模块的基础功能验证耗时约 8 分钟支持 hh3 和 hh5 设备”。LLM 才能根据描述准确选择技能。我们有一个真实教训某个技能描述写得太笼统Agent 在收到“测一下空调面板”的时候错误调用了整车的冒烟测试多跑了十分钟。后来把所有技能描述都重写成“触发条件 覆盖范围 约束条件”三段式这个问题基本消失。3.3 AI 失败分析与根因定位AI Agent 最能体现价值的地方是测试失败后的分析。传统自动化平台只能告诉你某条用例失败了但为什么失败需要人工查看截图和日志。我们的方案是失败证据包。执行引擎在用例失败时采集三样东西当时的屏幕截图、HMI 系统日志、操作步骤序列。然后交给 AI Agent 做多模态分析。视觉模型负责看截图判断是 UI 布局错乱、控件未出现、还是弹窗遮挡导致无法操作大语言模型负责读日志分析是任务响应超时、资源竞争还是系统服务崩溃。两者结果合并后Agent 输出一个分级结论确定缺陷、疑似缺陷、环境异常、脚本问题。这个分级非常关键。它决定了结果卡片推送时文案的口吻也决定了要不要自动重新执行一次确认。对于环境异常类失败我们会自动重跑一次对于疑似缺陷则保留截图让研发确认。实际效果来看失败分析的准确率大概能覆盖七成场景。剩下三成还是需要人来判断但已经能帮测试人员节省大量翻日志的时间。3.4 上下文管理和 Token 控制的几条经验Agemt 平台跑久了token 成本问题就会浮现。这里分享几个控制成本的做法。系统提示词保持精简且固定。车载 HMI 相关的背景知识放在单独的文档索引里需要时通过检索增强获取而不是全量塞给模型。技能描述和任务结果采用结构化工具体系减少自由文本输入。自由文本越少上下文越短模型越不容易在“理解任务”上发散。日志分析不要全量喂给模型。执行引擎做完日志摘要只把关键异常堆栈、错误码和重点片段传给 Agent。车载系统的日志动辄几十 MB全量输入既不现实也烧钱。这套思路落地后单次任务的 token 消耗比最初版本下降了六成而失败分析的结论准确率基本没变。经验就是先评估哪些数据值得让模型看别当无脑搬运工。4. 车载 HMI 自动化执行层的地基搭建飞书机器人管入口AI Agent 管调度执行层才是真正跟车机打交道的地方。这块的地基如果不稳上层再智能也白搭。4.1 车载 HMI 自动化的难点车载 HMI 自动化跟手机端自动化最大区别是设备形态。车机系统虽然很多基于 Android但它不是手机。屏幕比例奇特分辨率从 1280x480 到 1920x1080 甚至更高系统权限和应用预装结构和手机完全不同。很多车机还接入了控制器局域网总线信号和汽车特定服务比如空调、座椅、灯光。想在测试环境里模拟这些状态单纯的 UI 自动化框架做不到必须结合硬件在环仿真或者 CAN 信号模拟设备。我们的执行引擎用了双通道设计UI 通道通过 ADB 或厂商私有协议控制界面操作信号通道通过 CAN 盒模拟车辆状态。比如测试空调面板时UI 通道负责点击操作和界面截图信号通道负责模拟车内温度传感器数据变化。4.2 从 ADB 命令到设备管理池执行层第一件事是设备管理。我们维护了一个受控的车机设备池每台设备都有唯一的标识、当前状态、正在执行的任务和健康检查信息。设备分配策略参考了 K8s 的调度思路任务提交时指定所需的设备型号和系统版本调度器从池里选择空闲且匹配的设备。没有匹配设备时任务进入等待队列机器人会推送一条“当前无可用设备预计等待时间 XX 分钟”的消息。ADB 连接稳定性是另一个重点。车机设备通过 USB 或者网络连接到测试机长时间运行后容易出现 ADB 断开、设备离线的问题。我们的守护进程每隔 30 秒做一次设备健康检查一旦发现离线就自动恢复连接并把被中断的任务标记为需要重跑。这里提供一段设备状态检查的简化实现def check_device(device_id: str) - bool: result subprocess.run( [adb, -s, device_id, get-state], capture_outputTrue, textTrue, timeout5 ) return device in result.stdout执行引擎收到“设备离线”标记后会自动避免把新任务分给这台设备。4.3 用例编写规范与脚本模板车载 HMI 用例的编写规范直接决定 Agent 分析结果的上限。我们定义了一套用例标注体系每条用例必须包含前置条件、操作步骤、预期结果和截图点定义。前置条件用标签化的方式描述比如“设备型号hh3屏幕1280x720空调温度26℃”。操作步骤用序列化格式保证执行引擎和 AI 分析都能理解。预期结果尽量写可校验的断言比如“等待媒体页加载完成检查中央媒体区域截图”而不是“检查界面正常”。截图标记点非常重要。我们知道 Agent 做视觉分析需要参考截图因此用例执行过程中的每个关键步骤都强制截图。这一步的成本很低但后续对失败分析价值极大。规范的脚本模板长这样testcase: id: TC_HMI_MEDIA_001 title: 媒体页面切换音频源 preconditions: device: hh3 screen: 1280x720 app: music steps: - action: click target: audio_source_btn - action: wait wait_time: 2s - action: click target: bluetooth_source_item - action: check_visual capture_point: bt_audio_connect expected: - checkpoint: bluetooth_source_active这套规范既能让普通测试人员读懂也方便 Agent 对步骤进行语义理解。在实际维护中不需要人人会写代码会填结构化描述就能贡献用例。4.4 执行结果采集与证据链结果采集最忌讳零散。我们要求每一条用例结束后自动归档一套完整的证据链操作录像片段、关键步骤截图、系统日志截取、性能数据。证据链的管理不仅是存储还要有生命周期。任务结束一段时间后原始大文件会归档到冷存储只保留 Agent 分析产出的小体积摘要报告。这样既能应对研发需要深挖的情况又不会撑爆存储系统。车载 HMI 测试尤其看重录像。因为很多交互 Bug 只靠截图看不出来比如动画掉帧、页面切换卡顿、控件响应延迟。录像配合时间戳才能还原现象。执行层每次操作都会记录一个时间轴最终和录像合并。5. 落地过程实录与踩坑记理论讲了这么多真正落地时候坑才是最有价值的部分。挑几个印象最深的记录一下。5.1 从一条消息到任务完成的全流程先说一个常见场景测试人员在飞书群里发了一条消息“机器人 帮我跑一下空调面板的深色模式检查用 hh5 设备”。这条消息到了接入层后先走到 AI Agent。Agent 利用函数调用拉取技能列表发现空调面板深色模式检查技能存在设备支持 hh5接着生成执行计划检查设备池里是否有空闲的 hh5有则分配设备拉取深色模式检查用例集修改系统主题为深色逐条执行检查收集截图生成结果摘要。然后执行层开始干活。设备调度器分配 hh5 后执行引擎通过 ADB 控制车机把系统 UI 主题切换到深色模式运行用例集。十几分钟后执行完成结果摘要推回 AI Agent。Agent 看到一条用例失败失败原因是截图显示空调面板的温度数字与背景对比度过低疑似颜色对比度不达标。它自动调取日志发现主题切换后空调面板控件的颜色资源没有正确更新。最终报告定位到可能是主题资源包版本和机端固件不匹配。整个过程测试人员只需要在群里发一句话剩下的是平台串联完成的。这条链路打通的那一刻团队里所有人对这套平台的信任度都上来了。5.2 并发任务冲突与设备抢占上线初期我们遇到最严重的问题是并发设备抢占。两个任务同时申请同一台设备调度器没有加锁导致执行引擎对着同一台车机发了两条互相冲突的 ADB 命令最后直接把车机系统干到重启。修复方式是引入设备租约机制。任务拿到设备后要建一个带过期时间的租约其他任务申请设备时调度器只分配没有租约或租约已过期的设备。租约快到期时守护进程会检查任务是否还活跃避免误杀长时间任务。5.3 飞书消息发送频控和卡片样式兼容做了消息推送后还遇到消息发送频控问题。一个回归任务如果有一百条失败用例每条都推一条卡片飞书 API 分分钟就会被限流。最后改成聚合推送任务过程只推送进度摘要最终结果只推送一张汇总卡片详细用例结果全部放到上传的 Excel 文件里。这个改动不仅解决了频控还让群里显得更干净。卡片样式兼容也要注意。飞书卡片里涉及的 Markdown 语法是简化版不是所有标签都支持。刚接入时用了不少富文本标签结果部分客户端展示异常。最终收敛到 ld_md 的常用语法并在真机、桌面端、移动端都做了展示测试。5.4 AI Agent 偶尔“自作主张”某个版本里Agent 在任务计划里擅自加了一个“设备重启”步骤。它可能觉得重启能提高测试稳定性但在生产车机上这是灾难性的操作。车机重启后整个测试环境被破坏自动化框架连不上设备任务全挂。这次事故之后我们在执行层加了一层安全护栏高危操作必须显式授权。Agent 可以请求执行某个危险动作但需要任务发起人额外回复确认。同时 Agent 的技能描述里明确标注了哪些操作属于受控操作正常情况下不应该主动调用。6. 常见问题排查速查表把实际运维中出现频率最高的问题整理成一张速查表方便大家落地时对照。这些坑都是真金白银换来的。问题现象可能原因处理方式飞书事件回调失败IP 白名单不匹配检查开放平台配置更新出口 IP机器人发送消息被限流短时间推送过多改为聚合推送限制推送频率Agent 选择了错误技能技能描述不清晰重写技能描述增加触发条件说明Agent 建议高危操作护栏缺失加入高危操作授权机制ADB 设备连不上长时间压力导致 USB 掉线加健康检查自动恢复连接用例失败但环境正常控件 ID 变化或分辨率适配问题记录控件映射版本及时更新报告查看时图片加载失败截图文件过期被清理调整文件保留策略长期报告延长归档期排查项建议频率参考工具ADB 设备状态每 30 秒自研守护进程任务队列积压每 5 分钟Redis 监控飞书消息回调延迟每次任务后日志记录告警设备磁盘空间每小时df 命令巡检LLM token 消耗每任务统计数据面板关于飞书机器人这块还有几个小经验第一自定义机器人 webhook 方式虽然接入快但功能受限严重。面向复杂任务流建议一步到位用自建应用。第二消息回执里一定要带任务 ID。这样后续用户在群里说“为什么还没跑完”的时候机器人可以按任务 ID 查询状态而不是“查也不查直接回复在跑”。第三文件上传在飞书开放平台里需要注意调用凭证的权限配置token 过期和权限不足是两个最高频的报错点。7. 落地过程中最大的体会先说体会再给扩展思路。如果只让我总结一条这套平台真正有价值的地方不是写了多少自动化用例而是把测试上下文完整地沉淀下来并流动起来。飞书机器人让任务发起变得几乎没有门槛AI Agent 让执行和反馈变得自动化而整个体系最重的部分其实还是车载 HMI 工程化本身。任何团队想复刻这套平台一定要先掂量一下自己的执行层底子够不够扎实。没有稳定的执行层AI Agent 做得再花哨最终也只是在脆弱的自动化上叠加不靠谱的信息。这套平台从设计到落地前后经历了三次较大的迭代。第一版只是简单接了个 webhook 机器人把 CI 报告推到群里第二版补了事件订阅和任务闭环第三版才把 AI Agent 真正融入到编排和失败分析环节。每一步的收益都实实在在但也都是踩了坑之后才补上的。如果你所在团队也想做类似方向我的建议是不要一上来就搞全套 AI Agent。先把飞书机器人和任务执行闭环跑通让用户在群里能发起任务、拿到报告。这一步稳定之后再逐步加入自然语言解析、失败分析、智能编排。渐进式落地比大爆炸式重构要稳妥得多。最后分享一个后续正在探索的方向让 AI Agent 根据历史缺陷密度和代码变更范围主动推荐这轮迭代应该关注哪些 HMI 功能模块把测试策略从“全量回归”升级为“风险驱动”。这会有意思得多但这是另一个阶段的事了。