理想汽车智能体算法一面:Agent Loop 与奖励设计
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 你理解的 Agent Loop 是什么?一个完整的循环中有哪些状态?
- 如何判断一个任务是否适合使用 Agent,而不是普通工作流?
- 为什么使用 LoRA,而不是直接进行全参数微调?
- 训练过程中如何判断奖励设计存在问题?
- 失败轨迹如何用于后续训练?
- 请做一下自我介绍
- 项目中最难解决的问题是什么?
《参考解析》
Agent Loop:状态不是对话历史
一个完整的循环是”感知 → 规划 → 行动 → 观察 → 更新状态 → 判断终止”:读入用户输入或工具返回,判断下一步是继续调工具、向用户澄清、修正计划还是直接结束,执行动作后再把结果写回状态。真正区分工程水平的是状态里放了什么。至少要有六类:目标与约束(这一步最容易在多轮之后被丢掉)、已确认事实(带来源与时间戳,且必须区分 observed 和 inferred)、未决问题、动作历史(工具名、参数、结果摘要)、预算(剩余步数 / token / 金额)、终止条件是否满足。
把历史对话直接拼进 prompt 当状态是常见错误,有三个硬伤:上下文有限,轮数一多早期约束会被截断或稀释,而模型对中段信息的注意力本来就弱;信息密度低,几十条工具原始返回里真正有用的可能只有两三行;无法区分事实和推断,上游的推断会被下游当成确定事实继续用,错误层层放大。正确做法是把状态建成结构化对象(dataclass / Pydantic)落到 Redis 或数据库,每轮只把与当前子目标相关的那片切片渲染进 prompt;大块的工具返回(网页正文、代码执行日志)归一化后写进对象存储或文件,prompt 里只放摘要加引用 id,需要时再按 id 取回。动作历史用 append-only 的事件日志,便于回放、归因和离线评测。工程护栏同样属于 Loop 的一部分:max_steps、max_tokens、墙钟超时、重复动作检测——把规范化后的工具名和参数做 JSON 排序后哈希,同一个指纹重复出现就拒绝执行并把历史错误作为反馈交回模型重新规划。
什么时候该上 Agent,什么时候一个 DAG 就够了
判据可以压成四条。第一步,动作序列能不能在写代码时枚举完——能枚举就用工作流。第二步,分支是不是由外部环境决定且分支数很大——工具多、每条分支都要人工维护,才是 Agent 的收益点。第三步,错误的代价和可逆性——涉及支付、删除、对外提交这类不可逆动作,必须走确定性代码加人工确认,不能交给模型自由决策。第四步,能不能评测——工作流的每一步都能单测,Agent 的路径是指数级的,只能靠轨迹级指标(任务成功率、平均步数、每条成功轨迹的 token 成本)来回归。
现实里最稳的是混合架构:把信息搜集、候选生成、路由判断这类”软”环节交给模型,把权限校验、金额校验、幂等提交这类”硬”环节写成确定性代码,模型只负责在这些关卡前面提议、由代码来裁决。成本也要提前算清楚:Agent 每一轮都要重放一遍上下文,token 消耗随轮数快速上升,没有预算护栏的 Agent 很容易在一条长轨迹上烧掉几十倍于工作流的钱,却只换来几个百分点的成功率。反过来说,任务复杂不等于必须上 Agent——引入 Agent 就同时引入了不可预测的调用路径、更高的异常处理复杂度和更难的评测。
奖励设计失衡的排查
先看症状,别先看曲线。典型信号有六类:奖励在涨但真实业务成功率不涨;输出变长而信息密度在掉;同一个工具被反复无意义调用;大量引用与结论无关的证据;拒答率或过度保守的回答突然上升;不同难度、不同长度下的奖励分布明显不公平。出现任意一条,就要怀疑模型找到了投机路径——比如只要格式完整就能得分,它就会学会堆砌理由;只要调用次数有奖励,它就会空转工具。
排查手段是消融加反事实。消融:把奖励的每一项单独拿掉或置零,看业务指标掉多少,用这个来校准权重,而不是拍脑袋定 0.40 / 0.25 / 0.20 / 0.15 这种拍出来的系数——权重应该用线上指标回归出来。反事实:固定结论内容,只改格式、长度、模板词,观察奖励变化幅度,如果改长度带来的 Δr 比改对错还大,说明奖励模型依赖了表面特征。分桶:按难度和长度分别看奖励均值与方差,如果模型在一个桶上刷满、其他桶不动,就是被 exploit 了。最后一定要有可验证的兜底奖励——工具名对但参数错不给分、没有引入新证据的长调用不给分、引用的证据 id 必须出现在本次召回集合里,把主观打分限制在”可验证项之外”的范围内。
失败轨迹:不要整条当负样本
失败轨迹的信息量比成功轨迹大,但直接全部标成负样本会浪费掉大部分信号,而且会教模型”整条轨迹都是错的”这种错误因果。正确姿势是先做失败归因,按层级分类:任务理解错、计划拆解错、工具选择错、工具参数错、没用上有效证据、中间事实被覆盖、结论错、格式不合规。不同层次对应完全不同的训练数据——参数错要构造工具调用修正数据,检索错要构造查询改写数据,结论错要补反事实样本和证据对比样本,格式错则是输出约束数据的活。
最关键的一步是切分。一条轨迹往往前 k-1 步都对,第 k 步才出错,那就把轨迹拆成 (prefix, bad_action, expected_action) 三元组:前缀加正确动作构成正样本,前缀加原错误动作构成负样本,一条失败轨迹因此能放大成多个步级样本,正好对上过程监督和步级 DPO 的形式。分类函数本身很朴素——顺序扫描轨迹,找到第一个状态为无效的步,返回失败位置、错误类型、前缀和当时的动作。
两个必须防的坑。第一,剔除环境性失败:工具 500、超时、评测器 bug、金标本身错,这些不构成模型的错误,混进去会让模型学到错误的因果,最好在归因阶段就用日志把它们单独分流。第二,防止过拟合到某一类失败模式:负样本要按错误类型分层采样,别让”格式错”这种最容易构造的类型刷满训练集;再用一份 held-out 的失败集做回归,确认各类错误率是真的在降。失败原因还可以反向利用——把 critique 作为上下文喂回模型(reflexion 式),但要注意上下文膨胀,critique 也要做摘要和去重。
LoRA 还是全参微调
四个维度决定。数据量:指令数据在万条以下、目标只是适配风格和格式,LoRA 足够,r 取 16~64;数据量到十万条以上且与预训练分布差异很大,才轮到全参微调或者继续预训练加微调。任务偏移:改输出风格、领域术语、格式约束,LoRA 的低秩空间够用;要注入大量新知识、改变词法分布或底层推理模式,低秩更新往往不够——这也是 LoRA 被反复验证的短板。遗忘风险:全参微调会明显冲击通用能力,通常要掺 5% 左右的通用数据回放;LoRA 只更新旁路分支,对基础能力的破坏小得多。部署形态:LoRA 支持一个 base 加 N 个 adapter 热切换(vLLM、SGLang 都支持 batch 级多 LoRA),多租户场景下省的是整份权重的显存;全参微调只能一个场景存一份权重。
还有两条容易被忽略的:如果目标的本质是”记住规则条款、随业务更新”,优先考虑 RAG 而不是微调,知识更新的成本差一个量级;LoRA 上线前如果要 merge 进 base,务必在 fp32 下合并再转 bf16,否则会有精度损失,而保持 adapter 独立加载则会带来百分之几到十几个百分点的额外延迟,具体取决于 r 和 batch 大小——这两条要拿线上的延迟预算来权衡,而不是默认 merge。