大模型面试题
Agent面试题
什么是 Agent?与大模型有什么本质不同?
本质上是一个能自主完成目标的AI系统,和传统AI最核心的区别在于自主性和能动性
传统AI是你问一个问题他回答一个问题,独立、被动响应;Agent有自己的规划能力,给一个复杂目标,能把任务拆成多步,通过调工具、访问记忆、感知环境一步步执行,直到完成。
工具调用+记忆机制+多步推理和自我纠错
Agent 的基本架构由哪些核心组件构成?
LLM:整个系统的大脑,负责理解任务和做决策
推理模型的代价是延迟更高、token 消耗更大,常见的工程做法是:用推理能力强的大模型做核心决策(比如任务规划、关键判断),用更快更便宜的小模型做简单任务(比如意图分类、格式提取),根据不同环节的需求来搭配。
工具:Agent能跟外部世界交互,搜索、执行代码,调用API
模型负责「决定做什么」,程序负责「真正执行」
这里还有一个非常容易被忽略的点:工具描述的质量直接影响 Agent 的表现。
MCP JSON-RPC:三类能力 Tools(会改变外部世界的操作,比如发邮件)、Resources(只读的数据源,比如文件内容)、Prompts(预定义的提示词模板,比如代码审查模板)。
HOST SERVER CLIENT
记忆:让Agent在任务执行过程中保持状态
短期记忆 装在context window,记住任务中间状态,摘要压缩,滑动窗口
长期记忆 向量数据库记忆衰减:记忆衰减的做法是给每条记忆加一个时间权重,越久远的记忆权重越低,检索的时候自然就排在后面了。
规划模块:把复杂目标拆解成可执行的步骤
COT(Chain of Thought,思维链)让模型把思考过程写出来,而不是直接输出最终答案
LLM 的 token 生成是逐步进行的,每一步推理的输出会成为下一步推理的输入,把中间步骤写出来,等于给了模型更多的「思考空间」
先规划再执行:好处是整体结构清晰,你能在执行前就看到完整计划,方便人工审核;缺点是如果中间某一步的结果和预期不一样,原来的计划可能就不合适了,需要重新调整。
动态 重规划策略
边执行边规划:好处是灵活性极高,能根据实际情况随时调整;缺点是容易「走偏」,因为每一步都是局部最优决策,有时候会忽略整体目标
Reflection(反思):Agent完成一步或者完成整个任务后,再让一个LLM来判断做的好不好,结果是否符合预期。评估不通过,就重试或者换一种策略。,这个机制显著提升输出质量。代码生成、文案写作。Reflexion会生成一段具体的反思总结,记录失败原因,作为额外的上下文写个下一次输出
Workflow,Agent,Tools 这三个的概念和区别介绍一下
这三个概念是粒度从小到大的三层结构
Tools是最小的能力单元,封装好的可调用函数,比如搜索,执行代码、发邮件,只负责执行,没有决策能力
Agent是一个完整的决策系统,内部用LLM做大脑,自己判断什么时候调用哪个Tool,要不要继续,什么时候结束、是主动的
Workflow是更上层的编排框架,把Agent、LLM、Tools组织成一条确定性流程,每个节点做什么、按什么顺序流转都是开发者事先写死的
tools =封装函数+说明书
好的工具设计核心原则:职责单一,描述要精确,错误信息要清晰
常见的停止条件:LLM主动判断任务完成,输出最终答案;设置最大循环次数,返回当前已有的结果并告知用户;设置总token预算上限;超时机制
Agentic Workflow:用Workflow 固定主流程的骨架,在需要灵活判断的节点嵌入Agent,其余固定节点直接用LLM或Tools
几种常见的Workflow编排模式:提示链;路由;并行化;编排者-工人
了解哪些其他的 Agent 设计范式?Agent 和 Workflow的区别是什么
两者最核心的区别是谁来决定下一步。Workflow是我提前把流程写死的,每一步固定,确定性高,好控制;Agent让LLM自己决定下一步做什么,灵活但不可控
常见设计范式:纯Agent,还有React,Plan-and-Execute、Reflection
如何选:步骤不多,每步比较独立,React就够,简单直接,灵活,每一步都能根据最新情况做决策,特别适合那些任务边界不太明确、需要探索性地获取信息的场景,
任务复杂,步骤之间有依赖关系需要全局统筹,Plan-and-Execute,适合那些目标明确、需要多步骤协作完成的复杂任务,比如深度研究、长文写作、多工具协同的数据分析
输出要求高,允许花一些时间和token,Reflection
两者混用:固定流程的部分用Workflow,灵活决策的接待嵌入Agent能力
先从最简单的workflow开始,只有当发现某个节点确实需要灵活决策、写死的逻辑无法覆盖所有情况时,才把那个节点升级为Agent
Agent 推理模式有哪些?ReAct 是啥?具体是怎么实现的?
最基础的就是直接输出答案,没有中间推理;COT是让LLM先把推理过程写出来再给答案,准确率更高,COT是纯文字推理,没有办法和外部世界交互;ReAct是在COT基础上加了行动,让LLM交替输出思考和工具调用,每次行动后再根据结果继续思考,形成一个循环。
ReAct是目前Agent用的最广的的模式,因为推理过程可见,又能动态利用外部工具
存在循环漂移问题;错误传播
Plan-and-Execute做全局规划(能力强的大模型),用ReAct做每一步的执行(便宜的小模型)。
ReAct的本质是思考-行动-观察的循环,推理过程显示化,调用外部工具;循环由代码框架驱动,模型每次只输出Thought、Action,代码负责解析,执行工具、把Observation填回历史,进入下一轮
ReAct、Plan-and-Execute、Reflection 三种范式有什么核心区别?实际项目中该如何选型?
ReAct是边想边干,走一步看一步,单步迭代实时调整,灵活度最高;
Plan-and-Execute是先想全再干,先定完整计划再分步执行,适合长流程任务,不容易跑偏
Reflection不是独立的完整流程么人是给前两者加的检查修正buff,用来提升输出质量
选型指南
任务不复杂、流程不固定、需要实时调整的,直接用 ReAct,够用就好,别搞复杂的。任务很长、容易跑偏、需要整体结构清晰的,上 Plan-and-Execute,先把计划定清楚再执行。如果执行过程中经常遇到意外需要调整计划,就加上动态 Replan。输出要求高、不能出错的,在前两者基础上叠加 Reflection 做自我检查。如果还需要跨任务积累经验、避免重复犯错,就用 Reflexion 把失败教训沉淀下来。
复杂任务怎么做的任务拆分?为什么要拆分?效果如何提升?
任务拆分的原因是LLM一次性处理太复杂的任务很容易出错,把大任务拆成小任务,每次聚焦一件事,准确率会明显提升。(LLM的工作台 context window 是有大小限制的,能同时处理的信息量是有上限的)
分成两种:静态拆分,提前将步骤写死(由人决定);动态拆分,让LLM自己根据目标规划步骤,更灵活但也更难控制(由模型决定,给它一个目标,让它先输出一个执行计划,再按计划一步步执行,这是 Plan-and-Execute 模式的核心思想)
拆完之后步骤之间可能有依赖关系,我的经验是能并行的步骤并发跑,端到端延迟可以降很多,有时能降40%到60%
自适应拆分
拆的太细也不行:步骤太多,LLM调用次数越多,总toke消耗上升;步骤太碎,每步只做一件很小的事情,LLM看不到全局,产出的各部分也容易衔接生硬
原子操作作为划分单步的标准:这个步骤只做一件独立的事情,边界清晰,做完有明确的输出,和其他步骤不互相依赖
不要在开始时就把所有步骤的力度定死,而是在执行过程中根据每一步的实际难度动态调整。先让执行器完成当前任务,做得好就继续往下走,不做多余的拆分。先试,做不好再拆分
动态重规划:讲当前步骤的结果和剩余的计划一起交给规划模块,实践中设置触发条件,比如当某部的输出和预期差异很大时,或者某步骤执行失败才启动Replan.
拆分结果的验证标准
完整性、独立性、可验证性
请你介绍一下 AI Agent 的记忆机制,并说明在实际开发中应该如何设计记忆模块
Agent需要记忆才能在多步任务中保持状态、跨任务积累知识
感知记忆(当前输入的原始内容)
短期记忆(context window 里的对话内容)
长期记忆(外部数据库、语义检索找回)
语义记忆:存的是事实性知识,回答是什么
情节记忆:存的是具体时间的经历,回答之前怎么处理过类似情况
程序记忆:存的是怎么做某件事的方法论,回答按什么流程来
用户偏好和习惯、任务执行中产生的关建结论和决策、以及外部知识
实体记忆(结构化提取的关键事实)
混合存储
结构化的偏好字段用关系数据库精确查,非结构化的知识和历史用向量数据库语义检索,两者配合使用。
什么时候取出来用?
第一种叫「主动检索」,在任务开始前,用当前任务的描述去检索相关记忆,把结果注入 system prompt 作为背景知识。这样 Agent 一开始就带着「历史记忆」进入任务,不需要用户每次重新交代背景。第二种叫「被动触发」,Agent 在推理过程中,判断当前步骤需要某类特定知识时,主动发起检索。具体做法是把「查记忆」封装成一个 Tool,让 Agent 自己决定什么时候调。这种方式更灵活,但依赖模型判断什么时候该去查。
实践上两种结合效果最好:session 开始时做一次主动检索,把关于用户偏好和背景的记忆加载进 system prompt;任务执行过程中,遇到需要专业知识或历史数据的步骤,再让 Agent 按需检索
Context Window 管理:短期记忆的「工作台」不够大怎么办
滑动窗口
摘要压缩
把不常用但重要的信息卸载到长期记忆里面
[知识图谱:让记忆之间产生关联]
在 Agent 的记忆模块里引入知识图谱,通常是和向量数据库配合使用的。向量数据库负责处理模糊的语义检索(比如用户说「之前那个项目」,向量检索能找到最相关的项目记忆),知识图谱负责处理精确的关系推理(比如查某个用户的所有相关公司和角色),两者互补。具体做法是:对话过程中用 LLM 自动提取出实体和关系,存入知识图谱。检索时先用向量检索拿到一批候选记忆,再用知识图谱补充关联信息,最后把两部分结果合并后注入 context。
记忆整合:从碎片到知识
去重 冲突消解(最新的时间戳) 抽象提炼
整合的节奏也很重要
完整记忆模块的配合方式
第一阶段:任务开始前,先「读」记忆
用户发来一个新请求,Agent从实体记忆里取出用户的结构化偏好(语言偏好、风格要求、过往决策),再用任务描述作为查询词,去长期记忆里做一次语义检索,拿回最相关的历史背景。把这两部分信息拼进 system prompt 的开头,Agent 进入任务时就已经带着完整的「用户画像」,不需要用户重复交代背景。
第二阶段:任务执行中,持续「用」记忆
任务开始执行,短期记忆(messages 列表)全程工作:用户的每一条消息、模型的每一次输出、工具调用返回的每一个结果,都追加进 messages。每次调用 LLM 都把这份完整历史带上,Agent 始终知道自己做到哪一步、前面发现了什么。
如果某个执行步骤需要特定的专业知识(比如查某个 API 的文档、回想某次历史决策),Agent 可以临时发起一次长期记忆检索,把「查记忆」封装成一个 Tool,用当前上下文作为查询词,把检索结果注入到这一步的 context 里,用完即走,不需要永久保留在 messages 中。
第三阶段:任务结束后,主动「写」记忆
任务完成,进行最后一步:把本次任务产生的新知识写回持久化存储。具体来说,如果用户在对话中表达了新的偏好(「以后写函数都要加类型注解」),就更新实体记忆的对应字段;如果任务产生了有价值的结论(「竞品 A 的定价是按用量收费」),就把这条摘要写入长期记忆,embedding 后存入向量数据库,供下次检索。最后,短期记忆(messages 列表)清空,工作台恢复干净,等待下一个任务。
Agent 的长短期记忆系统怎么做的?记忆是怎么存的?粒度是多少?怎么用的?
记忆系统分两层。
短期记忆就是context window里的对话历史,存当前任务的中间状态,任务结束就清掉;长期记忆用向量数据库存,把信息embedding 后写入,用的时候做语义检索拿回来注入prompt
粒度上通常按一次完整交互或一个关键事件为单位存,太细碎检索碎片化,检索时拿到不完整的信息,太粗糙又丢失细节噪声多,需要根据业务实际调整
比较合理的粒度是按「一次完整交互」或「一个独立的知识点/事件」来存。前者是用户的一个具体请求加上 Agent 处理结果,信息完整性好;后者是把「用户偏好:Python,简洁风格,英文注释」打包成一条结构化记录,检索时一次拿到完整偏好,不会碎片化。过程中的每一步细小中间结果通常不需要存长期记忆,短期记忆够用。
存的时候,把内容转成向量和原文一起存;取的时候,把当前问题也转成向量,找向量空间里最近的几条记忆。「语义相似 = 向量距离近」这个特性,让你不需要精确匹配关键词,靠意思相近就能把相关记忆检索出来。
什么是 Multi-Agent?
多个 Agent 协作完成任务,每个 Agent 各有分工,有的负责搜索、有的负责写代码、有的负责做评审。
单个 Agent 主要受两个限制:一是 context 窗口大小,复杂任务信息量一多就撑爆了,就开始遗忘,这是结构性的限制;二是单点能力,什么都让一个 Agent 做,每件事都是泛才。
并行执行,多个worker同时跑,整体效率有实质性提升
通过专业分工和并行执行,能处理更复杂、更长流程的任务,这是在实际项目里选择多智能体的原因
三种选型
顺序型
并行扇出
辩论/评审
目前组织主要有两种:一种是去中心化,有一个由一个统一的调度者来分配任务,收集结果;另一种是去中心化,Agent之间自行协商、直接通信
说说 Single-Agent 和 Multi-Agent 的设计方案?
Single-Agent适合任务流程清晰,复杂度适中的场景,实现简单、好维护;Multi-Agent适合需要专业分工、任务量大或者需要并行执行的复杂场景
去中心化用的更多,因为好控制、好调试、出问题链路清晰
Single-Agent真正力不从心:任务太长、信息量太大,context撑爆,Agent开始遗忘;不同步骤需要完全不同的专业能力;任务中有多个独立子任务
中心化方案:核心是一个叫Orchestrator的特殊角色,负责三件事:读懂用户的大目标、把他拆成一个个子任务,判断每个子任务交给哪个Agent去做,收集产出,把他们拼成最终答案。
静态路由:预先定义好的
动态规划:根据用户输入动态生成任务计划
自适应编排:实时调整后续计划
去中心化系统会遇到这几类问题
任务分配没有协调
执行顺序没有保证
失败没有感知
没有人确认任务整体完成了
面试总结
第一,选型标准不能只说「任务复杂就用 Multi-Agent」,要说出具体的三类场景:context 要撑爆了、需要不同专业分工、有子任务可以并行,不属于这三类就用 Single-Agent,盲目引入 Multi-Agent 只会增加系统复杂度,带不来对应收益。
第二,Multi-Agent 架构方案要主动提中心化和去中心化两种,而且要明确说出工程里几乎都选 Orchestrator 中心化模式,因为可控、可追踪、出了问题能顺着调度链路排查。
第三,去中心化「听起来灵活」但要能说清楚它的实际问题:任务分配没协调、执行顺序没保证、失败没有感知,这才是它在生产环境里不可用的根本原因。
Agent记忆压缩通常有哪些方法
摘要压缩
滑动窗口
重要性过滤:打分筛选,只选重要内容,不按时间筛选
结构性抽取:把关键信息抽成结构化数据数据存起来,换一种载体存信息
常见的工程组合:滑动窗口+摘要
滑动窗口负责控制对话历史的总长度上限,摘要压缩负责在历史被丢弃之前做一次提炼,把关键信息留下来。
Prompt Caching的思路:
如果 prompt 的前缀部分在多次请求之间是一样的,就把这部分的计算结果缓存起来,下次请求如果前缀匹配,直接复用缓存,不重新计算。
这和前面的记忆压缩是两个不同层次的优化。记忆压缩在「信息层」工作,决定哪些内容值得被保留在对话历史里;Prompt Caching 在「计算层」工作,对已经决定要带进去的内容减少重复计算的开销。两者解决的不是同一个问题,可以同时使用,是互补关系,不是替代。
在工程实践中,为什么有时候选择「手搓」Agent,而不是直接用成熟框架?
框架用起来快,但有几个实际痛点
第一,抽象层太多,调式的时候不知道哪步出了问题,得一层层往下扒
第二,版本升级经常有破环性变更,线上稳定性难保证,手搓没有多余开销
第三,框架的通用设计往往和具体业务需求有偏差,定制起来反而更费劲
折中方案:核心手写,周边工具性性能借用框架
框架适合的时机:POC阶段快速验证idea,目标是跑通而不是优化,,团队刚接触Agent开发,用框架能少踩基础性的坑
手搓的时机:准备上生产,稳定性成为核心关切,性能和成本百年的敏感,业务逻辑高度定制,团队需要高可观测性
工程实践
很多真实项目的演进轨迹是这样的:先用框架快速跑通,验证了方向;遇到第一批线上问题之后,开始把排查困难的关键部分替换为手写;流量上来之后,把性能敏感的核心逻辑全部手写;最后,框架只保留做得很好的周边工具。这条路走下来,既享受了早期框架的速度,又在生产阶段拿回了掌控权。
如何赋予 LLM 规划能力?
为什么需要规划能力:LLM默认一口气生成答案,没有显示推理过程,多步推理任务容易跳步出错,规划机制就是把隐式推理过程显示化
COT是让LLM把推理步骤写出来,线性地一步步推导到答案;要不要把推理写出来
Zero-shot COT Few-shot COT
TOT是把它同时探索多条推理路径,选最优的继续深入,边探索边剪枝边评估;走错了方向怎么纠偏
GOT是图结构推理,推理节点可以复用和合并,适合更复杂的任务;不同路径的中间结论能不能复用
讲讲 Agent 的反思机制?为什么要用反思?具体怎么实现?
生成-评估-改进
反思机制给我的理解:让Agent在完成一个步骤或整个任务后,自我评估输出质量,判断有没有问题,不达标就重试或调整策略
用反思的原因是LLM第一次输出不一定是最优的,逻辑跳跃,遗漏细节,事实错误,表达含糊,加一轮自我检查能显著提升质量,相当于人写完东西自己再看一遍
代价是至少一次LLM调用,token消耗和延迟都会增加,所以只在质量要求高的关键节点启用反思,不是每步都做
评估prompt
任务:{task}当前输出:{current_output}请评估以上输出:1.有没有事实错误或逻辑问题?2.有没有遗漏重要内容?3.表达是否清晰准确? 如果输出已经足够好,回复「PASS」; 否则指出具体问题并给出改进建议。改进prompt
原始任务:{task}当前输出:{current_output}评估意见:{reflection}请根据评估意见改进输出:两个粒度
步骤级
错误发现的早但开销大
任务级
能看到整体问题但前期做的无效工作难以挽回
工程权衡
输出质量要求高、错误代价大的关键节点,比如最终报告生成、重要决策的推理过程开反思
简单直接的任务,比如格式转换、简单问答,加反思不值得,实时性要求高的场景也一样
最重要的是防死循环,必须需要设最大轮次,通常设2-3轮,绝对不能依赖LLM自己判断停止,原因是LLM有时会陷入为了改而改的循环
多agent互评,比自我反思效果更好,同一模型对自己的输出有自洽偏见
如何设计多 Agent 的协作与动态切换机制?
协作靠两件事:消息传递和共享状态。
消息传递
Agent完成自己的工作后把结果发出去,下一个Agent取用;共享状态是所有Agent共同读写一个状态对象,记录进展和中间结果
状态管理:多 Agent 共享状态的设计要点
状态结构要分层,通常会把状态分成「全局状态」和「局部状态」两层
写入规则要明确。最简单也最可靠的做法是「只追加不覆盖」,每个 Agent 完成工作后把结果追加到状态里,而不是修改已有的字段
错误状态的处理。如果某个 Agent 执行失败了,它的错误信息也应该写入状态,而不是悄悄吞掉
如果各 Agent 之间的依赖关系比较强,前一步的结果要直接传给后一步,用共享状态更直接。如果你希望 Agent 之间尽量解耦,互相不知道对方的存在,用消息传递更合适。
动态切换
靠Orchestrator来做,有两种方式:一种是静态路由,提前写好规则,一种是让LLM动态决策,根据当前情况实时判断改把任务交给谁
动态切换要说出静态路由和动态路由各自的优缺点:静态路由稳定可预测,但覆盖不了没预料到的边缘情况;动态路由灵活,但每次多一次 LLM 调用,且行为不可预测。
工程上怎么用
实践中最稳健的做法是两种路由组合用:主流程用静态路由,把确定性的节点切换都写成规则,保证绝大多数情况下系统行为稳定可预测;只在遇到没有匹配规则的边缘情况时,才交给 LLM 动态决策。这样静态路由负责「保底」,动态路由负责「兜住异常」,两者互补。
面试总结
首先,协作机制要说出两种通信方式的本质区别:消息传递的核心是解耦,发送方不需要知道接收方是谁,适合 Agent 之间需要独立运行的场景;共享状态的核心是直接,所有 Agent 读写同一个对象,LangGraph 就是这个思路,适合各步骤依赖关系明确的流水线型任务。
RAG面试题
什么是 RAG?详细描述一个完整 RAG 系统的详细工作流程?
检索增强生成。他解决的核心问题是LLM的知识在训练之后就固定了,遇到到私有数据或者最新的消息他就答不上来。Rag的做法是在生成答案之前,先在外在知识库里检索相关内容,然后把检索结果和用户的问题一起交给LLM,让他基于这些上下文来回答。本质上就是给大模型开了一个开卷考试的口子,不用再考靠死记硬背了。
离线阶段
文档加载
文档切割,实践中一般用500-1000token作为一个chunk,同时做一定的重叠(前后各重叠100 token),避免把一段完整的语义从中间切断
Embedding
入库:把每个chunk的向量和原始文本一起存进向量库
在线阶段:用户提问时实时检索
query 处理:继续Query改写,让LLM把用户的问题改写成更适合检索的形式,,或者从对话历史中补充必要的上下文。
向量检索(粗排):把用户的问题也转成向量,然后去向量库里做相似度搜索,向量检索本质上只是比较两个向量的距离,它没有深度理解查询和文档之间的语义关系,所以召回的结果里难免混入一些「看着近但其实不相关」的内容。
Rerank(精排):把用户问题和每个候选chunk拼在一起,深度理解他们之间的相关性,然后重新排序,把不相关的结果过滤掉
生成,把用户问题+精排后的chunk拼成prompt,交给LLM生成最终答案。Prompt 里通常会明确告诉 LLM「只根据提供的资料回答,资料里没有就说不知道」,这样能有效抑制 LLM 瞎编的倾向。
核心价值
知识可热更新
知识可溯源
大模型的 RAG 主要用来解决什么问题?
知识时效性:LLM训练完知识就固定了,训练截止日期后发生的事他一无所知
私有知识覆盖:公司内部文档、行业专有数据根本没有机会进训练集,LLM 对这些内容是空白的
幻觉问题:没有知识依据时 LLM 容易「自己发挥」编出一个听起来合理但实际错误的答案,给了它参考资料之后幻觉就少很多。
知识被固定在了模型参数里。RAG的解法是把知识存到外部, 用的时候实时检索注入,彻底绕开参数里的知识限制。
幻觉是知识缺失的副产品,RAG解决的是根源,不是单独在治幻觉
相比直接微调 LLM,RAG 解决了什么问题?微调和 RAG 各自的优劣势是什么?
我的理解这两个东西解决的不是一个层面的问题,不是谁替代谁的关系
微调是把新知识直接烧进模型参数里,适合改变模型的行为风格或者培养深度的专业能力;RAG是在推理的时候实时检索注入知识,适合知识需要频繁更新、或者需要有溯源的场景。
知识库类的问答系统我会首选RAG,成本低而且可以随时更新,如果是要让模型学会特定的输出格式或者行业语气,那微调更合适。
实际上这两个方案也可以综合用,先微调再套RAG,先对基础模型做一轮微调,让它学会输出格式、语气风格、行业术语;再用 RAG 来提供具体的知识内容。微调解决「怎么说」,RAG 解决「说什么」,各司其职
调优优先级:先提检索,不是先换模型
面试总结
面试官问 RAG 和微调的区别,你要先说清本质:微调是改模型参数,RAG 是不改参数、推理时注入知识。然后各说优劣:微调擅长定制输出风格、培养深度专业能力,但成本高、更新慢、不可溯源;RAG 擅长知识频繁更新、答案可溯源的场景,但多了检索延迟、检索质量卡住上限。
最后一定要提到两者可以组合使用:微调解决「怎么说」,RAG 解决「说什么」。能把这个组合思路讲出来,面试官就知道你不只是背了定义,而是真正理解了怎么在工程里做选型。
RAG 中的文档是怎么存的?粒度是多大?详细说说文档切割(Chunking)策略?
文档不能直接存进向量库,必须先切成小块也就是chunk,每个chunk分别向量化之后存进一条记录
每条记录我理解有三个核心部分:向量用于相似度检索,原始文本是检索命中之后塞给LLM读的内容,metadata是来源文件、页码这些附加信息,用于过滤和溯源
切割粒度没有固定答案,通常500-1000个token是一个合理的起点,但更重要的是根据文档类型来选策略,,普通文本用固定大小加重叠,有标题结构的文档按语义边界切,代码按函数切,如果既要检索精度又要上下文完整的话,会用父子切割,就是小块检索、大块返回
向量:索引卡(用来找)
原文:书页(LLM真正读的)
metadata:书签(过滤和溯源)
文本切割策略
固定大小切割
语义边界切割
维护一个分隔符优先级列表,先尝试按段落切,然后句子,再然后标点。。。
特殊内容专项处理:
代码按函数和类切分
表格按整块保留,转成markdown格式存储
父子切割
面试总结
500-1000是起点,关键是根据文档类型选策略。用的实际策略:固定大小加重叠做兜底,对结构化文档用语义边界切割,对高质量要求的场景用父子切割
怎么规避语义被切割掉的问题?
第一,切的时候就不要在语义中间截断,用重叠切割和语义边界切割来保证每个chunk内容是完整的,也就是按句子、段落这些自然的边界来切
第二、切完之后用检索策略把上下文补回来,核心方案是句子窗口检索,命中一个句子就把周围几句一起返回给LLM;另外还有父子切割,小块检索命中,大块内容输出
还有一个比较有价值的方案,是Contextual retrieval,在做Embedding之前先让大模型看着整篇文档,为每个chunk生成一段背景说明,把这段背景和chunk拼在一起再向量化,从根本上解决独立chunk没头没尾的问题
和上面句子窗口检索对比:句子窗口是检索命中后动态扩展上下文,父子切割是提前把父子关系存好。两者思路相近,父子切割更灵活(父 chunk 大小可以精确控制),句子窗口实现更简单(不需要维护父子 ID 关系)。
在 RAG 中 Embedding 究竟是什么?如何选择和评估一个 Embedding 模型?
Embedding是把一段文本转成一串固定的浮点数向量的过程。他有一个关键的特性,就是语义相近的文本,转出来的向量在数学空间里的距离也近,RAG的语义检索就是靠这个实现的,不是关键词匹配,而是看两段内容的意思相不相近。
选模型主要看三个维度
第一是中文支持,中文场景优先选BGE系列,中英文比例,text-embedding-3-small
第二是向量维度,维度越高精度越高,但存储成本也越大,1024,1536
第三是最大输入长度,决定能处理多长的chunk
OpenAI的text-embedding BGE Qwen3-Embedding
正确的评估方法
准备几百条业务相关的【问题+正确答案chunk】对,分别用候选模型最检索,看正确的chunk有没有出现在前K条结果里,指标为Hit@K
Embedding 有哪几种算法你了解过吗?
Embedding算法大致经历了三代演进
第一代是静态词向量,以Word2Vec和GloVe为代表,每个词映射成向量,但同一个词不管上下文是什么,向量永远不变,处理不了多义词
第二代是以Bert为代表的上下文相关向量,同一个词在不同语境下有不同的向量,表达能力提升,但Bert本身输出的是token级别的向量,两个句子要比较相似度就必须拼在一起跑,百万条文档就要跑百万次,检索速度不可接受
第三代是SBert、SimCSE、BGE为代表的句子级对比学习Embedding,专门为两段文本有多相似这个任务优化,提前把所有文档向量算好存起来,查询时只算一次,是RAG场景的标配。
什么是向量数据库?有没有做过向量数据库的对比选型?
向量数据库是专门用来存储和检索高维向量的数据库,核心能力是近似最近邻搜索,也叫ANN,能在百万甚至亿级的向量里快速找出最相似的几条
普通关系型数据库的索引结构是对高维向量基本是失效的,需要专门的数据库来处理
本地开发阶段用Chroma,配置上手极快,生产环境中的中小规模推荐Qdrant,性能不错,API也比较简洁;如果到了超大规模就要考虑Milvus,字节和小米都在用;不想自己运维就用Pinecone云托管,如果项目中已经有PostgreSQL,也可以直接用pgvector,不用引入新组件
核心索引算法
HNSW(Hierarchical Navigable Small World):构建一个多层图结构,查询时能从最上层的稀疏图开始导航逐层收缩范围,最终在最底层找到最近邻。优点是召回率高、查询速度快;缺点是建索引时内存消耗大,不适合内容极度受限的场景。
几个参数:M(每个节点最多认识几个邻居);ef_construction(建图时每个节点考察多少候选);ef(查询时搜索的候选集大小)
IVF(Inverted File Index):先对向量做聚类,把相似的向量分进一个桶中,查询时只搜最相关的几个桶,不是全量遍历。优点是占用内存小、适合超大规模,缺点是精度比HNSW略低,需要调参。
HNSW 精度高、速度快,为什么还需要 IVF?原因很简单:HNSW 的内存消耗和向量数量成正比,到了亿级规模内存可能扛不住。IVF 用聚类换内存,牺牲一点精度就能处理超大规模数据,两者各有适用场景。
向量数据库的核心能力
metadata过滤,先过滤在检索
实时更新
与关键词检索融合,纯向量检索对一些精确词语(比如产品型号「GPT-4o」、专有名词)的效果不好,有些向量数据库同时支持向量检索 + BM25 关键词检索,做混合召回效果更好。
讲讲你用的向量数据库?数据量级是多大?性能如何?遇到过性能瓶颈吗?
生产环境用的是Milvus,数据量在百万条向量左右,每条是1024维,用HNSW索引,单次查询的延迟在20-50ms。选Milvus主要是因为他支持分布式部署和读写分离,适合数据量大,并发高的场景。
给出具体的数据量级和性能指标,150 万条 1024 维向量,HNSW 索引,P50 延迟 20ms,P99 延迟 60ms,100 QPS 并发稳定。这些数字要有,不能说「感觉挺快的」
遇到两个典型的瓶颈
第一个是内存压力,百万级1024维向量原始数据就要好几个GB,后来开启了标量量化SQ8,把float32压成int8,内存直接降到原来的1/4
第二个是大批量写入的时候会触发后台Segment合并,把多个小的增量 Segment 合并成大的封存 Segment 并建好索引,影响查询延迟,解法是批量写入改到业务低峰期,分批小批次写入
你使用 RAG 给大模型一个输入,系统是怎样的工作流程?
系统先对问题做预处理(改写成更适合检索的形式),然后把问题向量化,,去向量库里找最相关的文档片段,再经过精排筛掉噪声,最后把筛选出来的片段和问题一起拼成Prompt交给大模型,基于这些参考资料生成最终答案。
让大模型在回答时有真实的知识作为依据,而不是凭空发挥。
第一步:Query 预处理(Query Rewrite)
简单改写,把口语化问题改写成更正式、独立完整的检索句。
HYDE(Hypothetical Document Embeddings):让 LLM 先「假设」一个可能的答案,用这个假设答案的向量去检索。你可能会觉得奇怪,不用问题去搜反而用假设答案去搜?原因很简单:问题和答案的用词往往差异很大,但假设答案和真实答案的用词更接近,它们的语义空间天然更匹配,命中率往往更高。
多角度改写:把同一个问题扩展成 3-5 种不同表述,分别检索后合并结果,覆盖面更广
第二步:Query Embedding(问题向量化)
第三步:向量检索(ANN 搜索)+ 多路召回
向量检索负责捕获语义相似性,BM25/全文检索负责捕获关键词精确匹配,两路各有所长。
多路结果通过 RRF(互倒排名融合)算法合并,最终召回的结果比单路覆盖面更广、质量更高。
第四步:Rerank 精排(Cross-Encoder)
把用户问题和每个候选片段拼在一起输入,深度理解他们之间的语义匹配程度
第五步:Prompt 拼装
prompt=f""" 你是一个专业助手,请根据以下参考资料回答用户的问题。 如果参考资料中没有相关信息,请回答「根据现有资料无法回答」,不要自行猜测。 参考资料: [1]{chunk_1}[2]{chunk_2}[3]{chunk_3}用户问题:{user_query}"""第六步:大模型生成 + 溯源
整个链路串起来,工程上常见的优化手段是缓存高频 Query 的检索结果,以及把向量检索和 BM25 检索并行执行,把两路召回的延迟从串行叠加变成取最大值。
请你介绍一下向量检索和关键词检索的区别?
关键词检索(BM25 这类)靠的是词频统计,看查询词在文档里出现了多少次,擅长精确命中;向量检索靠的是语义空间里的距离,能理解「换了种表达方式的同一个意思」,擅长模糊语义匹配。两者各有盲区:BM25 遇到同义词就没辙,向量检索遇到专有名词、产品型号这类精确词就容易漏。所以 RAG 系统里通常两路都跑,向量检索捕获语义相关的内容,BM25 精准命中关键词,再用 RRF 算法把两路结果合并,这样覆盖面比单路宽很多。
它的基本思路可以用一个类比来理解:想象一个巨大的图书管理员,他给每本书建了一张「词汇卡片」,记录这本书里每个词出现了几次。用户来查「手机 截图」,管理员就把包含「手机」的书挑出来,再把包含「截图」的书挑出来,然后看哪些书同时被挑中、被挑中的词出现频率高,这些书就排前面。这个「词汇卡片」系统就是倒排索引,它记录的不是「每篇文档里有哪些词」,而是反过来,记录「每个词出现在哪些文档里」。
打分的时候看两个指标:词频(IF)和稀缺度(IDF)
RRF(Reciprocal Rank Fusion):互倒排名融合
RRF 的思路很巧妙:它不看各路的原始分数,只看排名。你可能会问,为什么不直接把两路的分数加权平均呢?原因是向量相似度(0 到 1 的余弦值)和 BM25 分数(可能是任意正数)的量纲完全不同,就像拿摄氏度和华氏度直接加在一起没有意义。RRF 用排名的倒数来打分,排名越靠前倒数越大,最终按总分降序排列。这样,两路都认为相关的文档会排在最前面,只有一路认为相关的文档也不会被完全丢掉。
defreciprocal_rank_fusion(results_list,k=60):""" results_list: 多路检索结果,每路是一个 [doc_id, ...] 的有序列表 k: 平滑参数,防止排名第 1 的文档权重过大,通常取 60 """scores={}forresultsinresults_list:forrank,doc_idinenumerate(results):ifdoc_idnotinscores:scores[doc_id]=0# 排名越靠前(rank 越小),倒数越大scores[doc_id]+=1/(rank+k)# 按总分降序排列,取 Top-Kreturnsorted(scores.items(),key=lambdax:x[1],reverse=True)# 使用示例vector_results=["doc_a","doc_b","doc_c"]# 向量检索的排序结果bm25_results=["doc_b","doc_d","doc_a"]# BM25 的排序结果merged=reciprocal_rank_fusion([vector_results,bm25_results])# doc_b 两路都高排,doc_a 也两路命中,会排在前面如何润色用户的 Query(Query Rewrite)?目的是什么?
。。。