美团全栈开发日常实习一面:ReAct、RAG 三路召回与 Agent 自进化 49 问
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 自我介绍并介绍一下你的项目
- 为什么主体选 ReAct?它和思维链、思维树、Plan and Execute 有什么区别?
- 为什么不用 LangChain 等流行框架,要自己写?
- 用户输入后,从路由到 Skill 调用的完整流程是什么?
- 精确场景为什么不走 RAG?什么场景走 RAG?
- RAG 三路召回的完整流程是什么?(粗过滤、Top20、打分、ReRank 取 Top8)
- RAG 检索可能遇到什么问题?数据坍缩、噪声、召回不足怎么解决?
- 多 Agent 在什么情况下触发?怎么拆分子 Agent?
- 子 Agent 的工具和消息权限怎么做隔离?
- 子 Agent 之间怎么通信和传递结果?
- 会话记忆怎么压缩和存储?(滑动窗口、摘要、SQLite、倒排索引)
- 历史对话内容存在哪里?怎么按需调取?
- Agent 的提示词怎么设计?分哪几层?
- 怎么防提示词注入?
- 工具调用的 Schema 和权限怎么设计?
- 工具调用失败怎么办?(重试、降级、报错)
- 是否支持文件上传?文件怎么存储?
- MCP 是什么?协议里 host、client、server 各自的角色?
- 为什么要用 MCP?解决了什么问题?
- Skill 和 MCP 有什么区别?
- Agent 自进化怎么设计的?轨迹层和知识层分别存什么?
- 因子 / 策略生成的状态怎么流转?
- 自进化的触发机制是什么?产出有哪些?
- 项目怎么做测试和迭代?(测试集、端到端、线上反馈)
- 检索效果用什么指标评估?(精确率、召回率、MRR)
- 微服务项目整体架构是什么?拆成了哪几个模块?
- 服务之间怎么调用?(OpenFeign)
- 登录认证怎么做的?为什么用双 Token(RefreshToken + AccessToken)?
- Gateway 负责什么?(路由分发、鉴权)
- 数据分层存储怎么设计的?各类数据分别存在哪里?
- 为什么行情 / 资金曲线选 ClickHouse、关系数据选 Neo4j?
- MQ 在项目里的作用是什么?
- 回测这类长任务为什么要异步处理?完整流程是什么?
- 幂等怎么实现?常见方案有哪些?
- 分布式事务了解吗?
- 前后端实时通信怎么做的?为什么用 WebSocket?
- WebSocket 的协议头和握手流程是什么?
- Redis 在项目里做什么用?
- 多实例部署下重复获取数据怎么办?(分布式锁)
- Redis 和数据库的一致性怎么保证?三种方案分别是什么?
- 缓存和数据库操作的先后顺序怎么选?binlog 方案怎么做?
- 之前有没有 Java 项目经历?
- 熟悉哪些 Java 技术栈和中间件?(Spring Boot、Spring Cloud、Redis、RabbitMQ)
- 什么是 IOC?什么是 AOP?
- 你是哪里人?
- 为什么研一就想出来实习?时间怎么安排?
- 拿过哪些竞赛奖项?
- 你怎么看待数学?
《参考解析》
为什么选 ReAct、以及它和 CoT / ToT / Plan-and-Execute 的区别,是 Agent 岗的第一道分水岭。 CoT 是「一次想完再答」,中间没有外部信息介入,适合纯推理题;ToT 在 CoT 基础上做多分支搜索与回溯,成本高,适合解空间小、可评估的规划类问题;Plan-and-Execute 是「先出一份完整计划再逐步执行」,好处是全局视角、便于人工审阅,坏处是计划一旦与现实不符就得整体重来。ReAct 的核心是推理与行动交替——每一步 Thought 之后可以调工具拿 Observation,再据此调整下一步,所以它天然适合「信息不全、需要边查边定」的任务。选它的理由要说清场景:任务的不确定性高、外部工具是主要信息来源、而且允许中途失败重试。反过来说,流程固定、步骤可枚举的场景就不该用 ReAct,直接写 workflow 更可控也更便宜。
「为什么不用 LangChain」要答成工程权衡,不是贬低框架。 常见理由有三条:一是链路定制深,框架的抽象层会在调试时遮蔽真实请求(提示词被拼成什么样、检索命中哪些片段看不清);二是框架迭代快、依赖重,生产上不好锁版本;三是自研只需要一个很薄的编排层(路由、工具注册、状态机、重试),代码量并不大。回答时可以补一句诚实的话:框架适合验证想法,自研适合背责任——一旦线上出问题,得有人能定位到具体那一行。
RAG 的三路召回,重点讲清「为什么需要三路」。 通常是向量召回(语义泛化)、关键词/BM25 召回(精确术语、编号、错误码)、以及基于元数据或规则的召回(时间、版本、类别过滤)。三路各自粗召回 20 条左右,融合(RRF 或加权)后交给 cross-encoder 重排,取 Top8 进上下文。要主动说出常见故障:数据坍缩(切分与嵌入导致不同文档向量高度相似,召回结果同质化,需要多样性控制如 MMR)、噪声(相关但不支撑答案,要靠重排和「证据是否足够」的判断过滤)、召回不足(查询与文档用词差异大,需要查询改写/HyDE)。衡量指标不能只看召回的 precision/recall,还要看最终答案的忠实度和拒答准确率。
多 Agent 的拆分与隔离,回答要落到「权限」和「上下文」两条线上。 触发条件通常是任务可明显分成异构子任务(检索、编码、校验)、单个 Agent 的工具集过大导致选路不准、或者需要并行。拆分原则是按能力边界而不是按步骤数量。隔离有两个层面:工具权限上每个子 Agent 只挂自己需要的工具,且敏感工具(写文件、发请求、付款)走白名单和参数校验;消息权限上子 Agent 只看得到自己那部分上下文,避免污染和越权。通信方式有主 Agent 汇总(层级式)和共享黑板/消息队列(对等式)两类,前者可控、后者灵活,代价都要说清。
记忆压缩的分层策略。 短期用滑动窗口保原文,中期做摘要但仍保留实体与数字,长期落库(SQLite/向量库/倒排索引)按需检索回灌。关键设计点是按需调取:历史对话不能每次全量塞进 prompt,而要先用查询去记忆库里召回相关片段。这里容易忽略的是写入侧——什么该记、以什么粒度记、冲突时谁优先,都需要明确规则,否则记忆库会变成噪音源。
提示词分层与工具 Schema 设计。 提示词一般分四层:身份与目标、能力与工具说明、行为约束(什么不能做、不确定怎么办)、以及输出格式。分层的目的不是好看,而是让改动可定位——改约束不该动到身份层。工具 Schema 要窄:参数少而明确、枚举优于自由文本、必填项显式标注,并且每个工具都要定义失败语义(可重试 / 不可重试 / 需要人工)。工具失败的处理顺序是先分类(网络抖动可重试、参数错误不该重试、权限不足要上报),再决定重试(退避 + 幂等)、降级(换工具或返回部分结果)还是抛给用户。
MCP 与 Skill 的区别,答法是「协议 vs 内容」。 MCP 是模型与外部工具/数据源之间的通信协议,host 是承载对话的应用、client 是 host 内的连接器、server 是暴露工具与资源的一方;它解决的是「工具怎么被发现和调用」的标准化问题,让同一套 server 能被不同 host 复用。Skill 更偏过程性知识——把某类任务的流程、判断标准、注意事项写给人/模型看,通常不涉及运行时协议。一句话概括:MCP 让模型能调用,Skill 让模型知道该怎么用。两者经常组合:Skill 里说明流程,具体动作通过 MCP 工具落地。
Agent 自进化与状态流转。 自进化的可行形态是「轨迹层 + 知识层」:轨迹层存每一次任务的输入、动作序列、结果与人工反馈,用来做失败归因和样本挖掘;知识层是沉淀后的规则、提示词片段、工具用法、评测集。触发机制一般有三类——定时批量挖掘、失败率阈值触发、以及人工标注回流。这里必须说清产出的验证闭环:新知识上线前要在回归集上验证,否则「进化」很容易变成把噪音写进提示词。
微服务与中间件部分的老几样,需要能自圆其说。 双 Token 的目的是让 AccessToken 短命(泄露窗口小)、RefreshToken 长命(避免频繁登录),刷新时旧 RefreshToken 要作废以防重放。网关负责路由、鉴权、限流、灰度,鉴权结果往下游透传用户身份。ClickHouse 适合列式聚合与时间序列(资金曲线、行情这类写入量大、按维度聚合的查询),Neo4j 适合多跳关系(资金链路、股权、担保关系)——选型理由要落在查询模式上。长任务(回测)异步化是为了不占请求线程、便于重试和断点续跑,链路是「提交任务 → 落库排队 → worker 消费 → 状态回写 → 前端轮询/推送」。幂等的常见方案是唯一业务键 + 去重表、状态机条件更新、以及消费侧去重。
Redis 与数据库的一致性,三种方案要能比较。 先更新数据库再删缓存(Cache Aside,最常用,仍可能有短暂不一致)、先删缓存再更新数据库(并发下更容易脏读,一般配延迟双删)、以及订阅 binlog 异步失效(Canal 之类,解耦但引入延迟)。核心结论是没有强一致,只有最终一致,所以关键业务不能把缓存当权威数据源。顺序问题的判据是「谁更容易脏、脏多久能自愈」。
WebSocket 的握手与协议头。 客户端发一个带 Upgrade: websocket、Connection: Upgrade、Sec-WebSocket-Key 的 HTTP 请求,服务端回 101 Switching Protocols 并带 Sec-WebSocket-Accept(由 Key 加固定 GUID 做 SHA-1 再 base64),之后同一个 TCP 连接上跑的是 WebSocket 帧协议,帧头包含 FIN、opcode、掩码位和长度字段。选它而不选轮询,是因为服务端要主动推送且需要低延迟、低开销——但要主动补充它的软肋:连接状态、断线重连、心跳保活、以及多实例下的会话路由(需要共享订阅或网关粘性)。