跳转到内容

AI 工程面试:模型会犯错,项目怎么做可靠

模型说“空调已经调好了”,不代表空调真的调好了。做可靠的 AI 系统,要把理解请求、执行操作、确认结果分开:模型把用户的话转成操作请求,程序检查设备、权限和参数,再调用接口;查到结果后,才能告诉用户完成了什么。程序检查也不能消除所有理解错误,选错设备、漏掉条件这些问题,仍然要通过测试和实际使用来发现。

从零做项目,我会先选一个确实需要 AI 帮忙的功能,把它做到能用,再看它是否比原来的办法省事。开发中的资料检索、编码和测试可以交给 Agent;要解决什么问题、允许它做什么、结果算不算合格,不能只凭它的一句“已完成”来决定。

下面是一组模拟面试追问。例子是接入 Home Assistant 的家庭助手;请求、故障和比较数据都是为解释问题设计的,不是实际测试结果。

面试官:让你从零做一个家庭助手,你先做什么?

我会先问用户:现在控制家里的设备,哪一步最麻烦?

如果只是每天晚上十点关灯,Home Assistant 的自动化规则就够了,不需要模型。用户真正想省掉的,可能是找房间、找设备、记名称这些操作。他想直接说“把客厅空调调到 26 度”,说错了还能接一句“不是客厅,是卧室”。这时,模型理解自然语言和多轮纠正才有用。

我会选这样的一个任务做第一版,先不把所有家电都接进来。范围可以只到空调控制,但从用户提出要求,到设备执行,再到回复结果,这个过程要做完整。后面要换模型或增加功能,也有一个能运行的版本作为比较对象。1

面试官:第一版具体怎么做?

用户说“把客厅空调调到 26 度”,系统先查询客厅有哪些空调。只有一个明确目标,就准备执行;如果有两台,又没有设置默认设备,就问用户要控制哪一台。执行以后,再读取设备状态,告诉用户确认到了什么。

我的家庭助手已经采用了 Skill 加固定脚本的做法:Skill 说明有哪些功能、什么时候调用;脚本封装 Home Assistant 的查询和控制。模型调用已有功能,不需要临时写一段脚本去操作设备。

每次请求都要记录模型选择的动作、脚本收到的参数和工具返回的结果,方便定位错误。原型阶段可以先接模拟设备,等决策过程跑通,再接真实设备验证接口和反馈。

读取状态时,还要弄清它是不是设备回报的。例如 MQTT climate 的某些配置会在发出命令后,直接假定设备已经执行,这叫乐观模式。此时页面显示 26 度,不能当作设备已经设成 26 度的证据。2

面试官:模型每次输出都可能不一样。你刚才说的控制过程,怎么保证不会乱操作?

限制候选设备,再检查控制参数。模型提出的操作不能直接发到 Home Assistant。

比如模型提出一个操作:把 climate.living_room 设为 26 度。脚本要检查这个设备是否在允许控制的范围内,以及它是否支持这个温度。只有检查通过,才向 Home Assistant 发请求。

模型看到的设备列表也要尽量准确。用户指定客厅,就给它客厅的候选设备,而不是让它从全家的几十个设备里猜。如果名称仍有歧义,就询问用户,不强行选一个。

让模型输出 JSON 有助于接入程序,但它主要解决格式问题。temperature=26 是合法参数,卧室空调的 ID 也可能合法;模型把客厅理解成卧室,JSON 校验并不会发现。目标解析是否正确,要另外测。

面试官:那我在 Prompt 里写清楚“不得越权、不得控制其他设备”,能省掉这些检查吗?

不能。Prompt 能提醒模型,但执行程序还得自己判断这次操作有没有权限。

用户能控制哪些设备,应该由登录身份和后台权限记录决定,不能让模型传一个 authorized=true 就算通过。模型拿到的工具也应只包含当前功能需要的操作。家庭助手要调温度,就提供查设备、读状态、设温度这些功能,不必给它执行任意脚本的权限。

