字节一面 Agent 岗:ReAct、幂等与上下文压缩连环追问
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 自我介绍,重点讲一个你做过的最复杂的 Agent 项目。
- 你这个 Agent 的整体架构是怎样的?画一下架构图,讲清楚每一层负责什么。
- 你提到用了 ReAct 模式,具体怎么实现的?Thought、Action、Observation 在你的代码里怎么体现?
- 每一步的输入输出格式是怎么约定的?有没有用结构化输出?
- 如果 LLM 返回的 tool call 格式不合法,或者参数类型不对,你的系统怎么处理?
- 有没有做校验和重试?重试几次?重试会不会导致重复操作?
- 工具调用超时后怎么处理?超时时间怎么设?
- 幂等怎么设计?幂等 key 怎么生成?为什么不让大模型自己生成 key?
- 上下文管理是怎么做的?如果对话超过 100 轮,你怎么控制 token 消耗?
- 上下文压缩具体怎么实现?压缩提示词怎么写的?
- 压缩完怎么确认 LLM 真的完成了压缩?
- 压缩后丢失了关键信息导致任务失败,你怎么回退?
- 长流程任务如果中途服务重启了,怎么恢复未完成的任务?
- 你存了哪些状态信息?存在哪里?恢复后怎么保证状态一致性?
- 多 Agent 之间怎么通信?消息队列还是共享内存?
- 子 Agent 挂了怎么办?怎么保证任务不中断?
- 多个 Agent 同时修改同一个文件,怎么避免冲突?有没有用分布式锁?
- Agent 之间传递的上下文怎么保证完整、不遗漏关键信息?
- 如果传给子 Agent 的上下文不完整,导致它做了错误决策,你怎么发现和补救?
- 你做过 Agent 评测吗?评测集怎么构建?多少条?
- 评测指标关注哪些?准确率、任务完成率、人工介入率?
- 如果模型输出 JSON 格式不稳定,怎么校验?Schema 怎么设计?
- 如果评测集覆盖不全,怎么发现?
- 你平时怎么用 AI 写代码?你的 AI Coding 工作流完整描述一下。
- 从拿到需求到提交代码,AI 参与了哪些环节?每个环节你做了什么?
- 你如何确保 AI 生成的代码质量?如果 AI 生成的代码能跑但逻辑很烂,哪些问题必须改?
- 如果 AI 连续三次没修好一个 Bug,你会怎么处理?有没有遇到过真实案例?
- 你最近在关注 Agent 领域的什么新技术?DeepSeek-Harness 和 LangChain 的本质区别是什么?
- 手撕:合并嵌套 JSON。给定两个嵌套结构的 JSON 数据,实现合并函数;规则是相同 key 递归合并、仅在一个字典中的 key 直接保留、列表拼接;要求处理 key 冲突且类型不同的情况,写出完整代码并分析时间复杂度。
《参考解析》
ReAct 的工程落地:格式约定比「用了 ReAct」重要
ReAct 的本质是把「想一步 → 做一步 → 看结果」显式化成文本循环:模型输出 Thought(推理)和 Action(工具名 + 参数),运行时执行工具并把 Observation 拼回上下文,进入下一轮,直到模型给出 Final Answer。工程上真正决定成败的是格式约定和终止条件:每一步用固定模板(或直接走模型的 function calling / JSON mode),在 prompt 里给出正例反例;运行时解析失败要有兜底(抽 JSON 片段、修尾逗号、截断补括号);必须设最大步数、最大 token 和整体超时三个硬上限,否则一个打转的循环能烧掉整天的 token。此外要防「原地打转」:对 工具名 + 参数 做哈希去重计数,连续 N 次相同调用或同一工具反复失败就中断、换策略或转人工。
tool call 容错:校验、重试与幂等的三角关系
模型输出永远当成不可信输入:工具名不能直接拿去路由函数(一个幻觉出来的名字可能触发任意调用),参数必须过一遍服务端 JSON Schema / Pydantic 校验。校验失败时把结构化错误回填给模型让它自修正,体验比直接报错好得多,但要设重试上限(2~3 次)并准备降级路径(换工具、追问澄清、转人工)。
重试和幂等是一对:读操作随便重试,写操作必须幂等,否则「超时但其实执行成功了」的重试会变成下两次单。幂等 key 的正确生成方式是业务侧确定性派生——用「会话 ID + 步骤序号 + 工具名 + 规范化参数」的哈希,而不是让大模型自己编一个 key:模型生成的 key 每次调用都可能不一样(温度、上下文变化),等于没有幂等;而且让模型参与生成等于把幂等性建立在不可信输出上。服务端拿 key 去查「是否已执行过」,已执行直接返回上次结果。超时时间的设置要分层:单次工具调用超时(按该工具 P99 的 2~3 倍)、单步 Agent 超时、整个任务超时,三级都要有,并且超时后的错误信息要能让模型判断「能不能换个方式重试」。
上下文压缩与长流程状态恢复
超过 100 轮后 token 必然炸,常见策略按性价比排序:① 固定不可丢弃的部分(系统指令、任务目标、用户核心约束);② 对历史做滚动摘要,把「已确认的事实、已完成的步骤、待办」结构化保留,原始对话丢弃;③ 只保留最近 N 轮原文;④ 把长工具返回落盘,上下文里只留引用和摘要。压缩提示词要写成有 schema 的抽取任务(输出固定字段),而不是「帮我总结一下」。压缩完要验证:检查输出是否是合法结构、关键字段是否为空、是否还含有原始目标中的实体,任何一项不过就重试或退化成「原文保留 + 只丢最早的一批」。
压缩丢信息导致任务失败时,回退靠的是原始记录没有真的删掉——历史消息、工具调用入参出参都持久化在库里,压缩只影响送进模型的那一份视图;出问题可以按任务 ID 重建完整上下文重新跑。同理,服务重启后要恢复未完成任务,前提是把状态存成外部化的、可重放的形式:任务状态机(pending/running/done/failed)、已完成步骤的幂等 key、工具调用记录、模型与 prompt 版本、中间产物引用。恢复时以持久化状态为准做幂等重放,而不是从头再来;一致性靠状态机流转加乐观锁(版本号)保证,恢复失败就落到明确的失败态并告警,绝不静默重头跑。
多 Agent 协作:通信、写冲突与上下文传递
通信选消息队列还是共享内存,看两点:是否跨进程/跨机(跨就是 MQ,进程内共享内存/直接调用即可)、是否需要解耦与削峰(需要就 MQ)。要补一句:MQ 至少一次投递语义意味着消费侧也必须幂等。子 Agent 挂掉的保护手段是心跳 + 超时 + 重试 + 熔断,重试要落到「任务分片」粒度,不能让整个任务重来。
多个 Agent 同时改同一份资源(文件、数据库行、工单)时,别指望靠协调提示词解决,要靠机制:文件级用租约/锁文件或干脆让每个 Agent 只写自己的分片再合并;数据库用行锁 / 乐观锁(版本号 CAS);跨机统一用分布式锁(Redis Redlock 或 etcd lease)并设 TTL 与续约,防止持锁进程挂掉造成死锁。
上下文传递的完整性靠结构化交接单而不是把历史对话整段转发:handoff 时明确写出任务目标、已确认事实、已完成动作及其结果、当前阻塞、期望输出格式,并做一次校验(必填字段非空、引用的中间产物 ID 存在)。子 Agent 判断错误时的发现机制是「回到父级做结果校验」:父级对子任务结果做 schema 校验 + 事实一致性检查(引用的文件/ID 是否真实存在),不一致就带着具体差异重派,而不是笼统地说「再试一次」。
Agent 评测与 AI Coding 工作流
评测集要从真实流量与失败案例里长出来,而不是坐在工位上编:把线上 badcase、人工介入过的会话、边界场景各按比例采样,规模从几十条起步,按能力维度分层(单工具调用、多步规划、长上下文、异常恢复),每条给出标准答案或可判定的成功条件。指标除了准确率,还要看任务完成率、平均步数(成本)、工具调用成功率、人工介入率、超时率、以及端到端的 P95 延迟。覆盖不全的发现办法是分层看指标:整体涨但某一维度跌,就说明新增数据偏科;也可以做「扰动测试」(改输入措辞、注入噪声、删掉一条关键信息)看模型是否还稳。Schema 设计上宁可窄而严:字段类型明确、必填项显式、枚举值穷尽,配 additionalProperties: false,避免模型自由发挥。
AI Coding 工作流要能说清「哪里让 AI 做、哪里人把关」:需求澄清与方案设计人主导,样板代码、测试用例、重构、写文档、翻日志交给 AI,自己负责审关键逻辑(并发、边界、错误处理、安全、性能)和最终验收。AI 生成的代码能跑但逻辑烂时,必须改的是:错误被吞掉(catch 了什么都不做)、资源泄漏、无边界校验、隐式全局状态、复制粘贴出的重复逻辑、以及看不出意图的命名与魔法数。连续三次修不好同一个 bug 时正确动作是停下来换信息源:让 AI 复现并给出根因假设与验证方法,缩小到最小可复现用例,必要时人肉读代码或加日志,而不是继续换措辞重问。
手撕:嵌套 JSON 合并
递归是自然的解法:对两个 dict 的 key 并集遍历,两边都有就递归合并;只有一边有就直接取;两边都是 list 就拼接(注意应该是浅拼接还是逐元素递归合并,取决于业务语义,面试时要问清);类型不同(比如一边 dict 一边标量)时要显式定义策略——常见是「后者覆盖前者」或「报错拒绝」,绝不能隐式强转。复杂度是 O(两个 JSON 的总节点数),空间上递归深度等于最大嵌套深度。如果要求不改动入参,就要在合并时拷贝节点。写完主动补充:嵌套极深时递归会爆栈,改成显式栈的迭代版本;list 拼接要说明是否去重、是否保持顺序。