想象你走进一栋藏书百万的图书馆,向管理员抛出一个问题:“恐龙为什么会灭绝?”这位经验丰富的管理员不会把整座图书馆的书都搬到你面前,也不会靠记忆凭空编一个答案——他有三步绝活:第一步,在索引系统里快速圈定二十本可能相关的书;第二步,一本本翻看目录和章节,挑出最可能讲到恐龙灭绝的那一本;第三步,翻到具体页面,用红笔划出那句关键的话。这不是小说情节,而是“检索式问答系统”每天都在做的事。它的正式名字叫检索式QA:先从一个庞大的知识库里“召回”可能包含答案的段落,再“精排”出最相关的文档,最后“抽取”出答案片段。而让这一切成为可能的,是一种叫“向量检索”的技术——它把问题和文档都变成一串数字,让含义相近的东西在数字空间里彼此靠近。这篇文章将带你用图书管理员的视角,看懂检索式QA的完整运作流程。
掌握检索式QA的向量检索架构,你就能设计出可溯源、低幻觉的企业级问答系统——这是智能客服、搜索引擎、法律检索等场景中最抢手的NLP技能之一。
目录
可视化演示
交互展示问答向量空间相似度检索、双塔编码器架构及两阶段检索精排流程,插示向量检索QA的效率与精度平衡。
本节配有交互动效(进入视口后自动加载,约 3KB)。
向量空间相似度检索 Q向量与Doc向量余弦相似度 Top-k检索最相关文档 sim=cos(q,d) 高相似度文档更可能包含正确答案
整体流程:一次完美的借书答题
想象一下,你站在图书馆咨询台前,管理员的三步动作行云流水:先敲键盘筛出一摞书,再快速翻看每本书的目录,最后指着某一页念出答案。这套流程之所以高效,是因为他把“找书”和“读书”分开了——先用廉价的方式缩小范围,再用昂贵的方式精读少数几本。
这就是检索式QA的基本哲学。它不像生成式模型那样凭空“想”答案,而是从一个预先整理好的知识库中,经过“召回-精排-抽取”三段式流水线,找出并摘录最可信的答案。好处非常明显:答案有出处、可溯源、不易编造;知识库更新时,系统无需重新训练就能掌握新知识。开放域问答和知识库问答场景中,这种架构是绝对的主流。
从数学上看,整个系统可以理解为一个复合函数,前一个阶段的输出就是后一个阶段的输入:
回到图书馆的比喻:你把问题交给管理员,他先筛书、再读书、最后划线。检索式QA就像一个永不疲倦、速度极快的图书管理员,在数百万文档中重复着这套动作,几秒内就能给你一份带出处的答案。
- 召回模块 Recall(q)根据问题 q 从整个知识库中快速挑出 Top-K 个候选文档,对应图书管理员的“快速筛书”。
- 精排模块 Rerank(q, ·)对召回得到的少量候选逐一深度打分并重新排序,对应管理员的“逐本翻看目录”。
- 抽取模块 Extract(q, ·)从精排后的最优文档中截取一个连续片段作为最终答案,对应“用红笔划出关键句”。
- 嵌套关系三个模块层层递进,前一步的输出恰好是后一步的输入,构成严格的流水线。
向量编码:给文档办一张语义索书卡
图书馆里的每本书都有一张索书卡,上面的编号可不是按书名首字母随便排的,而是按“内容主题”排列——讲天文的书挨在一起,讲恐龙的书也挨在一起。管理员正是靠这套编号,才能一眼看出哪些书可能相关。向量编码做的,就是给每一段文本办一张“语义索书卡”。
计算机并不理解文字,它只理解数字。向量编码(Embedding)把一句自然语言转换成一串几百维的浮点数,例如 768 维。这串数字所在的“语义空间”中,含义相近的句子距离更近:“恐龙灭绝的原因”和“小行星撞击导致恐龙消失”虽然字面完全不同,但因为语义接近,它们的向量会互相靠近。深度编码器(如 BERT、SimCSE)通过海量文本预训练,学会了把语义折叠进向量的位置关系里。
有了语义索书卡,管理员才能在百万藏书中快速定位“可能与恐龙相关的书”。同样的,向量编码让计算机拥有了“看出语义相近”的能力,这是后续召回和精排的地基。
- 输入句子 s待编码的自然语言文本,比如一段运维文档或一条用户提问。
- 深度编码器 Encoder(·)以 Transformer 为核心的预训练语言模型,逐层融合上下文语义。
- 语义向量 e_s取编码器输出的 [CLS] 向量或对全部 token 做平均池化,得到整句话的向量表示。
- 高维空间 R^768向量落在 768 维实数空间中,维数越高,能区分的语义差异越细腻。
双塔召回:快速圈定一摞候选书
管理员进入第一步实操:他不会把每本书都抽出来读,而是先在索引数据库里快速比一圈索书卡,把几十本“看起来相关”的书一次性搬到桌上。这个过程追求的是“别漏掉”,而不是“绝对精确”——漏掉一本可能正确答案的书,后面再努力也白费。
双塔召回(Two-Tower Retrieval)就是这套快筛机制的工程实现。它有两个编码器塔:问题塔专门编码用户问题,文档塔专门编码知识库文档。两个塔结构相同、参数不共享,各自把文本映射成向量。文档塔的工作可以在离线状态下提前完成——把上百万文档一次性全部编码成向量,存进向量索引库(如 FAISS、HNSW)。线上来一个问题时,只需让问题塔算一次向量,然后在索引库中做近似最近邻(ANN)搜索,毫秒级返回 Top-K 个最相似的文档。
管理员用索书卡快速圈定候选书的动作,在系统里就是一次余弦相似度排序。双塔召回的关键设计是“文档向量离线算好、在线只算问题向量”,这保证了即使知识库上百万篇文档,也能在毫秒级完成粗筛。
- 问题向量 e_q问题塔把用户问题编码为向量。
- 文档向量 e_d文档塔把某篇文档编码为向量(离线预计算)。
- 点积运算 ·两个向量逐位相乘再相加,反映方向重合程度。
- 模长归一化除以两个向量的模长,消除文本长度带来的影响,得到夹角余弦值。
交叉精排:逐页翻书定胜负
粗筛搬来的二十本书里,有些可能只是在封面上印了一只恐龙,内容根本不相关。管理员现在要坐下来,一本一本翻看目录、插图和关键章节,仔细掂量每本书与问题的真正匹配程度,然后给它们重新排名。这就是交叉精排要解决的问题。
与双塔“各自编码、最后比一下”的方式不同,交叉编码器(Cross-Encoder)让问题和文档彻底“面对面”:把两者拼成一个输入序列 [CLS] 问题 [SEP] 文档 [SEP],喂给深度模型。这样问题中的每个词都能直接“看到”文档中的每个词,产生细粒度的语义交互。模型最后用 [CLS] 位置的输出向量过一个线性层,再用 Sigmoid 函数压成一个 0 到 1 之间的相关分数。精排准确率高,但代价是计算量巨大——每个候选文档都要单独跑一遍完整模型,所以它只能用在召回后的少量候选上。
管理员逐本翻书判高下的过程,在系统里就是交叉编码器对每个候选算出一个精细分数。双塔求“快”,交叉求“准”,两者配合,才能在百万文档中既找得快又找得准。
- 拼接输入问题和文档以特殊分隔符拼接成一句话。
- CLS 表征 h_CLSBERT 最后一层 [CLS] 位置的表征向量,浓缩了整对文本的交互信息。
- 线性层 w^T一维权重向量,把 768 维交互向量压缩成一个实数 logit。
- Sigmoid 压缩把 logit 映射到 (0,1),作为相关概率。
答案抽取:用红笔画出关键句
管理员终于锁定了那本最相关的书——不是把整本书复印给你,而是翻到具体那一页,用红笔划出关键的一句:“约 6600 万年前,一颗小行星撞击地球,导致了恐龙的灭绝。”检索式QA的最后一步,就是做这样一次精准的“划线”。
抽取式阅读理解(Extractive QA)把精排出的最佳文档输入模型,模型对文档中的每个词都预测两个概率:它是答案起始位置的概率,以及它是答案结束位置的概率。拥有最大“起始概率”的词和最大“结束概率”的词之间夹着的那一段连续文本,就是最终答案。经典实现如 BERT-SQuAD,就是通过添加两个额外的分类头来完成起止索引预测。
管理员的红笔,在模型里就是一组概率分布。抽取式QA的最大优点是“忠实”:它不会编造句子,只是从原文中剪出一段最合适的答案,让每一条回答都能回溯到知识库中的原始出处。
- 段落位置 i, j答案片段在文档中的起始词位置和结束词位置。
- 起始概率 P_start(i)第 i 个词作为答案起点的概率,由 softmax 归一化得到。
- 结束概率 P_end(j)第 j 个词作为答案终点的概率。
- 联合搜索 argmax在所有满足 i ≤ j 的起止组合中,寻找概率乘积最大的一对。
系统评估:给图书管理员发 KPI 绩效表
馆长如何考核管理员的工作?三张表:第一张看“找得全不全”——该搬上桌的书有没有漏;第二张看“排得准不准”——正确答案是否排在候选列表最前面;第三张看“快不快”——用户等了多久才拿到答案。检索式QA系统的评估,同样围绕这三张表展开。
Recall@K 衡量前 K 个召回结果中是否包含正确答案,反映召回阶段的“覆盖率”;MRR(平均倒数排名)则对每个问题,取第一个正确答案排名的倒数并求平均——排名越靠前,分数越接近 1;EM 和 F1 用于考核抽取出的答案片段与标准答案的字面匹配程度。除此之外,线上系统还必须关注时延和吞吐量:双塔召回虽然快,但召回数量 K 太大会拖慢精排;精排模型虽然准,但若响应超过 3 秒用户就会流失。性能指标与效果指标之间存在微妙的权衡。
馆长的三张 KPI 表,对应着检索式QA的评估铁三角:召回率保证“找得全”,MRR 保证“排得准”,时延保证“答得快”。任何一个环节掉链子,用户在真实场景中都感知得到。
- 问题集合 Q用于评估的测试问题集,共 |Q| 个问题。
- 排名 rank_r第 r 个问题的第一个正确答案在候选列表中的排序位置。
- 倒数 1/rank_r排名第 1 得 1 分,排名第 5 只得 0.2 分,惩罚排位靠后。
- 全体平均对所有问题的倒数排名取平均,衡量系统整体的排序质量。
AI 应用场景
企业智能客服:IT 服务台机器人
阶段:双塔召回 + 交叉精排 + 答案抽取(全流程)
解决的问题:传统客服 FAQ 依赖关键词规则匹配,员工说“VPN 连不上”和文档中“虚拟专用网络无法访问”字面完全不同,规则匹配直接失配。向量检索把问题和文档映射到同一语义空间,语义等价也能命中。
具体案例:某互联网公司把 3000 篇 IT 运维文档编码为向量库。员工提问“公司 Wi-Fi 老掉线怎么办”,系统先用双塔召回 Top50 文档,交叉精排后送入阅读理解模型,3 秒内返回“建议切换到 5G 频段并更新网卡驱动”的答案,且附带原文链接。答案命中率从关键词方案的 61% 提升到 87%。
法律判例检索助手
阶段:双塔召回 + 交叉精排
解决的问题:法律法规和裁判文书中专有名词极多,同一个含义有“违约金过高”“约定利息超出法定上限”等无数种表达方式,关键词检索严重漏检。双塔召回可在语义层面圈定同类判例。
具体案例:某法律科技公司的“智能法规问答”系统,接入数万部法律法规和裁判文书。律师提问“借款纠纷中约定利息过高是否有效”,系统先通过向量检索召回相关法条和类似判例,再精排引用原文,并在答案中标注案号,方便人工核验。
搜索引擎的“答案盒子”
阶段:双塔召回 + 精排 + 抽取
解决的问题:传统搜索引擎返回十条蓝色链接,用户还得一个个点开找答案。搜索引擎在结果页顶部直接展示一句话答案,本质就是一个检索式QA系统。
具体案例:搜索“珠穆朗玛峰有多高”,结果顶部直接显示“8848.86 米”。系统先从网页库中召回相关页面,精排选出最权威来源,再用抽取模型切出包含数值的关键句子,最后附上来源链接。
Python 演示
# -*- coding: utf-8 -*-# 迷你检索式问答:字符二元组向量化 + 召回 + 精排 + 抽取import math DOCS = [ "恐龙的灭绝与小行星撞击地球有关。", "地球自转一周大约需要24小时。", "光合作用是植物将阳光转化为能量的过程。", "牛顿提出了万有引力定律。", "蛋白质是构成生命的基础分子。", "白垩纪末期的火山活动可能加剧了恐龙的灭绝。"] def tokenize(text): # 字符二元组:把句子切成一连串二字片段作为特征 text = text.replace(" ", "") return [text[i:i+2] for i in range(len(text) - 1)] def encode(text): # 把文本编码成二元组频次向量 vec = {} for t in tokenize(text): vec[t] = vec.get(t, 0) + 1 return vec def cosine_sim(v1, v2): # 余弦相似度:衡量两个向量方向是否一致 s = sum(v1.get(t, 0) * v2.get(t, 0) for t in set(v1) | set(v2)) l1 = math.sqrt(sum(x * x for x in v1.values())) l2 = math.sqrt(sum(x * x for x in v2.values())) return s / (l1 * l2) if l1 * l2 else 0.0 def recall(query, k=2): # 双塔召回:问题向量与全部文档向量做粗筛 qv = encode(query) cands = [] for doc in DOCS: cands.append((cosine_sim(qv, encode(doc)), doc)) cands.sort(key=lambda x: x[0], reverse=True) return cands[:k] def rerank(query, cands): # 交叉精排:在候选上叠加特征重新打分 qset = set(tokenize(query)) def score(item): sim, doc = item overlap = len(qset & set(tokenize(doc))) / max(len(qset), 1) return 0.7 * sim + 0.3 * overlap cands.sort(key=score, reverse=True) return cands def extract_answer(query, doc): # 答案抽取:教学简化版返回整句,真实系统用起止索引模型 return doc def main(): question = "恐龙为什么灭绝?" print("问题:", question) hits = recall(question, k=3) print("双塔召回结果:") for s, d in hits: print(" %.3f %s" % (s, d)) hits = rerank(question, hits) print("交叉精排结果:") for s, d in hits: print(" %.3f %s" % (s, d)) print("抽取答案:", extract_answer(question, hits[0][1])) if __name__ == "__main__": main()
def tokenize(text)— 把中文句子切成相邻二字的片段,作为最朴素的“语义特征”,相当于给文档办简易索书卡。def encode(text)— 统计二元组出现次数,得到文本的稀疏向量,代表真实系统中 Embedding 向量的简化版。def cosine_sim(v1, v2)— 计算两个向量的余弦相似度,对应双塔召回中的相似度打分。hits = recall(question, k=3)— 问题向量与全部文档向量逐一比较,取出 Top-3 候选,模拟粗筛阶段。def rerank(query, cands)— 在候选集上叠加“语义重叠度”特征重新排序,模拟交叉精排的打分重排。extract_answer(question, hits[0][1])— 从精排第一的文档中返回答案片段,对应答案抽取阶段。
常见误区
误区“我一直以为向量检索就是把文章里的关键词提取出来,数一数问题里出现哪个词多就返回哪篇,词频越高就越相似。”
正解向量检索的核心是语义编码,关键词重合只是最初的粗浅信号。深度编码器能把“恐龙灭绝”和“小行星撞击导致恐龙消失”这种字面几乎不重合、但语义高度相关的文本映射到相近位置。TF-IDF 做的是字面匹配,向量检索做的是语义匹配。
为何常见:早年搜索引擎的关键词匹配深入人心,而“语义向量”看不见摸不着,初学者很容易用旧经验类比。
误区“交叉编码器精排效果明显比双塔好,那为什么还要双塔召回?直接拿交叉编码器对全部文档打分不就行了。”
正解交叉编码器需要对“每个问题 × 每篇文档”做一次完整深度推理。知识库若有 100 万篇文档,一次问答就要跑 100 万次模型,时延无法接受。双塔先把文档向量离线算好,线上只需算一次问题向量加近邻搜索,毫秒级粗筛出几百个候选,再交给交叉编码器精排。这是效率和精度的权衡。
为何常见:只听说精排准确率高,忽略了计算成本,没有正视知识库的规模效应。
误区“答案抽取应该像 ChatGPT 一样由模型凭空想出一段通顺的话回答用户。”
正解抽取式 QA 是从给定段落中“选”出一个连续的片段,绝不生成新词。模型通过预测起始位置和结束位置概率来截取原文。这样做的好处是答案忠实、有据可查、不会编造事实。生成式大模型是另一条技术路线,两者不能混为一谈。
为何常见:生成式大模型的流行让大家忘了抽取式路线的存在,混淆了“检索式”与“生成式”的本质区别。
误区“向量相似度排名第一的文档就一定会包含答案,直接抽出来给用户就行。”
正解相似度高只代表“话题接近”,不代表“段落里一定藏着答案”。编码器未在垂直领域微调、文档本身信息不完整、问句存在歧义,都可能导致高相似度文档没有答案。因此真实系统需要精排模块进一步过滤,并配置“无答案拒答”机制,而不是盲目信任相似度排名。
为何常见:把“语义相似”直接等同于“包含答案”,对语义空间的非线性映射缺乏理解。
误区“只要把 Recall@K 和 MRR 刷上去,系统上线后用户就一定满意,所以唯一任务就是调相似度阈值。”
正解离线指标与线上体感相关但不等价。MRR 高不代表时延达标,Recall@K 高不代表无答案时能优雅拒答。线上系统还要看端到端时延、吞吐量、无答案处理、用户反馈等因素。为了将时延控制在 300 毫秒内,有时反而要调小召回 K 值,这是系统工程中的取舍艺术。
为何常见:实验室评估习惯单指标导向,忽略了真实系统中多个目标互相制约的现实。
面试题
Q1. 检索式问答系统最突出的优点是?
- A.能凭空创造全新见解
- B.答案可溯源且少编造← 正确答案
- C.完全不需要任何数据库
- D.无需预训练数据参与
答案:B。检索式QA从知识库中检索并抽取答案片段,答案有出处、可回溯,几乎不会凭空编造。A是生成式模型的特点;C和D描述都不正确,检索式系统恰恰依赖知识库和预训练编码器。
Q2. 检索式QA中的“向量”指的是?
- A.数据库中的一列数值
- B.文本映射成的稠密数组← 正确答案
- C.内存地址的指针
- D.关键词的匹配次数
答案:B。向量是文本经过深度编码器后得到的一串浮点数(如768维),用来表示语义。A是数据库概念,C是程序概念,D是词频统计,都不是语义向量的定义。
Q3. 双塔召回阶段的主要目的是?
- A.逐字细读每篇文档
- B.快速筛出候选文档← 正确答案
- C.生成最终的答案文本
- D.给文档做人工标注
答案:B。双塔召回的任务是以极低延迟从海量文档中粗筛出少量候选,强调“快”和“别漏”。逐字细读是精排阶段的事,生成答案文本是抽取/生成阶段的事。
Q4. 余弦相似度的取值范围是?
- A.[0, 1] 恒为正
- B.[-1, 1] 可为负← 正确答案
- C.[0, 10] 通常为正
- D.[1, 100] 越大越像
答案:B。两个向量夹角的余弦值在 [-1, 1] 之间,为 1 时方向完全一致,为 -1 时方向完全相反。传统词频向量全非负时余弦值才非负,但深度语义向量可能含负分量,因此可为负。
Q5. 双塔架构中,文档向量的计算时机通常是?
- A.每次线上问答实时计算
- B.离线预计算并建立索引← 正确答案
- C.由交叉编码器在线推理
- D.由人工手动构造词表
答案:B。文档向量数量巨大但更新不频繁,适合离线批量编码后存入向量索引库。线上每次问答只实时计算一个问题向量,再做近邻搜索。A会浪费大量算力,C属于精排阶段,D不是向量化方法。
Q6. 交叉精排相比双塔召回的主要劣势是?
- A.无法处理长文本输入
- B.计算开销大、响应慢← 正确答案
- C.召回候选数量太少
- D.不支持相似度量计算
答案:B。交叉编码器让问题和文档的每个词充分交互,效果好但每个候选都要单独跑一次完整模型,计算量远大于双塔的一次近邻搜索。因此它只能用在召回后的少量候选上。
Q7. 抽取式QA模型预测答案采用的方法是?
- A.自回归逐字生成新句
- B.预测起止位置索引← 正确答案
- C.抽取全篇摘要内容
- D.用规则匹配正则式
答案:B。抽取式阅读理解对段落中每个词预测“作为答案起点”和“作为答案终点”的概率,取概率最大的一对起止位置之间的连续片段作为答案。A是生成式方法,C是摘要任务,D是传统规则方法。
Q8. MRR=0.5 的含义是?
- A.全部答案均排在首位
- B.正确答案平均倒数排名0.5← 正确答案
- C.系统精确率达到半数
- D.只要一半问题问得对
答案:B。MRR 是对每个问题取第一个正确答案排名的倒数再求平均。MRR=0.5 表示各问题正确答案的倒数排名平均为 0.5,例如两个问题的首个正确答案分别排第 1 位和无限靠后时,平均值趋近 0.5。
Q9. 检索式双塔召回阶段可以使用的检索手段有哪些?
- A.向量内积相似度← 正确答案
- B.余弦相似度打分← 正确答案
- C.ANN近似最近邻索引← 正确答案
- D.SQL关系型存储
答案:ABC。双塔召回把文档向量化后,用内积或余弦相似度衡量相关性,并借助 FAISS/HNSW 等 ANN 索引加速近邻搜索。SQL 存储适合精确查询结构化数据,不适合海量高维向量的近似检索。
Q10. 检索式QA系统评估常用的指标包括哪些?
- A.Recall@K 召回率← 正确答案
- B.MRR 平均倒数排名← 正确答案
- C.EM/F1 答案字面匹配← 正确答案
- D.BLEU 生成文本得分
答案:ABC。Recall@K 衡量召回覆盖率,MRR 衡量答案排序质量,EM/F1 衡量抽取答案与标准答案的字面重合度。BLEU 是机器翻译等生成式任务的指标,不适合评估检索式QA的答案抽取结果。