小林是刚入行的律师助理,主任给他布置了一个任务:在一间堆满十万份判决书的老档案库里,找到与手头“网约车事故保险拒赔案”最相似的旧判例。档案库里恰好有三样工具:一套按关键词编目的卡牌目录、一排按“案情含义”排列的智能书架,还有一位经验丰富的带教合伙人做最后把关。小林很快发现,只用卡牌目录搜关键词,会漏掉那些“换了个说法”的相似案件;只凭书架的“感觉”又可能飘得没边。最靠谱的办法,是把三样工具串成一条流水线:先用目录快速筛出候选,再用智能书架补上漏网的语义近邻,最后请合伙人精挑细选排出座次。这套“三步找旧案”的方法,正是案例相似度匹配中 BM25 + Embedding + ReRank 的三阶段架构。

岗位关联

掌握“稀疏召回 + 向量补充 + 精排”的检索架构,是构建司法智能检索系统的核心技能,直接决定 AI 法律产品能否被法官、律师真正信任并规模化采用。

目录
  1. 可视化演示
  2. 检索挑战:为什么“找同款”这么难
  3. BM25 稀疏召回:卡牌目录先粗筛
  4. 向量语义补充:智能书架补漏网
  5. ReRank 重排序:合伙人精挑细选
  6. 流水线实现:三步走如何落地
  7. 评估与收益:怎么知道找得准
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示法律案例检索的BM25稀疏召回、Embedding语义补充与ReRank精排三阶段架构,理解词汇匹配与语义理解如何协同。支持三模式切换与播放暂停。

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

BM25召回 词频+逆文档频率 TF-IDF 高频词+稀有词区分度越高,召回候选。

检索挑战:为什么“找同款”这么难

小林第一次进档案库就傻了眼:十万份判决书,表面千奇百怪——有的写“拒赔”,有的写“不予理赔”,还有的绕了半天说“不属于保险责任范围”。其实都在讲同一件事,可字面上就是不一样。如果只按字面找“拒赔”,大量真正相似的旧案就被漏掉了。这就是案例检索面对的两大挑战:判例规模海量,加上词汇鸿沟与语义等价表达并存。

在计算机看来,一份判决书就是一串字符。早期检索靠“词面匹配”:文档里出现了查询里的词,就给高分;没出现,就判零分。可汉语表达千变万化,“解约”和“解除合同”字面不同、语义相同,词面匹配必然漏召回。更麻烦的是,法官写判决书还爱用套话和长句,同一规则在不同文书里被包装成完全不同的句式。

为了解决这个问题,检索系统通常把“找相似”拆成三个子任务:召回、补充、精排。召回追求“别漏”,用 BM25 这种稀疏检索快速捞回一批词面匹配的候选;补充追求“懂意思”,用向量嵌入把文本变成语义坐标,找出“说法不同但意思相近”的案子;精排追求“要准”,用重排序模型在候选里仔细比对,排出最终名次。

TopK=ReRank⁡(BM25(q,D)  ∪  Embed(q,D))\displaystyle \text{Top}_{K} = \operatorname{ReRank}\left( \mathrm{BM25}(q, D) \;\cup\; \mathrm{Embed}(q, D) \right)

1. Top_K:最终要输出的 K 个最相似判例,K 通常取 5 或 10,如 Top5。

TopK\displaystyle \text{Top}_{K}

2. BM25(q, D):用词项统计在整库 D 中召回与查询 q 词面匹配的候选判例。

BM25(q,D)\displaystyle \mathrm{BM25}(q, D)

3. Embed(q, D):用向量表示在语义空间中召回与查询意思相近的候选判例。

Embed(q,D)\displaystyle \mathrm{Embed}(q, D)

4. 并集运算:合并两路召回结果并去重,得到规模更大的候选池,避免各自漏网。

BM25(q,D)∪Embed(q,D)\displaystyle \mathrm{BM25}(q, D) \cup \mathrm{Embed}(q, D)

