面灵AI→

百度 Agent 一面:Trace Hook、SSE、幂等、工具并发

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 先说一下这个 Agent 项目;它是做代码问答的吗?
  2. 如果拿一段线上报错日志,它能分析 bug、指出代码哪里有问题吗?
  3. 项目里的 LLM Runtime、Tool Runtime 分别负责什么?
  4. 模型路由的依据是什么?
  5. 错误重试是所有错误都会重试吗?参数错误怎么处理?
  6. 有流式输出吗?具体的流式响应是怎么处理的?
  7. SSE 和普通 HTTP 连接有什么区别?
  8. SSE 和 WebSocket 有什么区别?WebSocket 是半双工吗?
  9. 如果设计了多轮工具调用,上下文是怎么管理的?
  10. 工具的返回结果会很大吗?实际遇到过吗?
  11. 上下文截断除了丢失信息,还有什么问题?实际遇到过什么问题?
  12. 工具有超时吗?工具是并发调用的吗?
  13. 幂等性具体怎么保证?
  14. Trace 的 Hook 是怎么设计的?分为哪几层?
  15. 用户的一次请求在最终的 Trace 展示上是什么形式?
  16. Trace 具体怎么用?会做分析吗?
  17. 有没有通过 Trace 分析过中间问题?
  18. 工具调用结果缓存怎么设计?缓存内容、缓存时长怎么决定?
  19. 用户长期偏好怎么存储?什么数据结构?
  20. Agent 敏感信息泄露怎么防?输入过滤怎么做?输出脱敏怎么做?
  21. 系统设计:智能文档审阅 Agent,风险标注、修改建议怎么做?
  22. Java ThreadLocal 在会话管理中怎么用?ThreadLocal 内存泄漏注意什么?
  23. 意图识别模块:分类模型 vs 规则引擎?准确率怎么提升?
  24. 工具调用链路怎么设计?
  25. Agent 怎么做监控和回滚?
  26. 手撕:反悔贪心 & 堆。

《参考解析》

1. LLM Runtime 与 Tool Runtime 的职责,以及模型路由的依据。 LLM Runtime 管与模型打交道的全部工程细节:把内部消息结构翻译成各厂商的请求格式(system 参数、工具 schema、多模态分块),管理上下文预算,处理流式事件(文本增量、工具调用增量、结束原因、用量),做配额限流、超时重试、失败切厂商与成本记账。Tool Runtime 管动作侧:工具注册与元数据(schema、权限标签、副作用、超时与并发约束)、参数校验与构造、执行隔离(沙箱或网关)、超时与取消、结果归一成 {ok, data, error} 并裁剪、调用审计与幂等键。纪律是业务编排不直接碰模型 SDK 或工具客户端,否则换模型与会散落各处。模型路由的依据是任务类型(分类、抽取、写作、代码、强推理各用不同档)、上下文长度与模态、成本与延迟预算、可用性与配额;规则放配置便于热调,并记录这一次为什么选了这个模型。

2. 错误重试的分类与参数错误的处理。 可重试的是网络超时、连接重置、5xx、限流(退避更久或排队)、以及模型侧空响应与截断;不可重试的是参数不符合 schema、鉴权与权限不足、资源不存在、业务校验拒绝、内容安全拦截,重试只会重复烧钱并放大延迟。参数错误的标准处理是结构化回灌:把错误码、哪个字段错了、期望什么类型或范围作为工具消息回给模型,让它修正参数或改用别的工具;连续参数错说明工具描述或 schema 有问题,该修的是描述而不是让模型反复猜。实现上要用指数退避加抖动避免惊群,写操作必须幂等、结果不确定时先查状态再决定是否重试,重试次数与总预算绑定,并且每次重试都记进 Trace,否则看到的成功率与真实调用量对不上。

3. 流式输出:SSE、普通 HTTP 与 WebSocket。 普通 HTTP 是一问一答,长任务里客户端只能转圈。SSE 在长连接上做服务端单向推送:响应头声明 text/event-stream,服务端按 data: 事件持续推,浏览器端 EventSource 自动解析并可带 Last-Event-ID 断线续传;优点是协议简单、穿代理容易、正好匹配模型逐字生成;缺点是单向(客户端发消息要另开请求)、同域连接数有限、需要心跳保活、要显式关掉代理缓冲。WebSocket 是真正的全双工:一次 HTTP 升级握手后走独立帧协议,双方随时可发,适合协同编辑、实时语音这类双向高频交互;代价是协议更重、心跳与重连要自己实现、代理兼容性与鉴权要自己管;它不是半双工(半双工指同一时刻只能一个方向传输)。选型判据是只做服务端推流用 SSE,双向低延迟用 WebSocket,短请求一次返回用 HTTP。

