想象你走进一家餐厅,点一份“西湖醋鱼”。一位“记忆型厨师”从不看冰箱,张嘴就讲做法,可一旦这道菜他没见过,就会用相似的菜名拼凑出一个“听起来很像”的流程——就像传统大模型凭记忆硬编答案,编着编着就开始胡说八道。另一位“检索型店员”则相反,你问什么都给你抱来一堆菜谱,让你自己对着找——就像关键词匹配问答系统,答非所问还冷冰冰。而一家真正的老店会怎么做?店员先确认你的需求,去冰箱里找到最新鲜的草鱼和醋,再交给厨师按标准菜谱下锅,上桌时还告诉你这条鱼来自哪个供货商。 这就是检索增强生成(RAG)的日常:让问答系统先“查资料”再“作答”,让每一个答案都能在知识库里找到出处。RAG = 检索(Retrieval)+ 生成(Generation),它把传统问答“只会背”和“只会编”的两大毛病缝合在一起,用知识库作为答案的“食材仓库”,用检索作为“选料”环节,用大模型作为“掌勺”的厨师。这样一套组合拳,让系统告别张口就来的尴尬,也让你在企业里能交付“敢拍胸脯保证有依据”的问答产品。

岗位关联

对AI从业者而言,RAG是目前企业落地大模型最实用的技能之一:掌握它,你就能快速搭建出有据可查、可溯源、可兜底的问答系统,直接把大模型从“玩具”变成“生产力工具”。

目录
  1. 可视化演示
  2. 1. RAG 核心概览:两条腿走路,才不胡说八道
  3. 2. 知识库索引:把冰箱整理成食材仓库
  4. 3. 查询处理:听懂顾客的“黑话”
  5. 4. 检索召回:精准找到对的食材
  6. 5. 生成融合:炒出一盘有依据的菜
  7. 6. 质量保障:没把握就别硬上,还要说明出处
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示编码器将输入序列编码为上下文向量,解码器基于上下文逐步生成多步预测输出的完整流程。切换模式观察各阶段动态。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+captions.encode+'

1. RAG 核心概览:两条腿走路,才不胡说八道

想象一家餐厅里有一位“全凭记忆”的主厨。你问他某道私房菜怎么做,他张嘴就能说出一套流程;可一旦遇到没学过的菜,他就会用相似的菜名拼凑出一个“听起来很像”的做法。另一位“只会查菜单”的服务员则相反,你问什么都给你搬来一堆菜谱,让你自己对着找。这两类人正是传统问答系统的写照——一个天生话痨爱编造,一个只会翻书不会组织回答。

RAG 要做的事情,就是让“查资料”和“组织回答”配合起来。它的全称是 Retrieval-Augmented Generation,检索增强生成。整个链路可以拆成三步:先构建知识库索引,把外部文档切分、向量化后存起来;再检索,拿到用户问题后从库里找回最相关的几段文本;最后生成,把问题和这些文本片段一起交给大语言模型,让它在“有据可依”的前提下组织答案。

这个过程的本质可以用一个概率公式来理解:模型不再拍脑袋生成答案,而是先依赖一个检索分布挑选出证据文档,再在证据文档的条件下生成答案。

回头再看餐厅的比喻,步骤其实一一对应:$D_{ret}$ 是厨师从食材仓库里挑出的那一把草鱼和醋;$P(d \mid q)$ 是挑食材时的相关度判断——草鱼合适,猪肉不行;$P(a \mid q, d)$ 则是炉灶上真正做菜的过程。正因为每一步都有据可依,RAG 才得以告别“凭印象说话”的坏毛病。

P(a∣q)=∑d∈DretP(d∣q) P(a∣q,d)P(a \mid q) = \sum_{d \in D_{ret}} P(d \mid q) \, P(a \mid q, d)
通俗说:答案 a 的概率 = 先算出哪些资料 d 和问题 q 相关,再算给定资料和问题后生成答案的概率。两条腿一起走,才不会摔跟头。
  1. 候选证据集合$D_{ret}$ 表示从知识库中检索回来的文档集合,它是后续生成的全部“依据”,是 RAG 与纯生成最本质的差别。
    Dret={d1,d2,…,dk}D_{ret} = \{d_1, d_2, \dots, d_k\}
  2. 检索条件概率$P(d \mid q)$ 量化了文档 d 与问题 q 的相关程度,检索器就是要让相关文档的这项概率尽量高。
    P(d∣q)∝exp⁡(sim(q,d))P(d \mid q) \propto \exp\bigl(\mathrm{sim}(q, d)\bigr)
  3. 生成条件概率$P(a \mid q, d)$ 表示模型在看到问题 q 和文档 d 后,生成答案 a 的整体概率,这就是最后“炒菜”的步骤。
    P(a∣q,d)P(a \mid q, d)