这样,即使模型提出了越权请求,工具也会拒绝。网页、设备描述或工具返回里夹着“关闭所有设备”之类的文字,也不能替用户授权。工具仍然只执行当前用户被允许做的操作。

面试官:用户又加了个条件:“窗户关着才开空调。”你查的时候关着,执行前打开了呢?

控制脚本在发请求前,要重新查窗户状态。窗户已经打开,就不执行;状态查不到,也不把它当成关闭。

不过,查询和控制之间仍然有时间差。如果用户的意思是“只要窗户打开,空调就必须停”,这就需要持续监听窗户变化。应当用 Home Assistant 自动化或设备侧的控制程序来做,不能靠模型不停地查询和判断。

“执行前检查一次”和“始终维持一个条件”是两种功能。需求没分清,代码写得再完整,也可能做错事。

面试官:控制请求发出去了,但接口超时。是不是再试一次就行?

不能直接再发。超时只说明没收到响应,设备可能已经执行了。

对设置温度这样的操作,可以先查设备反馈:如果已经设成 26 度,就没有必要再发。如果仍未达到要求,再根据接口行为和当前条件决定能否重试。开关“切换”就不同,重复执行可能把刚打开的设备又关掉。

会重复产生后果的动作,需要更谨慎。接口若支持幂等键,也就是用同一个操作标识避免重复执行,重试就沿用原来的标识;不支持的话,仅在本地记一个 ID,并不能让远端自动去重。

查不清时,回复可以是:“控制请求已发出,但还没确认设备结果。”同时保存这个待确认的操作。服务重启后,新进程先查询它的结果,而不是重新执行。

面试官:这些都是设计。你怎么证明程序实际做到了?

把请求放进测试环境,检查它最后改了什么,而不是只看回复说了什么。

同一条空调请求,可以改变环境条件来测试:

测试场景应当看到的结果
目标明确,设备在线正确空调设为 26 度,其他设备未被修改
客厅有两台空调,没有默认目标助手先询问,没有擅自控制其中一台
用户要求窗户关闭才执行,但窗户开着没有发出控制请求,并说明原因
用户没有目标设备的权限工具拒绝,设备没有变化
设备已执行,接口响应丢失助手查询结果,没有盲目重复操作

每次测试前重置设备和操作记录,否则上一次留下的 26 度,可能让下一次看起来也成功了。能用程序检查的结果,例如设备 ID、参数和控制次数,就用程序检查。回复有没有准确解释失败,再由人工检查;样本多了,可以用经过人工校准的模型评分器辅助。

排查失败时,我会沿着同一次请求看记录:用户要的是客厅,设备查询返回了哪些候选,模型选了谁,脚本实际控制了谁。如果模型选的是客厅,脚本却操作了卧室,问题就在设备映射或执行代码里,不该继续改 Prompt。这些记录需要去掉不必要的个人信息。3

面试官:这批测试都通过,就可以上线了吗?

可以作为继续试用的依据,不能证明以后不会出错。比较新旧版本时,要用同一组请求和相同的初始状态,每条请求重复运行。发布前,还要用没有参与调优的样本检查,避免只把测试里那几句话处理好了。

举一个假设比较:30 个任务各跑 3 次,旧版完成了 80 次,新版完成了 84 次。但新版有一次控制错了设备,旧版没有。虽然总完成率提高了,我也不会直接发布新版。控制错设备应当单独记为严重错误,不能被其他场景的提升抵消。

效果合格之后,再看速度和成本。把测试中所有的模型和工具费用加起来,包括失败尝试和重试,再除以完成的任务数,才知道每次成功实际花了多少。用户要纠正几次、要等多久,也一起比较。一个便宜的模型如果经常让用户重新解释,未必是更好的选择。

离线测试之外,可以先限定设备范围、小范围试用,再把实际遇到的失败补进测试。哪些错误允许出现、哪些出现一次就必须停止发布,要按操作的风险事先定好。

面试官:试用后发现效果不好。你会换大模型、加 RAG,还是做多 Agent?