5. ReRank(·):重排模型对查询与候选逐对精细打分,输出最终排序结果。

ReRank⁡(⋅)\displaystyle \operatorname{ReRank}(\cdot)

就像小林“目录筛一遍、书架补一遍、合伙人把最后关”的三步走,这套总装图把“怕漏”和“怕乱”两个难题分别交给不同的环节化解。理解了总装图,后面的每一站都是在给这条流水线添砖加瓦。

BM25 稀疏召回:卡牌目录先粗筛

档案库门口的卡牌目录,每张卡上记着案件里的关键词和出现次数。小林翻“保险”“拒赔”“网约车”这些词,目录能精确告诉他哪些档案里出现过这些词、各出现多少次。这就是 BM25 的直觉:先按字面吻合度,把“大概率相关的”快速捞出来。

BM25 是一种“稀疏检索”方法,因为它靠的是词项在文档中的统计信号,而不是语义向量。它有三个关键部件:词频(TF)衡量词在文档里出现多少次;逆文档频率(IDF)衡量这个词在整个档案库里有多稀有;文档长度归一化则避免长文档“因为字多就占便宜”。

对查询里的每个词,BM25 综合这三者打分,最后把所有词的分数加起来。一个词在文档里出现越多、在库中越稀有、文档越精炼,这条“卡牌记录”就越值钱。现实中 BM25 借助倒排索引实现,能在毫秒级完成全库扫描,这是它作为第一道闸门的底气。

BM25(q,d)=∑t∈qln⁡(1+N−nt+0.5nt+0.5)×ft,d(k1+1)ft,d+k1(1−b+b∣d∣avgdl)\displaystyle \mathrm{BM25}(q,d)=\sum_{t\in q}\ln\left(1+\frac{N-n_t+0.5}{n_t+0.5}\right)\times\frac{f_{t,d}(k_1+1)}{f_{t,d}+k_1\left(1-b+b\frac{|d|}{\mathrm{avgdl}}\right)}

1. IDF(t):词越稀有区分度越高;“的”“与”这类常见词几乎不贡献分数。

ln⁡(1+N−nt+0.5nt+0.5)\displaystyle \ln\left(1+\frac{N-n_t+0.5}{n_t+0.5}\right)

2. f(t,d):词在文档中出现的次数,即词频 TF,出现越多潜在越相关。

ft,d\displaystyle f_{t,d}

3. 饱和加权:词频收益随次数增高而递减,k1 控制饱和速度,默认约 1.5。

ft,d(k1+1)ft,d+k1\displaystyle \frac{f_{t,d}(k_1+1)}{f_{t,d}+k_1}

4. 长度归一化:文档越长分母越大,防止“字多占便宜”的长文档分数虚高。

1−b+b∣d∣avgdl\displaystyle 1-b+b\frac{|d|}{\mathrm{avgdl}}

5. 求和符号:把查询里所有词的贡献相加,得到该文档的 BM25 总分。

∑t∈q\displaystyle \sum_{t\in q}

卡牌目录的缺点是“只认字、不认义”,可它的优点是快、稳、可解释。小林用它先筛掉八成无关档案,给后面的智能书架和合伙人省了大量力气。这正是 BM25 在整条流水线里的定位:第一道闸门,负责高召回率的粗筛。

向量语义补充:智能书架补漏网

档案库二层有一排“语义书架”,每份档案不是按标题硬编的,而是被安放在一个多维的“含义坐标”里:提到“网约车”的档案,自动靠向“共享出行”“交通事故”那些格子附近。小林把查询也标成一个点,书架按“距离最近”把档案推给他,哪怕里面一个关键词都没对上。

这个“含义坐标”就是嵌入向量(Embedding)。做法是用一个深度模型(如 Sentence-BERT)把句子映射成几百维的数值向量:“意思越像的句子,向量挨得越近。” 查询向量和文档向量的亲近程度,通常用余弦相似度衡量——夹角越小越像。