2. 知识库索引:把冰箱整理成食材仓库

知识库里的原始文档就像刚从菜市场买回来的一整扇猪肉、一整条鱼。直接整块丢进冰箱,等要做菜时再找,既难化冻又占地方。聪明的厨师会先把肉切成丝、鱼片成片,按用途分装在保鲜盒里,贴好“排骨、里脊、鱼片”的标签摆上货架。这一步,对应 RAG 的“文档预处理”。

RAG 的索引构建包含三个动作:切分(Chunking)、向量化(Embedding)、建索引(Indexing)。切分把长文档切成若干语义完整的文本块;向量化把每个文本块变成一个固定长度的实数向量;建索引把这些向量组织成可快速检索的数据结构,同时保留来源、时间等元数据。

切分不是越细越好。块太大,向量语义被稀释,检索不精准;块太小,语义不完整,模型看了也难理解。实践中常采用“滑动窗口式”切分,相邻块之间保留一段重叠内容,避免关键句子恰好落在切口上被拦腰斩断。切分数量可以用下面的公式估算:

切分完成以后,每一小段文本都要经过嵌入模型变成向量。你可以把向量想象成每盒食材上的“营养标签”——它用一堆数字概括了文本的语义。所有标签汇集起来,就成了这个知识库的“货架地图”,接下来所有检索动作都在这张地图上完成。冰箱整理得越清楚,出菜就越快越准。

N=⌈L−SC−S⌉N = \left\lceil \frac{L - S}{C - S} \right\rceil
通俗说:文本总长度减去重叠部分后,除以每次新推进的长度,最后不够一块也算一块。
  1. 变量定义L 是文档总长度(token 数),C 是每块的目标大小,S 是相邻两块之间的重叠长度。
    L>C>S≥0L > C > S \ge 0
  2. 每次实际推进量因为上一块的末尾 S 个 token 会在下一块开头复用,所以每一块真正带来的“新内容”只有 C - S。
    Δ=C−S\Delta = C - S
  3. 向上取整总长度按 Δ 分段,最后一段哪怕只有一个 token 也要单独成块,所以用天花板函数取整。
    N=⌈L−SΔ⌉N = \left\lceil \frac{L - S}{\Delta} \right\rceil

3. 查询处理:听懂顾客的“黑话”

老顾客走进店里,只说一句“老样子”,后厨根本不知道该做哪道菜。有经验的服务员不会直接把“老样子”传给后厨,而是先笑着确认:“还是上次的红烧肉加米饭吧?”——把省略的信息补全,再写单传给后厨。用户输入问题给问答系统时,也会出现大量类似的“黑话”。

在 RAG 中,这个“补全黑话”的环节叫 Query 处理 / Query 改写。它的目标是把用户原始问句改写成更适合检索的完整问题。常见操作包括:指代消解(“这个怎么弄”→“这个报销流程怎么弄”)、同义替换(“咋”→“怎么”)、口语转书面语(“多少钱”→“价格”),以及多轮场景下拼接对话历史。

改写之后,为了提升召回的容错率,还可以做“查询扩展”:不只检索一个句子,而是生成多个等价问法一起检索,再把结果合并排序。假设原始查询是 q,扩展后得到了一组候选表达 q1 到 qm,最终参与检索的查询集合就是:

别忘了,Query 改写看起来只是“多问一句”,却常常是检索质量的分水岭。就像服务员如果图省事直接把“老样子”传进后厨,端上来的菜多半不是顾客心里想的那道;而一句确认,就能让后续所有环节少走弯路。

