☰
飞书机器人AI Agent驱动的车载HMI自动化测试平台架构
2026/10/26 4:08:23 网站建设 项目流程

1. 为什么车载 HMI 测试要靠机器人来敲门

先讲一个我印象很深的场景:某次夜班测试,仪表台的导航卡死问题在凌晨两点出现在台架上,但负责这块的工程师第二天早上十点到了工位才看到消息。中间隔了整整一个夜班的时间,一个复现概率极低的偶发问题就这么被错过了。类似的情况在车载 HMI 测试里太常见了——测试任务跑在台架上,测试人员坐在工位上,中间靠人肉盯报告、翻日志、打电话来连接,链路又长又脆。

车载 HMI(Human-Machine Interface,人机交互界面)跟普通 App 测试有本质区别。它不是一台手机加一个应用那么简单,而是仪表、中控、副驾娱乐多屏联动,背后还挂着 CAN/CANFD 总线信号,某些界面状态必须靠总线信号触发才能进入。测试环境也特殊,多数是硬件在环台架,执行机连着真实的车机控制器,跑一遍完整交互回归常常需要好几个小时。这种场景下,“提交任务—排队等待—手动查看结果—人工翻日志—手动推送给开发”的传统流程,问题非常典型:

  • 任务流转慢:测试用例散落在各人的本地工程里,想跑一个完整回归得先从别人电脑上拷脚本。
  • 反馈链路长:台架在实验室,工位在办公区,失败截图和日志靠人肉搬运,等传到开发手里可能已经是第二天。
  • 可追溯性差:谁在什么时间基于什么环境跑了哪些用例,往往只有当事人自己清楚,一换人全部断档。

我们当时的目标很明确:把“提交测试指令”到“拿到可执行的结论”这条链路压缩进一个即时通讯对话框里。测试人员在飞书群里用一句话下达指令,平台自动调度台架、执行用例、分析结果,最后把结论和证据直接推给对应的人。于是就有了“飞书机器人 × AI Agent”这套车载 HMI 自动化测试平台。

本文适合正在搭测试基础设施、做效能工具,或者对车载智能座舱测试感兴趣的同学参考。我不讲太多理论,主要分享架构是怎么设计的、为什么这样设计,以及落地过程中踩过的几个真实坑。

2. 平台总体架构拆解:一条消息如何变成一次完整测试

整套平台的架构,我习惯把它分成五层来看:接入层、决策层、调度层、执行层、数据层。它们之间的边界,就是我们在设计阶段反复推敲过的地方。

接入层是飞书机器人,它干两件事:接收用户消息、触达用户。消息不光是文本,还包括消息卡片上的按钮交互、会议纪要里的链接、任务报告里的附件。

决策层是 AI Agent 的核心,负责把自然语言指令翻译成平台能执行的结构化任务。比如用户说“帮我把夜间冒烟测试跑一遍,重点看导航和仪表联动”,Agent 要识别出:测试范围是夜间冒烟场景、重点是导航与仪表联动、需要按特定优先级执行。

调度层是任务队列和资源管理器。车载测试台架不像云主机可以无限创建,它是物理设备,同一时间只能被一个任务占用。调度层要解决“谁先跑、在哪台台架跑、跑挂了怎么办”的问题。

执行层是测试执行引擎,负责真实驱动台架:下发总线信号、注入触摸事件、截图比对、读取日志。这一层直接跟硬件打交道,出问题最多,坑也大多集中在这里。

数据层统一存放测试用例、执行记录、失败截图、总线日志和最终报告。所有的结果都打上统一的 traceId,一条消息从进来到出报告,全链路可追溯。

一次完整的流转链路是这样的:用户在飞书群里发消息——机器人通过事件订阅收到消息——Agent 解析意图并返回结构化任务——调度层把任务放入队列,找到空闲台架——执行层在台架上跑测试——结果回写数据层——机器人把报告卡片推送到群里。整个过程用户只做了一件事:发消息、点确认。

为什么选飞书作为交互入口?三条理由。第一,团队日常沟通本来就在飞书里,触达效率远高于邮件和网页平台。开发同学看到群里机器人推送的失败卡片,当场就能点进去看详情。第二,飞书的消息卡片支持嵌套按钮和复杂交互,天然适合做“确认执行”“查看报告”“一键重跑”这类操作。第三,移动端体验好,测试人员晚上在家也能收到报告,不用开着远程桌面去翻网页。

3. 飞书机器人侧:命令解析、消息卡片与异步任务状态机