4. 多轮工具调用的上下文管理与截断风险。 结构完整性上,assistant 的 tool_call 与工具结果必须成对出现并带相同 call_id,裁剪按完整轮次或成对消息删除,只删一半会让不少厂商 API 直接报参数错误,这是线上最容易踩的坑。体积控制上,结果归一成结构化字段,列表做 top-N 与分页,日志、文件、网页全文这类大内容落对象存储、只把摘要与引用放进上下文,需要细节时再取。优先级上,系统提示与当前任务的硬约束永不裁剪,最近若干轮保留原文,更早的做摘要,检索内容按相关性动态注入。截断除了丢信息还有更隐蔽的问题:破坏消息配对导致 API 报错;把已经做过某步的记录删掉,模型重复调用同一个工具;把工具结果的错误信息裁掉,模型以为成功继续往下走。所以要先压缩再截断、按结构裁、裁完校验关键实体与消息配对,并监控裁剪后紧跟的失败率与重试率。

5. 工具超时、并发调用与幂等保证。 每个工具都必须有超时,而且要分层:单次调用超时、整体重试预算、整个 Agent 循环的时间上限,缺一层都会卡死任务;超时后要能真正取消下游,否则只是调用方放弃而服务端还在跑。并发要区分可并行与必须串行:多个无依赖的只读工具可以并发以降延迟;写操作、有顺序依赖的步骤、共享同一资源的调用必须串行或用锁隔离;并发要设上限,并把结果按调用顺序而不是返回顺序回注。幂等是并发与重试的前提:写操作用业务唯一键(用户 ID 加意图加参数哈希,或客户端 requestId)在服务端建唯一约束,重复请求直接返回已有结果;外部系统不支持幂等键就用先查后写加状态机 CAS 兜住;恢复与重试前先查状态确认是否已生效。把工具成功率、超时率、重试率、重复执行拦截次数做成指标。

6. Trace Hook 的分层设计与缓存、偏好存储。 Trace 分三层:接入层记请求 ID、会话与用户、渠道、入参摘要、总耗时与最终状态,回答用户这次经历了什么;编排层记每一轮循环的开始结束、规划结果、路由决策、模型调用的 prompt 版本与摘要、输出、token、延迟与提供商,回答 Agent 怎么想的、慢在哪;步骤层记每次工具调用的工具名、参数、结果摘要、耗时、重试与错误、幂等键,回答哪一步坏了。Hook 在关键节点留扩展点,要异步、失败不影响主流程,并对敏感内容脱敏后再写;用户一次请求在 Trace 上是一棵按时间排序的树,用法是排查、优化与挖 badcase 进评测集。工具结果缓存的判据是纯读且幂等、时效可接受、调用成本高三条同时成立,缓存键含全部影响结果的参数、用户或租户维度与工具版本,TTL 按变化频率定,库存余额这类强一致数据不缓存。长期偏好用宽表加向量混合:高频小字段放 KV,沉淀出的偏好片段放文档库并带来源与时间戳。

7. 监控、回滚与反悔贪心。 监控指标分四类:可用性(成功率、超时率、循环触顶率)、性能(首 token 时间、P95 延迟、队列深度)、成本(每请求 token 与金额、缓存命中率)、质量(工具成功率、重试率、格式失败率、点踩率),阈值按历史分位数设“超基线若干倍且持续数分钟”。回滚分层:提示词与路由配置是热配置,版本化、灰度、一键切回;代码用灰度发布加开关;数据层面的错误靠幂等与补偿脚本回正;最外层留降级开关在事故时牺牲体验保可用。处置顺序是止血、定位、最小改动修复、复盘并补回归用例。手撕反悔贪心与堆考的是识别题型:它用于看起来该选但可能后悔的场景,典型是带截止时间的任务调度;做法是按某个维度排序,用堆维护当前选择集合,遍历时先贪心加入,若不可行或收益变差就弹出堆顶实现反悔,复杂度 O(n log n);堆本身要能说清 sift up 与 sift down 的边界,以及 Java 的 PriorityQueue 默认小顶堆、大顶堆要传比较器。