Q′={q}∪{q1,q2,…,qm}Q' = \{q\} \cup \{q_1, q_2, \dots, q_m\}
通俗说:一句“老样子”不够用,就再列几个“红烧肉套餐”“还是上次那桌菜”一起报给后厨,总能蒙对一个。
  1. 原始查询q 是用户输入的原话,通常很短,甚至包含指代和省略,直接拿去检索效果差。
    q=用户原始输入q = \text{用户原始输入}
  2. 扩展查询集q1 到 qm 是通过改写、同义替换等方式生成的一个或多个等价问法,用来弥补原始表达的信息缺失。
    qi=Rewritei(q)q_i = \mathrm{Rewrite}_i(q)
  3. 联合检索Q' 是原句和扩展句的并集,检索时对 Q' 中每个元素分别查找,最后去重合并。
    Q′={q}∪{q1,q2,…,qm}Q' = \{q\} \cup \{q_1, q_2, \dots, q_m\}

4. 检索召回:精准找到对的食材

食材已经分装好、标签已经贴好,现在顾客点了“鱼香肉丝”,厨师要做的就是从一冰柜的料理盒里快速找到木耳、肉丝和泡椒。他不会把整个冰柜都抱到灶台上,而是扫一眼标签,挑出最相关的那两三盒。

检索环节正是干这件事。系统会把改写后的查询也变成一个向量,然后在知识库向量集合中,找出与查询向量距离最近、语义最相似的前 K 个文本片段。衡量两个向量相似程度最常用的指标是余弦相似度:

实际工业系统中,单纯靠向量检索还不够。因为向量适合捕捉“语义相近”,却容易漏掉“字面上必须一致”的关键信息,比如工单号、邮箱、型号。所以很多系统采用混合检索:向量检索 + 关键词检索并行,再把两路结果合并,经过重排序(Rerank)模型精打分,最终选出 TopK 片段送入下游。

这一步决定了“灶台上放什么”。如果厨师抱来的两盒食材跟鱼香肉丝毫无关系,后面再厉害的炒功也白搭。检索召回就是 RAG 的“选料关”,这一关守不住,生成阶段再努力也只能“巧妇难为无米之炊”。

cos⁡(q,v)=q⋅v∥q∥ ∥v∥\cos(\mathbf{q}, \mathbf{v}) = \frac{\mathbf{q} \cdot \mathbf{v}}{\|\mathbf{q}\| \, \|\mathbf{v}\|}
通俗说:把问题和文本片段各画成一根从原点出发的箭头,两根箭头方向越一致,夹角越小,余弦值越接近 1,两者越相关。
  1. 点积$\mathbf{q} \cdot \mathbf{v}$ 把两个向量对应位置的数字相乘再求和,反映两个方向上的共同分量。
    q⋅v=∑i=1dqivi\mathbf{q} \cdot \mathbf{v} = \sum_{i=1}^{d} q_i v_i
  2. 模长归一化$\|\mathbf{q}\|$ 和 $\|\mathbf{v}\|$ 是两个向量的长度,除以它们的乘积可以消除文本长短对分数的影响。
    ∥q∥=∑i=1dqi2\|\mathbf{q}\| = \sqrt{\sum_{i=1}^{d} q_i^2}
  3. 相似度取值余弦值落在 [-1,1],对非负向量则落在 [0,1]。值越大,语义越接近,越应该被召回。
    cos⁡(q,v)∈[−1,1]\cos(\mathbf{q}, \mathbf{v}) \in [-1, 1]

5. 生成融合:炒出一盘有依据的菜

食材备齐了,接下来就是真正上灶炒菜。但厨师不会闭着眼睛乱炒——他得看一眼标准菜谱:先爆香蒜末,再下肉丝,最后放木耳和泡椒,大火翻炒两分钟。菜谱和食材按顺序摆在一起,才能做出一道形神兼备的鱼香肉丝。

在 RAG 的生成阶段,“菜谱”就是 Prompt 模板,“食材”就是上一步检索到的 TopK 片段。系统把这些片段和用户问题一起组装进 Prompt,再交给大语言模型生成答案。为了让模型更专注地使用证据,Prompt 通常会明确声明:“请仅依据以下资料回答问题”。检索片段通常带编号,模型回答时可直接引用。

从建模角度看,生成器是一个条件语言模型。它不再单独依赖问题 q,而是把检索集合 $D_{ret}$ 作为额外的条件,每一步生成都同时参考问题、已有输出和证据文档:

同样一筐食材,有没有菜谱、有没有约束,做出来的菜完全不同。生成融合就是在“开放创作”和“照本宣科”之间找到了中间道路:既给模型充分的连接语义的能力,又用检索证据和 Prompt 规则把它的想象力牢牢拴在事实的缰绳上。