飞书机器人的接入方式不算复杂,但有几个细节值得讲清楚。我们用的是自定义机器人 Webhook 和事件订阅相结合的方式:Webhook 用于主动推送(报告、通知),事件订阅用于接收用户消息。

事件订阅这里有个容易忽略的点:飞书的事件回调是异步的,你得在应用后台配置事件订阅的 URL,并且返回特定的响应格式做 URL 验证。第一次配置的时候,我们直接返回了 HTTP 200,结果事件一直没推过来,排查了半天才发现是验证流程没走完。这个细节文档里有写明,但实操中真的很容易踩。

3.1 命令路由的设计:从关键词到 AI 意图解析

平台早期的命令路由很简单,就是关键词匹配。比如用户发“跑回归”,机器人就去匹配对应的回归测试集合。这在小范围内够用,但很快就发现一个尴尬的场景:测试人员手滑把“回归”打成了“灰归”,机器人毫无反应,他只能再发一遍。更麻烦的是,关键词匹配没法处理组合条件,比如“仪表和中控一起跑,但仪表优先”。

后来我们引入了 AI 意图解析,命令路由就变成了两层结构:第一层是轻量级规则匹配,命中高频的、格式固定的命令(比如精确的用例编号“HMI-1023 重跑”)就直接走流程;只有规则层无法解析的模糊指令,才交给 AI Agent 处理。这样既保证高频操作的响应速度,又避免大模型频繁介入带来的延迟和成本。

3.2 消息卡片交互:任务确认、失败重跑、报告预览

消息卡片是飞书机器人体验好坏的关键。最初我们只推送纯文本报告,结果发现开发同学点了链接还要跳转到 Web 页面看详情,多了一步就少了一半人看。后来改成卡片后,数据完全不同。

卡片我们设计了三种交互状态:任务下发后,机器人推一张“待确认”卡片,上面有测试范围、预估耗时、目标台架,用户点“确认”才开始执行;测试失败时,推“失败详情”卡片,一行标题写清楚失败模块和用例,下面附截图、日志片段和“重新单独跑”按钮;任务完成时,推“报告预览”卡片,用折叠的概要字段替代大段文字,核心指标一眼可见。

这里有个经验:卡片里的按钮回调要设计成幂等的。用户可能连点两次“重跑”,或者回调超时后自动重试,如果后端不判断任务状态,就会启动两个相同任务。我们在回调处理里加了任务状态校验,只有当前状态为“已结束”或“空闲”的任务才允许重新入队。

3.3 异步任务状态机

车载 HMI 测试任务动辄几十分钟甚至几个小时,不能用简单的同步请求来处理,必须引入状态机。我们定义了五个状态:

状态含义可流转到的状态
PENDING任务已入队,等待分配台架RUNNING, CANCELLED
RUNNING任务正在台架上执行DONE, FAILED, BLOCKED
BLOCKED执行过程中遇到阻塞(如设备掉线)RUNNING(人工恢复), CANCELLED
DONE执行完成,结果生成中最终态
FAILED执行失败或结果异常最终态(支持重新入队)

状态机看起来简单,但落地时最麻烦的是状态同步。执行引擎是独立的进程,跑在台架连接的工控机上,机器人与调度层跑在另外的服务器上,中间靠消息队列通信。如果执行引擎写完结果后进程崩溃,调度层可能永远等不到消息,任务卡在 RUNNING。我们最后用了一个很朴素的办法:调度层定时对超时 RUNNING 任务做心跳检测,连续几次探不到执行引擎的存活信号就自动标记为 FAILED,并且写入一条提示“执行引擎异常中断”。

