金融合规问答助手-RAG查询路由落地

金融合规问答助手-RAG查询路由落地

峰哥AI手记 · 金融合规 RAG 实战(第 8 篇)

上篇讲了"查询路由"的设计:W6 的 A/B 证明改写是按查询类型增益------口语化单文档题 hit@4 从 90% 拉到 100%,但跨文档综合题反而从 100% 掉到 80%。所以"是否改写"不该是全局开关,而该在检索前先判断这条查询"像不像需要改写的口语题"。

这篇记录实际落地:一个朴素路由规则 + 一份领域术语表 + 三路 A/B 评估,把上一篇里"理想路由 100%"的推演换成真实数字。


一、路由到底路由什么

路由不做检索,只在检索前决定"这条查询走哪条链路":

css 复制代码
用户问题
   │
   ├─[route() 路由分类器]── 判断查询特征 ──┐
   │                                      │
   ▼                                      ▼
[hybrid]                            [hybrid + rewrite]
 (精确/专业术语,不改写)            (口语化,先 LLM 改写再检索)
   │                                      │
   └──────────────┬───────────────────────┘
                  ▼
            重排 + 生成(永远看用户原文)

关键约束(沿用前面几周立下的纪律):

  1. 路由只决定链路,不改写问题本身------和改写那条原则一致,答题 LLM 永远看用户原文,改写只发生在检索侧。
  2. 路由必须可降级 ------分类器任何异常,默认走 hybrid(W6 基线,最稳),不让路由成为可用性瓶颈。
  3. 路由要可量化 ------和前面几周一样,路由规则改动必须能跑 evaluate_w8.py 出数字对比。

这一周复用的是 W6 的完整检索链路(向量 + BM25 + RRF + CrossEncoder 重排 + 查询改写),只在前头加了一个 route()。独立的 rag_w8.py,向量库 chroma_db_w8/,不和前面的周互相污染。


二、朴素路由的三条信号

用三个"零成本"信号,按优先级短路判断(命中即返回,不继续算):

信号 1:聚合/对比词 → 保广度,走 hybrid 跨文档综合题往往含"全流程""分别""对比""结合""综合"等聚合词。这类题改写容易把召回面压窄。直接命中 AGG_WORDS 集合就走 hybrid。

信号 2:术语密度 → 用户已用文档语言,走 hybrid 维护一张 data/glossary.txt(从三份 PDF 抽的 43 条高频专业词:个人金融信息、委托处理、适当性、穿透式登记......)。统计问题里命中术语的字符数占比:

python 复制代码
def term_density(query, terms):
    hit = sum(len(t) for t in terms if t in query)
    return hit / max(1, len(query))   # ∈ [0,1]

密度 ≥ 0.30 就认为"用户已经在用监管术语提问",改写要么画蛇添足、要么引入歧义,走 hybrid。

信号 3:否则口语化 → 走 hybrid+rewrite 前两条都不命中,说明是"外包公司处理客户资料"这种口语题,交给 W6 的改写链路提成文档术语。

python 复制代码
def route(query):
    if any(w in query for w in AGG_WORDS):
        return "hybrid"
    if term_density(query, DOMAIN_TERMS) >= 0.30:
        return "hybrid"
    return "hybrid+rewrite"

阈值 0.30 是拍的第一个数,A/B 跑完看效果再调(详见第五节)。


三、代码怎么接

rag_w8.py 里路由只新增了 route() 和 ask(question, auto_route=True),其余全复用 W6:

python 复制代码
def ask(question, mode="hybrid", rewrite=None, auto_route=False):
    decision = None
    if auto_route:
        decision = route(question)
        rewrite = (decision == "hybrid+rewrite")
    elif rewrite is None:
        rewrite = False
    # 之后和 W6 完全一致:改写(可选) → 混合检索 → 重排 → 生成
    ...
    meta = {"mode": mode, "rewrite": rewrite,
            "rewritten_query": ..., "route": decision}
    return answer, retrieved, meta

meta["route"] 把路由决策带出来,方便回归时看"这道题分到了哪条链路、分对没"。

evaluate_w8.py 跑三路:hybrid(固定不改写)、hybrid+rewrite(固定全改写)、route(按 route() 决策)。三路共用同一份 eval_set_v2.json(30 题 = 20 正例 + 10 负例),唯一变量就是"改不改写"。


四、三路 A/B 结果(真实数字)

evaluate_w8.py --retrieval-only 跑完,三路对比(正例 20 题,k=2/4/8):

模式 hit@2 hit@4 hit@8 MRR
hybrid(基线,不改写) 90.00% 95.00% 95.00% 0.912
+rewrite(全改写) 100% 100% 100% 0.975
route(W8 路由) 100% 100% 100% 1.000