P(a∣q,Dret)=∏t=1TP(at∣a<t,q,Dret)P(a \mid q, D_{ret}) = \prod_{t=1}^{T} P(a_t \mid a_{<t}, q, D_{ret})
通俗说:模型写答案(句子 a)时,是像写出一连串字(a1 到 aT)那样逐字生成的;每写一个字,都要看到“问题 + 证据 + 已经写出的部分”。
  1. 序列分解答案 a 被分解为 T 个 token,整体概率等于每个 token 在给定上文条件下的概率连乘。
    P(a∣q,Dret)=∏t=1TP(at∣a<t,q,Dret)P(a \mid q, D_{ret}) = \prod_{t=1}^{T} P(a_t \mid a_{<t}, q, D_{ret})
  2. 自回归条件$a_{<t}$ 表示已经生成的前 t-1 个 token,模型每步只在前文基础上继续,保证句子连贯。
    a<t={a1,a2,…,at−1}a_{<t} = \{a_1, a_2, \dots, a_{t-1}\}
  3. 证据贯穿全程q 和 $D_{ret}$ 出现在每一步的条件中,确保答案始终贴着问题和证据走,而不是天马行空。
    P(at∣a<t,q,Dret)≠P(at∣a<t)P(a_t \mid a_{<t}, q, D_{ret}) \neq P(a_t \mid a_{<t})

6. 质量保障:没把握就别硬上,还要说明出处

再老练的厨师也有打烊前发现冰柜空空如也的时候。这时候如果你催他“随便炒个菜应付下”,他可能会端出一盘不伦不类的东西。一位负责任的厨师会直接告诉你:“这道菜今天做不了,要不您换个菜?”——坦诚,反而赢了顾客的信任。

问答系统也一样。模型并不总是能检索到高质量证据,即使检索到了,也可能因为证据互相矛盾而拿不准。生产级 RAG 必须在生成之后加一道“质检关”:对无答案场景、低置信度场景做出明确处置,而不是硬编一个答案糊弄用户。常见的做法是给答案打一个置信度分数,再与预设阈值比较。

决策逻辑可以用一个分段函数来表达:

除了置信度,生产系统还会做引用溯源:模型在答案的每个关键句后面标注“来源[1]”“来源[2]”,对应到具体的检索片段。这就像菜单上写下每道菜的食材来自哪个供货商——万一顾客吃了不舒服,可以立刻追溯到原料批次。质检关加上溯源机制,问答系统才算真正“对说的话负责”。