飞书机器人消息处理器(简化版伪代码) def handle_im_message(message): text = message.text user = message.sender chat = message.chat_id # 第一步:规则层尝试解析 intent = rule_parser.try_parse(text) if intent is None: # 规则层无法解析,交给 AI Agent intent = agent.resolve_intent(text, user, chat_history) if intent is None: send_text_reply(chat, "我没看懂这条指令,试试这样发:跑回归 / 重跑 HMI-1023 / 查看仪表测试报告") return task = task_service.create_task(intent, user) if intent.require_confirm: send_confirm_card(chat, task) else: scheduler.enqueue(task.id) send_status_card(chat, task, "PENDING")
## 4. AI Agent 决策层:从“规则匹配”到“意图理解”的升级 AI Agent 是这套平台最有想象力、也最需要谨慎对待的部分。它接管的是“理解用户想做什么”和“决定怎么做”的决策职责,选型不当会造成用户体验断崖式下降。 ### 4.1 为什么规则引擎不够用了 纯规则引擎的问题在于它只能处理“枚举过的世界”。测试人员不会每次都乖乖按模板说话,真实指令千奇百怪:“把昨晚那条高架的路线再跑一遍”“导航卡死那个问题今天复现没有”“仪表和中控联动那条用例谁负责的”。有些是执行类指令,有些是查询类指令,有些甚至是在群里进行的日常讨论,根本不该触发测试任务。 早期我们也尝试过用关键词穷举来覆盖,但维护成本越来越高,规则之间还会互相冲突。比如“跑一遍导航”和“导航卡死那个任务怎么样了”,一个要执行、一个要查询,关键词重叠但语义完全不同。规则引擎在这种模糊语义面前非常脆弱。 ### 4.2 基于大模型的意图解析设计 最终方案是接入了大模型做意图解析。我们把问题转化为一个结构化输出任务:给模型提供系统提示词,描述当前可用的工具列表和它们的参数,让模型输出 JSON 格式的调用意图。 这里要强调一个设计原则:**AI Agent 只做意图解析和决策,不直接操作台架**。也就是说,大模型输出的是“调用哪个工具、传什么参数”,而工具的实际实现(下发总线信号、启动测试脚本)仍然由传统代码完成。这样做的好处是,即便模型输出不合法的参数,我们也只会在解析层拦截,不会让它直接摸到真实硬件。 系统提示词的大致逻辑如下: - 先说明当前关联的飞书群聊、最近会话上下文; - 列出可用工具:run_test_suite、query_task_status、get_report、rerun_failed_case、query_device_status; - 要求模型只输出 JSON,格式为 `{"tool": "run_test_suite", "args": {"suite_id": "night_smoke", "device": "bench_01"}}`; - 遇到无法判断的指令,输出 `{"tool": "needs_clarification"}`。 实际运行中,我们还加入了 `needs_clarification` 这个特殊工具。宁可让 Agent 多问一句,也不要它猜错方向导致在台架上乱跑。有一次测试人员发了一句“把这个任务优先级提高”,模型没有上下文知道“这个任务”指哪个,如果强行解析,就会把任务认错。多问一句的成本极低,但能避免大量错误执行。 ### 4.3 置信度阈值与人工兜底 大模型的输出不能无条件信任。我们对 Agent 的解析结果做了一个置信度打分:如果代码层面判断模型的输出与用户输入存在明显矛盾(比如用户明确说“仪表”,而模型输出“中控”),或者用户的指令包含多个可执行动作但没有明确优先级,就转人工确认流程。确认动作放在飞书卡片上,用户点“执行”或“改一下”,任何一个普通测试人员都能在几秒内完成。 这个设计是受了网上一篇文章的启发:不要把 AI 当全知全能的决策者,而是把它当作一个“翻译器”,把自然语言翻译成结构化的、可验证的动作描述。翻译错了不要紧,人在流程里兜底,这才是一套面向真实生产的 Agent 系统该有的姿态。 ## 5. HMI 测试执行引擎:台架、设备与图像识别三位一体 执行引擎是整套平台里最“接地气”的部分,它要在真实硬件上干活,因此也是踩坑最多的部分。车载 HMI 的自动化测试,本质上就是三件事:**感知界面状态、操作界面控件、校验预期结果**。听起来跟普通 UI 测试没区别,但细节完全不一样。 ### 5.1 硬件在环节点的设备抽象 台架不是一台普通的电脑。我们在每个台架上安装了一个执行代理,这个代理进程直接连接车机调试口和总线工控机。车机调试口负责抓取屏幕和注入触摸事件,总线工控机负责发送 CAN 信号。代理启动后向调度中心注册自身信息,包括台架编号、当前连接的车型平台、运行状态和负载。 设备抽象拆成三层:物理设备层、逻辑设备层、能力层。物理设备层是真实的台架;逻辑设备层定义统一接口,例如 `poweron`、`send_can_signal`、`capture_screen`、`inject_touch`;能力层记录该台架支持的能力集合,比如有的台架支持语音注入,有的不支持。调度层分配任务时,会先根据能力集合做过滤,避免把需要语音能力的用例派到没有麦克风阵列的台架上。 ### 5.2 测试用例模型:按“场景脚本”组织 车载 HMI 测试用例不能只写“点击按钮、检查文本”,它的前置条件往往是一揽子环境准备。我们约定了一套用例模型,每个用例包含: - **前置脚本**:启动整车模拟环境、设置特定总线信号值(如车速 120km/h、挡位 D); - **操作步骤**:按时间序列注入触摸事件、旋钮事件或语音指令; - **断言条件**:基于截图差异、OCR 文本识别结果、日志关键字三者综合判断; - **后置恢复**:复位总线信号、关闭车机高负载应用,保证不影响下一用例。 这套模型最大的价值在于可复跑性。传统的 UI 测试脚本如果前置状态没准备好,跑出来的结果往往是假的失败。我们把前置准备和恢复动作都固化进用例,一轮回归下来失败率明显下降。 ### 5.3 图像识别、坐标注入与总线信号配合 车载 HMI 的界面校验,最常用的是图像识别与 OCR。我们跑一个用例时,执行引擎按照脚本到达某个界面,先截屏,然后用预先标定好的模板图片做匹配,或者用 OCR 识别关键文案。界面元素的坐标不是写死的,而是根据每次截图的分辨率和模板匹配结果动态计算。 这里有一个细节:**车机屏幕的分辨率跟普通显示器一致,但它的实际显示区域可能有黑边,坐标不能直接用默认值**。我们的做法是在每台台架首次接入时,执行一次屏幕标定程序,自动识别显示区域边界,生成一个台架专属的坐标映射文件。所有后续的注入操作都先经过这个映射,否则点按会出现系统性偏差,而且很难排查。 总线信号在这里的作用不可替代。很多 HMI 状态不是靠点按进入的,而是靠信号驱动。比如要测试“车速超 120km/h 时仪表弹出超速提醒”,就必须通过总线工具发送真实的车速信号,让仪表控制器认为车速真的达到了 120km/h。我们的引擎里封装了一个 `send_can_signal` 接口,测试脚本里可以按语义调用,不用关心底层的报文格式。执行引擎会根据预设的 DBC 文件自动完成信号编码。