向量检索的意义在于语义补充:它专门解决 BM25 的词汇鸿沟。但纯向量也有短板——对专有名词、案号、精确数字不敏感,而且全库向量扫描的计算成本高。所以现实系统里把它放在第二道闸门:只负责“补”,不负责“全”。

cos⁡(vq,vd)=vq⋅vd∥vq∥ ∥vd∥\displaystyle \cos(\mathbf{v}_q,\mathbf{v}_d)=\frac{\mathbf{v}_q\cdot\mathbf{v}_d}{\|\mathbf{v}_q\|\,\|\mathbf{v}_d\|}

1. 向量点积:两向量逐维相乘再求和,衡量各语义维度上的“共鸣”总量。

∑ivq,i vd,i\displaystyle \sum_{i} v_{q,i}\,v_{d,i}

2. L2 模长:向量各分量的平方和再开根号,代表文本向量在语义空间中的长度。

∥v∥=∑ivi2\displaystyle \|\mathbf{v}\|=\sqrt{\sum_i v_i^2}

3. 余弦相似度:点积除以两向量模长之积,消除长度影响,只看方向是否一致。

cos⁡(vq,vd)=vq⋅vd∥vq∥ ∥vd∥\displaystyle \cos(\mathbf{v}_q,\mathbf{v}_d)=\frac{\mathbf{v}_q\cdot\mathbf{v}_d}{\|\mathbf{v}_q\|\,\|\mathbf{v}_d\|}

智能书架帮小林捞回了那批“说法不同、本质相同”的旧案,比如把“不赔”和“免责条款”自动联系到一起。它治的是漏召回,治不了“排得准不准”,所以下一道闸门,得请带教合伙人出马。

ReRank 重排序:合伙人精挑细选

小林把目录筛出的 15 份、书架补上的 5 份共 20 份档案摆到资深合伙人桌上。合伙人不看冷冰冰的关键词计数,而是把查询和档案“摊开在眼前对照着读”,反复推敲“这个案子到底像不像手头的案子”。读完全部候选,他一锤定音排出 Top5。

在技术上,这位“合伙人”就是重排序模型(ReRanker),最典型的是交叉编码器(Cross-Encoder):它把查询和候选判例拼接成一个输入,交给 Transformer 逐层交互建模,输出一个相关性分数。相比向量检索那种“先各自编码、再算余弦”的双塔模式,交叉编码器看得更细、算得更准,但速度慢,所以只能对前面两道闸门留下的少量候选精排。

工程上,重排分数可以和前面两路的分数做融合,也可以直接用重排分数重新排序。为了让各路分数能“公平对话”,通常先做归一化,再按权重相加。权重分配靠验证集调参,重排模型越可靠,γ 就可以设得越大。

Scorefinal=α S^BM25+β S^emb+γ Srerank\displaystyle \text{Score}_{\text{final}} = \alpha\,\hat{S}_{\text{BM25}} + \beta\,\hat{S}_{\text{emb}} + \gamma\,S_{\text{rerank}}

1. 归一化 BM25 分:对原始 BM25 分数做最大最小缩放,消除与余弦分的量纲差异。

S^BM25=SBM25−min⁡max⁡−min⁡\displaystyle \hat{S}_{\text{BM25}}=\frac{S_{\text{BM25}}-\min}{\max-\min}

2. 归一化向量分:余弦分数从 [-1,1] 线性映射到 [0,1],与重排分同量纲。

S^emb=Semb+12\displaystyle \hat{S}_{\text{emb}}=\frac{S_{\text{emb}}+1}{2}

3. 重排分数:交叉编码器输出的相关性分数,常经 sigmoid 压缩到 [0,1] 区间。

Srerank∈[0,1]\displaystyle S_{\text{rerank}}\in[0,1]

4. 加权求和:三个归一化分数按权重相加,权重在验证集上反复调优。

Scorefinal=αS^BM25+βS^emb+γSrerank\displaystyle \text{Score}_{\text{final}}=\alpha\hat{S}_{\text{BM25}}+\beta\hat{S}_{\text{emb}}+\gamma S_{\text{rerank}}