Answer(q)={a,if Conf(a∣q)≥θ拒答或澄清,otherwise\mathrm{Answer}(q) =\begin{cases}a, & \text{if } \mathrm{Conf}(a \mid q) \ge \theta \\\text{拒答或澄清}, & \text{otherwise}\end{cases}
通俗说:系统对候选答案的把握超过阈值 θ,就大胆回答;低于阈值,就老实说“不知道”并引导用户换个问法。
  1. 置信度函数$\mathrm{Conf}(a \mid q)$ 是系统对“答案 a 是问题 q 的正确回答”的信心,可由检索分数、生成概率、验证模型综合得到。
    Conf(a∣q)=Combine(sret,pgen)\mathrm{Conf}(a \mid q) = \mathrm{Combine}(s_{ret}, p_{gen})
  2. 阈值比较θ 是人工设定的门槛。门槛过高会答得少而精,门槛过低会答得多而糙,需要根据业务场景权衡。
    Conf(a∣q)≥θ\mathrm{Conf}(a \mid q) \ge \theta
  3. 双分支决策达标输出答案;不达标进入兜底流程——回复“没有找到可靠资料”、建议用户补充条件,或者转人工客服。
    Answer(q)={a,Conf≥θ拒答/澄清,otherwise\mathrm{Answer}(q) = \begin{cases} a, & \mathrm{Conf} \ge \theta \\ \text{拒答/澄清}, & \text{otherwise} \end{cases}

AI 应用场景

企业客服机器人(FAQ 问答)

阶段:问答推理与答案生成

解决的问题:传统 FAQ 机器人靠关键词匹配,用户换个问法(“运费谁出”“包邮吗”“邮费怎么算”)就命中不同规则,甚至给出互相矛盾的答案。RAG 直接把《售后政策》《物流规则》等文档切块入库,无论哪种问法都能检索到同一条规则,并附上出处。

具体案例:某电商平台在客服机器人中接入 RAG 后,用户问“退货谁承担运费”,机器人返回“退货由买家承担运费,质量问题的运费由商家承担”,并附上政策原文片段链接;用户追问“你是说邮费吗”,也有模块通过同义改写检索到同一条文档。上线后,客服转人工率下降了约 40%。

企业内部知识库问答(员工助手)

阶段:检索召回与内容生成

解决的问题:员工问“年假有几天”“VPN 怎么登录”,答案分散在 HR 制度、IT 服务指南等多份文档里。AI 工程师用 RAG 把全部内部文档切块入库,用自然语言问答取代原来的行政答疑群,减少重复提问。

具体案例:某科技公司为员工搭建内部问答助手,知识库覆盖行政、财务、IT 制度。员工问“VPN 怎么登录”,助手检索到 IT 指引片段,直接给出登录地址和密码修改流程,并标注“来源:IT服务指南 2025版”。员工点击来源即可核验,极大降低了对 IT 工单的依赖。

Python 演示

python95 行
import mathimport refrom collections import Counter  class MiniRAG:    """极简RAG演示: 分块 -> 向量化 -> 检索 -> 组装带引用答案"""     def __init__(self, docs, chunk_size=30, overlap=5):        self.chunks = []        self.vectors = []        self.chunk_size = chunk_size        self.overlap = overlap        for doc in docs:            self._add_document(doc)     def _tokenize(self, text):        return re.findall(r'[\u4e00-\u9fa5]|[a-z0-9]+', text.lower())     def _char_bigrams(self, text):        chars = re.findall(r'[\u4e00-\u9fa5]', text)        return [chars[i] + chars[i+1] for i in range(len(chars) - 1)]     def _add_document(self, doc):        tokens = self._tokenize(doc)        start = 0        while start < len(tokens):            end = min(start + self.chunk_size, len(tokens))            chunk_text = ''.join(tokens[start:end])            self.chunks.append(chunk_text)            self.vectors.append(self._vectorize(chunk_text))            if end == len(tokens):                break            start = end - self.overlap            if start >= end:                break     def _vectorize(self, text):        vec = Counter(self._tokenize(text))        for bg in self._char_bigrams(text):            vec[bg] += 1        return vec     def _cosine(self, vec_a, vec_b):        common = set(vec_a.keys()) & set(vec_b.keys())        dot = sum(vec_a[k] * vec_b[k] for k in common)        norm_a = math.sqrt(sum(v * v for v in vec_a.values()))        norm_b = math.sqrt(sum(v * v for v in vec_b.values()))        if norm_a == 0 or norm_b == 0:            return 0.0        return dot / (norm_a * norm_b)     def _rewrite(self, query):        synonyms = {            '咋': '怎么',            '咋样': '怎么',            '多少钱': '价格',            '费用': '价格',            '报销': '报销流程',        }        for k, v in synonyms.items():            query = query.replace(k, v)        return query     def retrieve(self, query, top_k=2):        q_vec = self._vectorize(self._rewrite(query))        scored = []        for i, vec in enumerate(self.vectors):            scored.append((self._cosine(q_vec, vec), i))        scored.sort(key=lambda x: -x[0])        return [(self.chunks[i], score) for score, i in scored[:top_k]]     def answer(self, query):        hits = self.retrieve(query)        if not hits or hits[0][1] < 0.05:            return '抱歉, 知识库中没有找到可靠信息.'        segments = [f'[{i+1}] {chunk}' for i, (chunk, score) in enumerate(hits)]        context = '\n'.join(segments)        main = hits[0][0]        return f'根据资料: {main} (来源: {context})'  if __name__ == '__main__':    docs = [        '公司VPN登录地址是vpn.example.com, 首次登录需要修改初始密码, 离职后账号自动注销.',        '员工报销需在OA系统提交申请, 发票拍照上传, 经部门审批后三个工作日内到账.',        '申请年假需要提前三天在OA提交, 每年10天, 未休完的可以结转至次年3月.',        '公司内部培训每月最后一个周五举行, 报名请在企业微信联系培训专员.',    ]    rag = MiniRAG(docs)    test_queries = ['报销怎么弄?', '年假有多少天?', 'VPN咋登录?', '这周五有会议室吗?']    for q in test_queries:        print('问:', q)        print('答:', rag.answer(q))        print()
目的:用约 100 行代码实现一个微型 RAG 问答系统,覆盖文档切分、简化向量化、余弦检索、Query 改写和带引用的答案组装,让你直观看到 RAG 的完整工作流。
  1. chunk_text = ''.join(tokens[start:end])按固定窗口切分文本,overlap 让上下文衔接不中断。
  2. self.vectors.append(self._vectorize(chunk_text))每个块都被转成词频向量,模拟真实系统中的嵌入向量。
  3. vec[bg] += 1把中文相邻二字组合加入特征,增强短语级语义表达。
  4. dot = sum(vec_a[k] * vec_b[k] for k in common)只计算两个向量共同出现的词,加速余弦相似度计算。
  5. return dot / (norm_a * norm_b)归一化点积,得到落在 [0,1] 的相似度分数。
  6. query = query.replace(k, v)同义词改写,把“咋”“多少钱”等口语替换为更完整的表达。
  7. scored.sort(key=lambda x: -x[0])按相似度从高到低排序,选出 TopK 片段。
  8. if not hits or hits[0][1] < 0.05:置信度门槛,检索分数过低时拒绝作答。
  9. return f'根据资料: {main} (来源: {context})'组装答案并附带来源片段编号,实现引用溯源。

常见误区

误区感觉 RAG 没啥技术含量,不就是把知识库文本塞到 prompt 前面给模型看吗?随便做做就能减少幻觉。

正解RAG 的成败取决于“能否检索到最相关的片段”。如果检索回来的是无关文本,模型反而会被带偏,比不检索更糟。就像给厨师拿错食材,再好的菜谱也做不出好菜。切分策略、向量质量、重排序环环相扣,缺一不可。

为何常见:最终效果看起来都是“模型看到了额外文本”,让人容易忽略中间的检索工程。

误区检索时把整篇文档甚至整本书都丢给模型,以为上下文越长信息越多,答案越准。

正解过长上下文会带来两个问题:一是大模型注意力容易分散,关键信息被淹没(即“Lost in the Middle”现象);二是成本与延迟上升。RAG 需要把文档切成合理大小的语义块,只送入相关信息。就像下锅的菜要切丁切片,一整棵白菜直接扔进锅里很难炒熟。

为何常见:人们本能地认为“信息越多越好”,忽略了模型的信息处理瓶颈。

误区部署了 RAG 之后,模型应该百分之百正确,再也不会胡说八道。

正解RAG 能大幅减少由“记忆缺失”导致的幻觉,但模型仍可能在“有证据但理解错了”甚至“检索到相关但错误使用”时产生错误答案。它不能完全消除幻觉,仍然需要置信度判断、答案校验和人工兜底。就像厨师有了正确的食材,仍可能放错调料。

为何常见:宣传中常说 RAG“解决幻觉”,让人误以为彻底解决了,实际是“缓解”而非“杜绝”。

误区检索时向量相似度分数最高的块,内容就是最权威最正确的。

正解向量相似度衡量的是语义相关性,不是事实正确性。一段相关但过时或错误的文本也可以有很高的相似度。所以生产系统还要结合更新时间、文档来源、人工标注等做质量过滤。就像“看起来像那么回事”的资料不等于“正确”的资料。

为何常见:把“相似”和“正确”混为一谈是常见的直觉误区。

误区用户怎么问就怎么查,反正向量检索能理解语义,改写纯属多余。

正解用户口语常常包含指代、省略和同义表达,比如“这个怎么弄”“上次那个”,底层向量模型不一定能处理好。通过改写补全能显著提升召回率,尤其在专有名词、缩写场景。“老顾客说老样子,服务员也要确认一下”这句确认,往往就决定了检索的命中质量。

为何常见:向量检索的“语义理解”能力被神化了,实际对复杂指代和超短 query 依然吃力。

面试题

Q1. RAG 中“R”和“G”分别代表什么?

单选题 · easy

  • A.Retrieval 和 Generation← 正确答案
  • B.Random 和 Generation
  • C.Ranking 和 Grounding
  • D.Retrieval 和 Gradient

答案:A。RAG 的全称是 Retrieval-Augmented Generation,即检索增强生成。A 正确。B 的 Random、C 的 Ranking、D 的 Gradient 都不是 RAG 的标准构成。

Q2. 为什么要对知识库文档做分块处理?

单选题 · easy

  • A.减少模型上下文的负担
  • B.让检索片段更符合语义单元← 正确答案
  • C.让知识库存储成本升高
  • D.让索引查询耗时显著增加

答案:B。分块(Chunking)能让检索命中到更精准的语义单元,同时避免把整篇长文档塞进 Prompt 造成注意力稀释。A 是分块带来的好处之一,B 更能体现“语义单元”这一核心理由,C 和 D 都是负面效果,不是目的。

Q3. 下列哪一项是 Query 改写要解决的问题?

单选题 · easy

  • A.文档切分的粒度过小
  • B.用户口语的指代与省略← 正确答案
  • C.索引的存储空间不足
  • D.模型参数量过于庞大

答案:B。用户会问“这个怎么弄”“那个流程呢”,这些指代和省略若不补全,检索很难命中。Query 改写负责把这类问题补成完整可检索的句子。A/C/D 分别属于分块、存储和模型部署问题,与用户表达无关。

Q4. 余弦相似度的取值范围是?

单选题 · mid

  • A.[-1, 1]← 正确答案
  • B.[0, 1]
  • C.[-∞, +∞]
  • D.[0, +∞]

答案:A。余弦相似度是两个向量夹角的余弦值,天然落在 [-1,1];若向量元素非负(常见于嵌入向量),则实际落在 [0,1]。选项 A 最准确,B 只在非负向量时成立,C/D 均错误。

Q5. RAG 生成阶段中,检索片段主要融入到哪里?

单选题 · mid

  • A.模型的损失函数约束
  • B.Prompt 的上下文区域← 正确答案
  • C.大规模训练数据集
  • D.模型底层的词表

答案:B。生成阶段的做法是将检索到的 TopK 片段放进 Prompt 上下文,让模型参考这些证据作答。A 属于训练优化手段,C 是预训练阶段的事,D 是词表设计,都不对。

Q6. 当检索置信度低于阈值时,生产系统通常应该?

单选题 · mid

  • A.随便编一个答案
  • B.拒绝回答并引导澄清← 正确答案
  • C.直接返回空字符串
  • D.重复一遍用户问题

答案:B。低置信度场景应拒答或请求澄清,避免“硬答”造成的幻觉。A 正是幻觉的源头,C 对用户体验伤害大,D 对解决问题毫无帮助。

Q7. 关于 RAG 中的答案引用溯源,下列说法正确的是?

单选题 · hard

  • A.引用会降低答案可信度
  • B.只需在最后列出文献即可
  • C.答案应标注具体检索片段← 正确答案
  • D.引用只对开发者有意义

答案:C。成熟的 RAG 系统会在答案中标注来源片段编号,用户可点击核验,提升可信度与可排查性。A 说法相反,B 只在文末列文献远不够,D 忽视了用户核验的价值。

Q8. 以下哪些策略能提升 RAG 的检索召回效果?

多选题 · mid

  • A.Query改写补全指代省略← 正确答案
  • B.混合检索兼顾语义与字面← 正确答案
  • C.对TopK结果进行重排序← 正确答案
  • D.将所有停用词全部删除

答案:ABC。A 改写查询可以提升召回,B 混合检索(向量+关键词)能兼顾语义与字面匹配,C 重排序能精筛 TopK。D 停用词删除并非提升召回的标准策略,过度删除反而可能损失关键信息。

Q9. 检索片段进入 Prompt 时,应注意哪些问题?

多选题 · hard

  • A.控制片段长度防止注意力稀释← 正确答案
  • B.给片段标注编号方便引用← 正确答案
  • C.放入不相关的内容扩充上下文
  • D.在提示词中指示基于片段作答← 正确答案

答案:ABD。片段长度要适当,过长会丢失重点(A);给片段编号让模型引用更清楚(B);D 让模型“只能根据资料回答”可抑制发挥。C 放入不相关内容会把模型带偏,是典型错误。

Q10. 关于 RAG 减少幻觉的原理,正确的是?

多选题 · extreme

  • A.生成过程绑定外部证据← 正确答案
  • B.检索结果也可能带来错误← 正确答案
  • C.已经能完全杜绝幻觉
  • D.拒答能降低硬答风险← 正确答案

答案:ABD。RAG 让模型从“背知识”变成“查资料后作答”,大幅减少记忆型幻觉(A);但检索结果本身可能过时或有误,从而带来新错误(B),所以不能完全杜绝幻觉(C 错误);无答案拒答策略能降低“硬答”带来的风险(D)。