执行引擎伪代码(简化)

def run_case(case, device): device.execute_script(case.precondition) for step in case.steps: if step.type == "touch": x, y = map_coordinates(device, step.target_element) device.inject_touch(x, y) elif step.type == "can_signal": device.send_can_signal(step.message_name, step.values) elif step.type == "voice": device.inject_voice(step.audio_file)

current_screen = device.capture_screen() if not verify_assertion(step.assertion, current_screen): return CaseResult.failed(step, current_screen) return CaseResult.passed()
### 5.4 设备并发调度的锁与队列 车载台架的并发问题跟云资源调度不一样。云主机可以随意扩容,但台架数量固定,一台台架同时只能跑一个任务。我们把单台台架设计成“互斥资源”,调度层维护一个设备状态表,任务入队后先抢占设备锁,抢到才启动执行进程。 这里遇到过一个问题:执行引擎本身崩溃后,设备锁没有释放,后续任务全部卡死。原因是执行进程报错退出时,清理逻辑没走完。后来我们在设备代理里加了看门狗设计,代理进程独立于测试进程运行,一旦发现测试进程异常退出,就自动释放设备锁并上报异常。设备代理本身也支持自愈重启,这算是聊胜于无的兜底方案。 ## 6. 落地过程中的五个真实的“坑” 任何平台都是在坑里长大的,这套平台也不例外。我把印象最深的五个问题记录下来,每一个都是线上故障级别,供大家参考。 ### 6.1 台架网络隔离导致机器人失联 第一个坑来得非常快。台架所在的实验室为了安全,网络策略很严格,只有少数几个端口能通到办公区,而飞书事件订阅URL的对外访问需要公网可达。结果就是:办公区的用户能发消息,但消息事件根本推不到部署在实验室侧的服务上。 最后方案是增加一台边缘网关,部署在办公区网络,由这台网关主动向实验室内部执行服务发起长连接,把飞书回调做转发。这样实验室内部不需要开放入站端口,只由办公区网关做出口访问。这个拓扑并不复杂,但在规划阶段如果不提前想清楚,搭完才发现就很痛苦。 ### 6.2 大模型幻觉把“回归测试”理解成“耐久测试” 有段时间我们让 Agent 自由解析的指令比例很高,结果出现了一次非常尴尬的误判。测试人员在群里说“跑一遍回归,顺便看看稳定性”,Agent 居然解析成“执行 8 小时耐久测试”。也就是说,本来十几分钟的回归任务,变成了一场跨夜跑。好在执行前有确认卡片,测试人员一看不对就取消了,没有造成实际损失。 这次事件后,我们把 Agent 的解析输出加了严格的“工具参数枚举校验”:工具列表和参数值必须在预定义的枚举内,不能自由生成。比如“测试套件”只能从用例仓库的套件列表中选择,“执行时长”不能随意设定,如果模型输出不在枚举内的值,直接转人工确认。这等于把大模型的输出从“自由文本”限制成“受控表单”,幻觉的影响面就大大缩小了。 ### 6.3 图像误报的饥饿问题 图像识别作为断言手段,最大的问题是误报。模板匹配容易把“按钮被弹窗遮挡”的情况误判为“按钮存在”,因为局部像素相似度足够高。OCR 则经常把中文的形近字认错。 我们的解决思路是“多信号联合判定”:一个断言不只看截图,还要结合日志关键字和总线信号状态。比如断言“导航已启动”,除了识别导航地图界面,还会检查车机日志里是否出现导航应用的启动关键字,二者都满足才算通过。虽然多了一道工序,但误报率直线下降。 ### 6.4 消息卡片回调节点频繁超时 飞书卡片按钮的回调请求走的是公网,而我们的服务部署在办公网络内,公网链路偶尔不稳定,回调经常超时。一开始我们以为是自己处理逻辑太慢,后来抓日志才发现是网络问题。 解决方式非常简单:回调处理接口直接返回成功,把真正的业务逻辑丢进异步队列处理。飞书的超时判定只关心 HTTP 响应,不关心后续任务是否执行成功。用户点击“重跑”后,按钮马上消失,任务异步入队,通过消息推送把结果反馈给用户。这样既保证交互流畅,又不会因为公网抖动导致按钮一直转圈。 ### 6.5 日志分散导致定位困难:统一 traceId 平台组件多了之后,最痛苦的是排障。用户反馈“机器人没反应”,但问题可能出在飞书事件订阅、Agent 解析、调度队列、执行引擎任何一个环节。早期日志散落在各个服务里,靠人工拼接时间线定位,效率极低。 后来统一了日志规范:一条消息从入口就生成 traceId,贯穿飞书回调、意图解析、任务调度、设备执行的每一步写入日志。查一个问题,只要拿 traceId 在所有服务里 grep 一遍,就能还原完整链路。这套机制成本很低,但带来的排障效率提升立竿见影。 ## 7. 实测数据与经验沉淀 平台上线运行了大概三个月之后,我们统计了几个关键指标,能直观看到这套“飞书机器人 × AI Agent”架构带来的变化。 | 指标 | 改造前 | 改造后 | |------|--------|--------| | 任务下发到开始执行的平均时间 | 约 40 分钟(人工排队等空闲台架) | 约 2 分钟(入队自动分配) | | 测试结果从产生到触达开发的平均时间 | 数小时到次日 | 实时推送,秒级触达 | | 一次失败定位所需的平均人时 | 30-60 分钟(人工翻日志、查截图) | 5-10 分钟(报告卡片直接带证据链) | | 回归测试的自动执行覆盖率 | 不足 40% | 超过 95% | 这里面固然有自动化能力提升的因素,但我认为真正带来质变的还是交互方式的改变。之前自动化测试跑完,结果躺在平台上没人看,价值没有闭环;现在结果主动推给人,还配套了确认、重跑、看详情这些动作,开发和测试的反馈环路被大幅压缩了。 关于选型,我个人的建议是: - **消息平台选飞书还是其他 IM**,不用太纠结,核心是你们团队本来在用哪个,符合真实习惯的才是好的; - **AI Agent 接入要克制**,先跑通规则引擎,再把模糊指令交给模型,不要一上来就全量 AI 化; - **台架资源管理一定要早做**,设备锁、心跳检测、自愈能力这些看似底层的能力,反而是平台稳定性的关键; - **报告卡片的设计决定了工具的渗透率**,一个不直观的入口等于不存在,务必反复打磨消息卡片的交互。 这套平台后续还有几个扩展方向在推进。一个是把 Agent 接进座舱语音助手,让测试人员坐在车上直接用语音下发测试指令;另一个是把自动驾驶仿真场景与台架测试串联起来,实现“仿真场景生成—回灌车机—结果自动断言”的闭环。另外,多车型并行扩展也在规划中,核心是把台架资源抽象成可用标签检索的资源池,按车型、控制器、软件版本动态匹配。这些方向能否全部落地不好说,但至少目前这套架构给了我们继续折腾的底气。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询