合伙人出手后,Top5 的座次才真正“可信”。这一步把前面“广撒网”的成果收敛成“精准命中”,是整套方法最见功力的一环,也是三阶段架构里“精度”的最终保障。

流水线实现:三步走如何落地

小林把方法固化成了流程:每天下班前,新到的判决书先被整理归档;收到新案时,先查目录、再翻书架、最后请合伙人。三步串成一条稳定的“装配线”,这就是检索系统的落地形态。

落地时要把“离线”和“在线”分开。离线阶段:为全部判决书构建 BM25 倒排索引,并用句子编码模型把每份文档的向量算好存进向量库。在线阶段:收到查询后,分别查倒排索引和向量库,合并候选,再调用重排模型打分输出。两阶段分离保证了查询时只需做轻量计算。

工程上的关键是“指数级裁剪”:BM25 召回几百个候选、向量补充几十个、重排只精审前 50 个,每一步都把计算量缩小一两个数量级,换来毫秒级响应。权重融合、阈值过滤、缓存策略等细节,决定系统在真实业务中是否稳定可用。

D全部→BM25C关键词→  ∪  向量  C合并→ReRankTopK\displaystyle D_{\text{全部}} \xrightarrow{\text{BM25}} C_{\text{关键词}} \xrightarrow{\;\cup\;\text{向量}\;} C_{\text{合并}} \xrightarrow{\text{ReRank}} \text{Top}_{K}

1. 全库 D:系统中全部已入库判决书,规模可达数十万份,是检索起点。

D全部\displaystyle D_{\text{全部}}

2. BM25 漏斗:借助倒排索引快速过滤,把全库缩到几百份词面相关候选。

→BM25C关键词\displaystyle \xrightarrow{\text{BM25}} C_{\text{关键词}}

3. 向量合并:与向量召回结果取并集,候选维持百级规模,不漏语义近邻。

→∪ 向量C合并\displaystyle \xrightarrow{\cup\ \text{向量}} C_{\text{合并}}

4. 重排出口:交叉编码器只对百级候选逐对精算,输出最终 TopK 结果。

→ReRankTopK\displaystyle \xrightarrow{\text{ReRank}} \text{Top}_{K}

档案库的“三步走”变成系统里一条清晰的漏斗流水线后,小林每天处理几十个新案也不慌——因为每一步只处理上一步的“精选”,量级小、速度快、结果稳。三阶段架构的工程价值,正在于把“精确”和“高效”这对矛盾拆开化解。

评估与收益:怎么知道找得准

主任问小林:“你这套流程到底比原来强多少?”小林翻出过去三个月人工标注的“标准答案”——每个查询到底有哪些旧案算真相似,然后用指标算给主任看。没有指标,再漂亮的架构也只是自我感觉良好。

检索系统常用两类指标:召回类指标看“该捞的捞上来没有”,精度类指标看“捞上来的准不准”。最常用的是 Recall@K(前 K 个结果里相关案例占全部相关案例的比例)和 MRR(第一个相关结果排在第几位的倒数)。前者要“全”,后者要“靠前”。

有了指标就能做迭代优化:发现召回率低,就调大两路召回的候选窗口或微调向量模型;发现前排不准,就加大重排权重或换更强的重排模型。在司法智能化落地中,检索质量的提升直接转化为律师找案例的时间节省与结论可靠度提升,形成可量化的业务价值。

Recall@K=∣Rrel∩RK∣∣Rrel∣\displaystyle \mathrm{Recall@}K = \frac{|R_{\text{rel}} \cap R_K|}{|R_{\text{rel}}|}

1. 标准答案集:人工标注出的与查询相关的全部案例集合,是评估的基准。

Rrel\displaystyle R_{\text{rel}}

2. 系统返回集:检索系统实际返回的前 K 个结果构成的有序集合。

RK\displaystyle R_K

3. 交集计数:返回结果中真正命中“相关集”的案例数量,分子越大越好。