按难度拆 hit@4:

难度 hybrid +rewrite route
keyword(5 题) 100% 100% 100%
paraphrase(10 题) 90% 100% 100%
cross(5 题) 100% 100% 100%

核心看点:路由把 W6 的 trade-off 完全拉平了------paraphrase 档守住 +rewrite 的 100%,cross 档也稳在 100%,整体 hit@4 从基线 95% 提到 100%,MRR 从 0.912 提到 1.000。

一个要说明的细节:本次重跑里 +rewrite 路的 cross 档也是 100%,和文章 7 引用的 W6 那次 80% 不一致。原因在第五节------改写本身是非确定的,跨文档题用改写有波动风险;路由的价值恰恰是用稳定规则把这类风险消掉,而不是赌某次改写的运气。


五、bad case:路由分对了吗

先看结论:路由零误判,20/20 全部 rank=1,MRR=1.000。

逐题看 route 的决策,分流是干净的:

  • keyword 题:id1/id5(密度高)走 hybrid,id2/3/4(密度低)走 rewrite------都 rank=1;
  • paraphrase 题(id6--12、21--23):全部走 rewrite------都 rank=1;
  • cross 题:id13/id17 走 hybrid,id14/15/16 走 rewrite------都 rank=1。

路由最关键的防守在 id13 :「银行在产品销售的售前、售中、售后全流程应如何向消费者披露信息?」含聚合词"全流程",被路由锁死在 hybrid,rank=1。而强制 +rewrite 路里,这道题这次只排到 rank=2(W6 那次评估甚至直接 miss,hit@4=0)。也就是说,改写对跨文档题是非确定的------有时排第 2,有时干脆 miss;路由用"聚合词"这个稳定信号把它拦在 hybrid 外,波动风险直接归零。这正是对文章 7 那个 −20pp 的实打实防御。

残留的结构性隐患:id14/15/16 也是跨文档题,但既没有聚合词、术语密度也低于 0.30,被路由分到了 rewrite。本次它们改写后恰好命中(rank=1),可一旦 DeepSeek 改写这批题时像 id13 那样波动,就会重现 W6 的 −20pp。规则对"无聚合词的跨文档题"还缺一个专属信号------这正是文章 7 第六节预言的"路由分错比无脑改写更糟"的真实缺口,也是 W9 语义路由要补的刀。

route 相对 +rewrite 的边际收益就在 id13:MRR 0.975 → 1.000,差距就是这道题从 rank=2 被提回 rank=1。路由不是让更多题"命中",而是让已命中的题"排到第一"。


六、路由的成本与边界

  • 延迟:规则版路由近乎零成本(几次字符串包含判断 + 一次密度统计),相比一次 CrossEncoder 重排可以忽略。
  • 误判代价 :路由分错比"无脑改写"更糟------把跨文档题误分到 rewrite 会直接重现 W6 的 −20pp。所以规则从"保守"起步:宁可少改写,不要误改写跨文档题。
  • 术语表维护 :glossary.txt 要随语料更新。下一周可以写个脚本从 data/*.pdf 自动抽高频专业词,减少手工。
  • 可观测 :每次路由决策记进 meta,回归时能直接定位误判题,而不是只看总分。

量化是这条系列的命根子。量化体检、查询改写、查询路由,串起来就是一条"用数字驱动每一刀优化"的主线。下一篇见。


若这篇文章对您有一点点帮助或启发,欢迎点赞 / 评论 / 收藏,与君共进步。

相关推荐
benchmark_cc2 小时前
量化项目启动时,应该先设计数据接口还是先编写策略?从系统架构角度做出正确选择
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
CaptainT2 小时前
第 4 卷:面向对象、数据建模与类型系统
python
2601_962885722 小时前
如何用 Python 判断一只股票“追涨”还是“抄底”更有效?
android·开发语言·python
用户7783366132112 小时前
选 SERP API 别靠感觉:用 SerpBase 实测的评估脚本与判定标准
python·api
用户019027581612 小时前
如何用 Python 计算 BRAR 情绪指标(AR/BR)?
python
荣码2 小时前
LangSmith可观测性:AI应用出bug根本不知道哪里出了问题,我踩了4个坑
java·python
在世修行2 小时前
智学文枢:AI 产出的持久化之道
python
CaptainT3 小时前
第 1 卷:执行模型与基础语法
python
buddyMe智能助手3 小时前
长任务拆给谁?把专门化智能体存成可复用、会增长的「选项」
人工智能·深度学习·安全·机器学习·aigc