平安好医生面试面经-附带回答版本
Q1:介绍下你自己。
A:面试官你好,我是xxx,计算机相关专业本科毕业,毕业后一直在做 Java 后端和AI相关的项目。最近参与过AI语音助手和设备配套的OTA平台,之前做过企业内部的AI知识助手、云图库和电商。其中AI语音助手和AI知识助手,主要是与AI相关的开发,AI语音助手是将RAG、Tool Calling、实时语音链路落实到Spring Boot、MySQL、Redis这些工程组件上,AI知识助手则偏向于RAG和Tool Calling;OTA平台、云图库和电商则主要是传统的Java后端开发。
Q2:RAG检索过程是什么样的?
A:在我们的开发应用中,会把RAG分为离线建库和在线检索两部分。
- 离线侧先解析文档,保留Markdown标题层级、表格和产品版本等metadata,再按照语义边界且chunk,生成向量并写入向量索引,同时建立BM25倒排索引。
- 在线侧先对query做分类或者改写,并且按照产品、版本、权限做过滤,然后并行做向量召回和关键词召回,用RRF合并候选,必要时用Cross-Encoder精排。最后按token预算去做驱虫和context packing,把来源一起交给模型,证据不足就拒绝回答。
Q3:打分过程是什么样的?
A:Top K并不是打分算法,它知识按照分数排序后的截断。向量召回的原始分数可能是余弦相似度、点积或者L2距离,BM25则根据词频、逆文档频率和文档长度给分。两路分数不能直接相加时,可以按排名用RRF融合,之后Cross-Encoder把query和chunk一起输入,得到更加精细的相关性分数。最终再取Top-N进入上下文。当然不同向量库的score方向和归一化方向不同,不能只看一个阈值。
Q4:ReAct的原理是什么?
A:ReAct的核心是模型根据当前观察选择下一步动作,工具返回结果后形成新的观察,再决定继续调用工具还是给出最终答案。生产环境中我们一般不会把内部推理原样展示或者记录,更多的是记录工具名、参数摘要、权限决策、结果状态和可审计的决策说明,另外为了避免循环,还需要限制最大步数、总时长和工具集合。
Q5:Plan和ReAct的区别是什么?
A:ReAct是每得到一次Observation就决定下一步的Action;Plan-and-Executor则是先形成相对完整的计划,再由执行器逐项执行,必要时重新规划。
Q6:Agent完整的工作过程是什么样的?
A:请求进来先生成traceId,并且绑定用户、租户和会话;随后组装system规则、当前问题、近期历史、RAG证据、可用工具和权限信息。模型如果选择工具,只输出工具名和结构化参数;服务端做schema、权限、范围、敏感操作二次确认和幂等校验,再通过带超时、重试、熔断的执行器调用。工具结果会被标准化为Observation回填,模型决定继续还是结束。最终回复要包含来源或执行结果,整个过程记录prompt/tool版本、耗时、错误码和审计信息。
Q7:上下文组装是什么过程?
A:上下文组装是每次调用模型前,把本轮需要的输入按优先级放进token budget。通常包含system prompt、当前问题、最近会话、RAG证据、工具描述、工具结果、用户画像和权限信息。安全规则和当前问题不能被裁掉,RAG证据要按照相关性和多样性选择;旧历史先做滑动窗口,再做摘要。需要注意ChatMemory只是这些信息的一个来源,但是不等于上下文本身。
Q8:记忆怎么持久化,如何提取?
A:这个功能的本质是把对话历史持久化到MySQL中,然后每次请求大模型之前会根据会话ID查询最近的历史消息,再重新放入模型上下文中。最初使用的Spring AI的内存对话记忆,但是这会导致服务重启后数据丢失,所以我们自定义一个ChatMemory实现Spring AI的ChatMemory接口。从数据层来说,是设计了一张chat_messagede表,通过conservationId区分会话,同时保存消息类型、文本内容和metadata。matadata类型是Map,使用MyBatis-Plus的JacksonTypeHandler将其转换为JSON存入MySQL中。再服务层的具体实现上,add方法负责把Spring AI的Message转换为数据库实体写入;get方法会根据会话ID查询最近N条消息,再转换为对应的UserMessage、AssistantMessage等对象;clear方法负责清空指定会话。最后,把这个自定义的chatMemory配置到MessageChatMemoryAdvisor中,每次请求都携带同一个chatId,Advisor就会自动读取并补充上下文,从而实现跨请求、甚至服务重启后的对话记忆。