∣Rrel∩RK∣\displaystyle |R_{\text{rel}}\cap R_K|

4. 召回比例:命中数除以相关总数,刻画系统“捞得全不全”的核心指标。

∣Rrel∩RK∣∣Rrel∣\displaystyle \frac{|R_{\text{rel}}\cap R_K|}{|R_{\text{rel}}|}

指标就是小林向主任交的“成绩单”。三阶段架构的价值最终都体现在这张成绩单上:召回来的数量更足、前排坐次更稳、法官和律师用起来更省心。从问题定义到工程落地再到量化评估,这条完整的链路才是案例相似度匹配的真正全貌。

AI 应用场景

智慧法院的“类案检索”系统

阶段:模型部署与在线应用

解决的问题:法官判案时需要对类似案件做到“同案同判”,传统做法靠人工翻阅卷宗、凭经验记忆,耗时且易遗漏,尤其面对跨地域、跨年份的海量判决书。三阶段检索架构将全量历史判决书建立索引,输入案情描述即可秒级返回最相似判例,大幅提升审判辅助效率。

具体案例:某地方法院接入类案检索平台后,法官输入“网约车保险拒赔纠纷”案情摘要,系统先经 BM25 召回关键词命中的 300 份判例,再用向量补充 80 份语义近邻,最终重排输出 Top10 判例并附相似度说明,办案检索时间从小时级降到秒级。

律所智能尽调与案例速递

阶段:数据标注与效果调优

解决的问题:律师接新案时需要在裁判文书中快速定位可引用的类案,人工检索对同义表述、地域差异、法官说理风格都不敏感。BM25 保证精确词命中、向量保证语义召回、重排保证引用价值排序,三者配合让“关键词搜不到”的案例也能浮出水面。

具体案例:某律所用同一架构搭建内部知识库,律师助理上传一段“开发商逾期交房违约”的事实描述,系统自动检索出包含“逾期交付”“违约金调整”等表述的历史判例,并按参考价值排序推送至团队协作群,供合伙人快速判断诉讼策略。

法学院校的判例教学平台

阶段:模型训练与评测

解决的问题:案例法教学中,学生需要“以一案带一串”地理解同类裁判规则,传统关键词检索难以串联语义相似的判例链。在 AI+教育数字化转型中,检索系统让学生输入案情描述即可看到语义相近的判例序列,辅助课堂讨论与论文写作。

具体案例:某高校法律实务课引入类案检索小程序,学生提交“外卖骑手工伤责任认定”的案情小作文,系统返回 5 个按相似度排序的典型判例,并显示命中依据,帮助学生在对比中归纳裁判口径。

Python 演示