我会打开失败记录,先看这次为什么没做好。比如用户说“客厅大空调”,系统却只提供一个看不出房间和型号的 ID。模型缺的是设备信息,先修设备查询,比换模型更直接。

不同问题对应不同改法:

  • 用户问设备说明书,系统没有相关资料,可以先检索说明书,再让模型根据查到的内容回答,也就是 RAG。
  • 用户说“按我平时的温度来”,系统没有保存这个偏好,需要补偏好记录和读取功能。
  • 候选设备和请求都很清楚,模型仍然选错,再比较提示词调整和模型替换。
  • 任务包含能独立进行的几项工作,可以先看并行调用工具能否解决。确实需要分别分析或协作,再考虑多个 Agent。单次调温通常不需要。

每次改动都要能说清楚准备修哪种错误。改完跑原来的失败样本,也跑其他场景,确认没有把别的功能改坏。4

交给 Agent 以后,人还要负责什么

Section titled “交给 Agent 以后,人还要负责什么”

面试官:你说的这些还是软件工程。AI 原生究竟有什么不同?

软件工程的要求还在,变化在于很多执行工作可以直接交给 Agent。

比如要修“选错空调”这个问题,我可以把失败记录、设备查询代码和接口说明交给 Agent,让它定位原因、提出修改、写代码、运行测试。人不用亲手完成每一步,但要让它知道准备解决哪个问题、可以改哪里,以及怎么验证。

交代任务也可以很具体:

这组记录里,助手把客厅空调选成了卧室空调。检查设备候选列表和选择逻辑,修复错误;不要改权限规则和 Home Assistant 接口。补上复现测试,并跑已有的设备控制测试。遇到需要改变产品规则的地方,先停下来说明。

Agent 拿到必要资料和检查方法,可以连续工作;遇到两台设备该选谁这样的产品问题,再交回人决定。5

面试官:实现和测试都由 Agent 写,它们一起理解错了怎么办?

测试要按用户需求来写,不能照着现有代码的行为来写。先把要求固定下来:控制的是指定设备,不得修改其他设备;目标有歧义时先询问;没有权限就不执行。

Agent 可以据此编写测试,但测试是否检查了这些要求,需要核对。例如,测试只断言“工具返回成功”,即使实际控制了另一台空调,也可能通过。在模拟设备的测试环境中,故意让控制函数返回成功,却操作另一台设备。测试应当失败;如果仍然通过,测试本身就需要改。

读代码时也有重点。设备 ID 怎样确定、权限在哪检查、控制请求在哪发出、超时后会不会再发一次,这些要结合代码和测试看明白。另一位评审者可以帮助发现遗漏,但多换一个模型,并不保证它不会产生同样的误解。

面试官:以后模型更强,这些程序检查和测试是不是就没用了?

为旧模型补短板的步骤,可以重新检查。比如旧版需要多个 Agent 才能完成的任务,新版一个就能做好;或者专门纠正设备选择的长提示,新版不需要了。这些都可以用原来的任务集比较,确认效果之后简化。

权限检查和设备结果查询不会因此删除。用户允许控制哪些设备、设备实际有没有执行,仍然要从权限记录和设备反馈里确认。升级模型时,我会用同一批任务检查错误、耗时和费用,再决定哪些步骤可以省掉。

  1. Google:Rules of Machine Learning。参考其中简单基线、先定义指标和验证处理流程的原则,不照搬传统 ML 的训练方法。 ↩

  2. Home Assistant:MQTT climate — Optimistic mode。说明缺少状态反馈时,部分属性会假定命令已经执行。 ↩

  3. Anthropic:Demystifying evals for AI agents。参考任务、重复试验、评分器、执行记录和最终结果的区分。这里的家庭助手用例和数据是说明性示例。 ↩

  4. Hamel Husain:Your AI Product Needs Evals。参考产品级评测、执行记录分析,以及人工和模型评审的组合。 ↩

  5. Simon Willison:Here’s how I use LLMs to help me write code。参考上下文提供、明确要求、运行验证和开发者责任。 ↩