python95 行
# -*- coding: utf-8 -*-import mathimport refrom collections import Counter CASES = [    "网约车司机发生交通事故被起诉,保险公司以车辆性质改变为由拒绝赔付",    "私家车改跑网约车后出险,保险公司主张危险程度显著增加而拒赔",    "出租车公司与司机承包合同解除,司机要求返还保证金及车辆押金",    "代驾司机在服务途中发生事故,平台与代驾公司责任如何划分",    "外卖骑手送餐途中受伤,平台是否应当承担工伤赔偿责任",    "外卖骑手与配送平台之间是否存在事实劳动关系",    "业主拖欠物业费,物业公司起诉要求支付欠款和违约金",    "物业公司以停水停电方式催缴物业费,业主起诉要求赔偿损失",    "开发商逾期交房,购房者主张解除合同并索赔违约金",    "开发商交付房屋面积与合同不符,购房者要求返还差价",] def tokenize(text):    """用字符二元组把中文切割成词项,兼顾同义表达"""    text = re.sub(r"[^\w]", "", text)    grams = []    for i in range(len(text) - 1):        grams.append(text[i:i + 2])    return grams def bm25_scores(query, docs, k1=1.5, b=0.75):    """对每条文档计算 BM25 召回分数"""    n = len(docs)    avg = sum(len(d) for d in docs) / n    doc_freq = Counter()    for doc in docs:        doc_freq.update(set(doc))    scores = []    for doc in docs:        tf = Counter(doc)        length = len(doc)        total = 0.0        for term in set(query):            if term not in doc_freq:                continue            idf = math.log(1 + (n - doc_freq[term] + 0.5) / (doc_freq[term] + 0.5))            freq = tf.get(term, 0)            norm = k1 * (1 - b + b * length / avg)            total += idf * freq * (k1 + 1) / (freq + norm)        scores.append(total)    return scores def embed(text, dim=64):    """把文本映射为向量:词项哈希到固定维度再归一化"""    vec = [0.0] * dim    for term in set(tokenize(text)):        h = 0        for ch in term:            h = h * 31 + ord(ch)        vec[h % dim] += 1.0    norm = math.sqrt(sum(v * v for v in vec))    if norm > 0:        vec = [v / norm for v in vec]    return vec def cosine(a, b):    """计算两个向量的余弦相似度"""    return sum(x * y for x, y in zip(a, b)) def rerank(query, candidates):    """重排序:候选内再用词项共现比例精细打分"""    q_set = set(tokenize(query))    refined = []    for score, idx, text in candidates:        overlap = len(q_set & set(tokenize(text))) / (len(q_set) + 1e-9)        refined.append((score * 0.7 + overlap * 0.3, idx, text))    refined.sort(reverse=True, key=lambda x: x[0])    return refined def search(query, cases, top_k=3, w1=0.4, w2=0.6):    """三阶段检索:BM25 召回,向量补充,重排精排"""    docs = [tokenize(c) for c in cases]    bm = bm25_scores(tokenize(query), docs)    qv = embed(query)    candidates = []    for i in range(len(cases)):        emb = cosine(qv, embed(cases[i]))        fused = w1 * bm[i] / (max(bm) + 1e-9) + w2 * emb        candidates.append((fused, i, cases[i]))    candidates.sort(reverse=True, key=lambda x: x[0])    base_top = candidates[:6]    final = rerank(query, base_top)    return final[:top_k] if __name__ == "__main__":    query = "私家车跑网约车发生事故后保险公司拒赔该不该赔"    print("查询:", query)    for rank, (score, idx, text) in enumerate(search(query, CASES), 1):        print("Top%d 案例#%d 相似度%.4f %s" % (rank, idx + 1, score, text))
目的:用纯 Python 实现一个迷你版的三阶段案例检索器,演示 BM25 召回、向量语义补充与重排精排如何协同工作。
  1. def bm25_scores(query, docs, k1=1.5, b=0.75):实现 BM25 打分,k1 控制词频饱和、b 控制长度惩罚强度
  2. doc_freq.update(set(doc))统计每个词项出现在多少篇文档中,为 IDF 做准备
  3. idf = math.log(...)计算逆文档频率,词越稀有贡献越大
  4. def embed(text, dim=64):用哈希技巧把文本映射成 64 维向量,模拟句子嵌入
  5. vec[h % dim] += 1.0词项哈希落到对应维度,相当于给语义坐标“投一票”
  6. norm = math.sqrt(...)计算向量模长,为余弦归一化做准备
  7. def cosine(a, b):对归一化后的向量做点积,即为余弦相似度
  8. def rerank(query, candidates):在候选内部用词项共现比例做二次精排,模拟交叉编码器
  9. fused = w1 * bm[i] / (max(bm) + 1e-9) + w2 * emb分数融合:先归一化 BM25 分再加权求和
  10. base_top = candidates[:6]只对前 6 个候选精排,体现“逐级裁剪”的工程思想

常见误区

误区都上 Embedding 了,还留着 BM25 干嘛?语义检索不是更聪明吗?直接全用向量检索不就行了。

正解向量检索对专有名词、案号、精确金额这类“字面要紧”的信号不敏感,比如“民再字第 123 号”这种判决书编号,在语义空间里根本区分不开。BM25 负责精确词命中、向量负责语义泛化,两者是互补关系;去掉 BM25,专名和稀有词的召回率会明显下滑。就像档案库的卡牌目录虽然笨,但找“编号完全一致”的文件时,比任何智能书架都快、都准。

为何常见:Embedding 的宣传点多强调“懂语义”,容易让人误以为它全面碾压关键词方法,忽略了词汇匹配在垂直领域(法律、医疗)的不可替代性。

误区系统说这个判例相似度 0.95,那我直接照搬它的判决思路就稳了。

正解相似度衡量的是“案情表述与裁判要旨的接近程度”,不等于“法律上必然可类推适用”。判决结果还受年份、地域、司法解释变化、证据细节影响。重排序环节的本质是“按业务相关性精排”,但业务上能否引用,仍需法律人把关。可以理解为“长得像的双胞胎,性格未必一样”,检索系统提供的是线索,不是结论。

为何常见:把“文本相似”与“法律效力”混为一谈,源于技术指标天然无法覆盖业务适用性,而产品宣传又常把相似度包装成“可靠度”。

误区重排序不就是给 BM25 分数和向量分数各乘一个权重加起来吗?我调调权重就等于做了重排。

正解真正的重排序通常用交叉编码器这类模型,把查询和候选拼接后做深度交互建模,输出的是“相关性概率”,而不是简单的加权和。加权融合(如 α、β、γ)只是工程上整合多路分数的手段,不能替代重排模型对“查询-文档”对的精细判断。好比合伙人得逐字对照才能定夺,而不是只看两页摘要的折中数。

为何常见:不少教程把“分数融合”和“重排序”画在同一张架构图里,初学者容易把流程上的求和误当成精排本身。

误区直接用开源的通用向量模型,把判例存进去就能上线了,不用再训练。

正解法律文本充满法言法语、长句嵌套和固定表达,通用模型在新闻、电商语料上学到的语义空间,往往分不清“追偿权”与“代位求偿权”的细微差别。需要用法条、判决书做领域微调,或至少用领域词典、术语增强,否则向量检索的语义补充效果会大打折扣。就像让只读过小说的实习生去法律档案库分类,常识够用、专业不足。

为何常见:开源模型的“零成本”错觉,让人低估了领域适配在垂直行业落地中的关键作用,导致上线后召回质量远低于预期。

面试题

Q1. BM25 在三阶段案例检索中承担的核心职责是?

单选题 · easy

  • A.用关键词快速召回候选判例← 正确答案
  • B.直接输出最终最相似判例
  • C.用向量计算语义相似分数
  • D.对候选集做神经网络精排

答案:A。BM25 是稀疏检索,靠词项统计信号快速筛出词面匹配的候选,为后续向量补充与重排缩小范围。直接输出最终结果的是重排阶段,用向量算语义分的是嵌入检索。这就像档案库的卡牌目录,只负责第一道粗筛。

Q2. BM25 公式中 IDF 越大,通常表示该词?

单选题 · easy

  • A.在越多文档中出现
  • B.区分文档的能力越强← 正确答案
  • C.在文档中出现次数越多
  • D.在查询中占据权重越小

答案:B。IDF 衡量词的稀有程度,越稀有说明区分度越高。常见词如“的”“与”在每篇都出现,IDF 趋近于 0,几乎不贡献分数。词频越高属于 TF 范畴,与 IDF 是两回事,二者共同构成 BM25 的统计基础。

Q3. 余弦相似度越接近 1,说明两个文本向量?

单选题 · mid

  • A.方向越一致,语义越相近← 正确答案
  • B.两个向量的长度完全相等
  • C.包含的字符完全一样
  • D.来源机构完全相同

答案:A。余弦相似度只看向量夹角、不看模长,因此对文本长短不敏感。方向一致表示各维度上的语义构成相似,是向量检索衡量“意思相近”的标准方式。长度相等、字符相同、来源相同都与余弦相似度的定义无关。

Q4. 向量检索对 BM25 最有价值的补充能力是?

单选题 · easy

  • A.匹配语义相近但用词不同的表述← 正确答案
  • B.完全替代关键词的精确匹配
  • C.保证输出具有法律效力
  • D.显著降低模型训练的成本

答案:A。向量检索的核心价值是跨越词汇鸿沟,找到“说法不同、意思相同”的表述,比如“不予理赔”与“拒绝赔付”。它无法替代精确词匹配,更不能保证法律效力,训练成本也不一定低,所以只是“补充”而非“替代”。

Q5. ReRank 阶段最典型的模型结构是?

单选题 · mid

  • A.交叉编码器拼接查询与文档← 正确答案
  • B.双塔编码器各自独立编码
  • C.词频统计加逆文档频率
  • D.随机森林打分类标签

答案:A。交叉编码器把查询与文档拼接成一句话输入 Transformer,逐层交互后打分,能捕捉词级互动细节,适合精排。双塔各自编码适合粗召回而非精排,BM25 属于召回阶段,随机森林也不是典型的重排方案。

Q6. 分数融合前对各路分数做归一化的原因是?

单选题 · mid

  • A.消除量纲差异避免某一路主导← 正确答案
  • B.让分数都变成整数方便计算
  • C.提高模型的训练收敛速度
  • D.降低候选集合的检索延迟

答案:A。BM25 分数受词频影响可达数十,余弦分数在 [-1,1],直接相加会让量纲大的那一路主导排名。先归一化再加权,各路人马才能“公平发言”。归一化不改变模型训练速度,也不直接影响检索延迟。

Q7. 双塔向量模型与交叉编码器精排模型的关键差别是?

单选题 · hard

  • A.是否让查询与文档进行逐层交互← 正确答案
  • B.是否需要对文本做分词预处理
  • C.是否依赖预训练语言模型参数
  • D.输出结果是否经过归一化处理

答案:A。双塔把查询、文档分别编码成向量再算相似度,交互浅、速度快,适合召回;交叉编码器拼接后逐层交互,能捕捉细粒度匹配信号但慢,适合精排。两者都需要分词、都可基于预训练模型,归一化也不是本质差异。

Q8. 实际部署三阶段检索时,控制整体延迟的关键策略是?

单选题 · extreme

  • A.逐级裁剪候选规模,只对少量候选精排← 正确答案
  • B.扩大每阶段候选数量直至覆盖全库
  • C.用 CPU 推理替代全部 GPU 加速
  • D.省略向量索引直接全库线性扫描

答案:A。三阶段架构的精髓是“漏斗”:BM25 和向量检索只需快速召回百级候选,重排模型即使慢也只处理几十条。若每阶段都追求全覆盖,重排成本将不可控,延迟必然爆炸。换 CPU 或全库扫描只会让瓶颈前移,而非解决问题。

Q9. BM25 打分时主要考虑的因素包括?

多选题 · hard

  • A.词项在文档中出现的频率← 正确答案
  • B.词项在整个文档集中的稀有程度← 正确答案
  • C.文档长度与平均长度的比值← 正确答案
  • D.法官在审理案件时的任职年限

答案:ABC。BM25 由词频 TF、逆文档频率 IDF 和长度归一化三部分组成,分别对应 A、B、C 三项。法官任职年限与检索打分毫无关系,属于干扰项——检索信号只来自文本本身的统计,不来自任何业务身份信息。

Q10. 案例检索系统常用的评估指标包括?

多选题 · mid

  • A.Recall@K 衡量相关案例的召回比例← 正确答案
  • B.MRR 衡量首个相关结果的排序位次← 正确答案
  • C.NDCG 衡量带位置折扣的排序质量← 正确答案
  • D.模型参数量衡量模型表达能力

答案:ABC。Recall@K、MRR、NDCG 都是检索排序的经典指标,分别关注召回完整性、首个命中位置、整体排序收益。参数量只是模型规模指标,衡量的是模型容量而非检索效果,不能直接度量系统找得准不准。