1. 背景与痛点
每个系统对应一个独立知识库,随着系统数量持续增长,知识库规模达到数十乃至上百个。每个知识库均有其独立的名称(Name)与范围描述(Description),文档按角色类型分类(desc / overview / architecture / api / business / product / ops / runtime),分类信息作为元数据写入向量库。
知识库数量庞大,无法将所有描述一次性塞入 LLM Prompt 暴力裁决(Token 成本、延迟、上下文窗口均不允许)。在初期尝试中,使用向量检索匹配知识库 Description 的方案召回准确率同样远低于预期。
核心痛点:
| 痛点 | 表现 | 根因 |
|---|---|---|
| 知识库数量多 | 数十到上百个知识库,全量裁决不可行 | 需要检索先收敛候选,再精准裁决 |
| 非对称检索 | 用户问"Redis 满容怎么办",检索不到描述为"提供 Redis 集群运维相关知识"的知识库 | 用户提问短、具体、多样;Description 抽象、概括、结构化,向量空间语义特征不匹配 |
| 硬字面量缺失 | 搜"Hubble 告警配置"召回了"监控系统运维"知识库而非"Hubble 使用手册"知识库 | 向量检索容易忽略业务专有名词,泛化过度 |
| 同义改述盲区 | 用户说"机器挂了",知识库描述是"主机故障处理",向量距离较远 | 单次检索只能覆盖一种表达角度,难以跨越口语/书面语鸿沟 |
2. 方案核心思路
知识库的文档内容已经完成向量化入库,每个文档分块(chunk)均携带 kb_id 元数据。因此不需要额外构建独立的路由索引 ------路由可以直接在已有的向量化内容上检索,按 kb_id 聚合结果即可判定目标知识库。
核心改进点在在线端:
- 多角度 Query 改写:把用户的单一提问改写为 1~3 条不同视角的查询,扩大召回覆盖面,解决"同义改述盲区"
- Hybrid 检索:Dense(语义)+ Sparse(BM25)混合检索,解决"硬字面量缺失"
- kb_id 聚合 + Rerank:将 chunk 级别的检索结果聚合到知识库级别,Rerank 精排
- LLM 终审裁判:多个高置信候选时,LLM 做最终 N 选 1
前提条件:知识库文档需要人工持续完善(尤其是 FAQ 条目),文档质量直接决定路由准确率。AI 不生成合成数据,所有检索目标均来源于人工维护的真实文档内容。
3. 整体架构
3.1 已有基础设施
#mermaid-svg-vdF2VPI1EOc8YV56{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vdF2VPI1EOc8YV56 .error-icon{fill:#552222;}#mermaid-svg-vdF2VPI1EOc8YV56 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vdF2VPI1EOc8YV56 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vdF2VPI1EOc8YV56 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vdF2VPI1EOc8YV56 .marker.cross{stroke:#333333;}#mermaid-svg-vdF2VPI1EOc8YV56 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vdF2VPI1EOc8YV56 p{margin:0;}#mermaid-svg-vdF2VPI1EOc8YV56 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vdF2VPI1EOc8YV56 .cluster-label text{fill:#333;}#mermaid-svg-vdF2VPI1EOc8YV56 .cluster-label span{color:#333;}#mermaid-svg-vdF2VPI1EOc8YV56 .cluster-label span p{background-color:transparent;}#mermaid-svg-vdF2VPI1EOc8YV56 .label text,#mermaid-svg-vdF2VPI1EOc8YV56 span{fill:#333;color:#333;}#mermaid-svg-vdF2VPI1EOc8YV56 .node rect,#mermaid-svg-vdF2VPI1EOc8YV56 .node circle,#mermaid-svg-vdF2VPI1EOc8YV56 .node ellipse,#mermaid-svg-vdF2VPI1EOc8YV56 .node polygon,#mermaid-svg-vdF2VPI1EOc8YV56 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vdF2VPI1EOc8YV56 .rough-node .label text,#mermaid-svg-vdF2VPI1EOc8YV56 .node .label text,#mermaid-svg-vdF2VPI1EOc8YV56 .image-shape .label,#mermaid-svg-vdF2VPI1EOc8YV56 .icon-shape .label{text-anchor:middle;}#mermaid-svg-vdF2VPI1EOc8YV56 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vdF2VPI1EOc8YV56 .rough-node .label,#mermaid-svg-vdF2VPI1EOc8YV56 .node .label,#mermaid-svg-vdF2VPI1EOc8YV56 .image-shape .label,#mermaid-svg-vdF2VPI1EOc8YV56 .icon-shape .label{text-align:center;}#mermaid-svg-vdF2VPI1EOc8YV56 .node.clickable{cursor:pointer;}#mermaid-svg-vdF2VPI1EOc8YV56 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vdF2VPI1EOc8YV56 .arrowheadPath{fill:#333333;}#mermaid-svg-vdF2VPI1EOc8YV56 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vdF2VPI1EOc8YV56 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vdF2VPI1EOc8YV56 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vdF2VPI1EOc8YV56 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vdF2VPI1EOc8YV56 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vdF2VPI1EOc8YV56 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vdF2VPI1EOc8YV56 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vdF2VPI1EOc8YV56 .cluster text{fill:#333;}#mermaid-svg-vdF2VPI1EOc8YV56 .cluster span{color:#333;}#mermaid-svg-vdF2VPI1EOc8YV56 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vdF2VPI1EOc8YV56 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vdF2VPI1EOc8YV56 rect.text{fill:none;stroke-width:0;}#mermaid-svg-vdF2VPI1EOc8YV56 .icon-shape,#mermaid-svg-vdF2VPI1EOc8YV56 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vdF2VPI1EOc8YV56 .icon-shape p,#mermaid-svg-vdF2VPI1EOc8YV56 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vdF2VPI1EOc8YV56 .icon-shape .label rect,#mermaid-svg-vdF2VPI1EOc8YV56 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vdF2VPI1EOc8YV56 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vdF2VPI1EOc8YV56 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vdF2VPI1EOc8YV56 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-vdF2VPI1EOc8YV56 .kb>*{fill:#f9f!important;stroke:#333!important;stroke-width:2px!important;}#mermaid-svg-vdF2VPI1EOc8YV56 .kb span{fill:#f9f!important;stroke:#333!important;stroke-width:2px!important;}#mermaid-svg-vdF2VPI1EOc8YV56 .db>*{fill:#fbc!important;stroke:#333!important;stroke-width:2px!important;}#mermaid-svg-vdF2VPI1EOc8YV56 .db span{fill:#fbc!important;stroke:#333!important;stroke-width:2px!important;} 知识库 C
知识库 B
知识库 A
分块 + 向量化
分块 + 向量化
分块 + 向量化
分块 + 向量化
分块 + 向量化
分块 + 向量化
分块 + 向量化
文档 1
FAQ / SOP / 架构
文档 2
文档 N
文档 1
文档 N
文档 1
文档 N
Hybrid 向量库
chunk + kb_id 元数据
所有知识库的文档已完成分块、向量化,写入统一的 Hybrid 向量库。每个 chunk 携带 kb_id、layer、doc_type 等元数据,是路由的检索基础。
3.2 在线路由管线
#mermaid-svg-fR3inln1fVzunYjH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fR3inln1fVzunYjH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fR3inln1fVzunYjH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fR3inln1fVzunYjH .error-icon{fill:#552222;}#mermaid-svg-fR3inln1fVzunYjH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fR3inln1fVzunYjH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fR3inln1fVzunYjH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fR3inln1fVzunYjH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fR3inln1fVzunYjH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fR3inln1fVzunYjH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fR3inln1fVzunYjH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fR3inln1fVzunYjH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fR3inln1fVzunYjH .marker.cross{stroke:#333333;}#mermaid-svg-fR3inln1fVzunYjH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fR3inln1fVzunYjH p{margin:0;}#mermaid-svg-fR3inln1fVzunYjH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fR3inln1fVzunYjH .cluster-label text{fill:#333;}#mermaid-svg-fR3inln1fVzunYjH .cluster-label span{color:#333;}#mermaid-svg-fR3inln1fVzunYjH .cluster-label span p{background-color:transparent;}#mermaid-svg-fR3inln1fVzunYjH .label text,#mermaid-svg-fR3inln1fVzunYjH span{fill:#333;color:#333;}#mermaid-svg-fR3inln1fVzunYjH .node rect,#mermaid-svg-fR3inln1fVzunYjH .node circle,#mermaid-svg-fR3inln1fVzunYjH .node ellipse,#mermaid-svg-fR3inln1fVzunYjH .node polygon,#mermaid-svg-fR3inln1fVzunYjH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fR3inln1fVzunYjH .rough-node .label text,#mermaid-svg-fR3inln1fVzunYjH .node .label text,#mermaid-svg-fR3inln1fVzunYjH .image-shape .label,#mermaid-svg-fR3inln1fVzunYjH .icon-shape .label{text-anchor:middle;}#mermaid-svg-fR3inln1fVzunYjH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fR3inln1fVzunYjH .rough-node .label,#mermaid-svg-fR3inln1fVzunYjH .node .label,#mermaid-svg-fR3inln1fVzunYjH .image-shape .label,#mermaid-svg-fR3inln1fVzunYjH .icon-shape .label{text-align:center;}#mermaid-svg-fR3inln1fVzunYjH .node.clickable{cursor:pointer;}#mermaid-svg-fR3inln1fVzunYjH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fR3inln1fVzunYjH .arrowheadPath{fill:#333333;}#mermaid-svg-fR3inln1fVzunYjH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fR3inln1fVzunYjH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fR3inln1fVzunYjH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fR3inln1fVzunYjH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fR3inln1fVzunYjH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fR3inln1fVzunYjH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fR3inln1fVzunYjH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fR3inln1fVzunYjH .cluster text{fill:#333;}#mermaid-svg-fR3inln1fVzunYjH .cluster span{color:#333;}#mermaid-svg-fR3inln1fVzunYjH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fR3inln1fVzunYjH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fR3inln1fVzunYjH rect.text{fill:none;stroke-width:0;}#mermaid-svg-fR3inln1fVzunYjH .icon-shape,#mermaid-svg-fR3inln1fVzunYjH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fR3inln1fVzunYjH .icon-shape p,#mermaid-svg-fR3inln1fVzunYjH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fR3inln1fVzunYjH .icon-shape .label rect,#mermaid-svg-fR3inln1fVzunYjH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fR3inln1fVzunYjH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fR3inln1fVzunYjH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fR3inln1fVzunYjH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-fR3inln1fVzunYjH .online>*{fill:#bbf!important;stroke:#333!important;stroke-width:2px!important;}#mermaid-svg-fR3inln1fVzunYjH .online span{fill:#bbf!important;stroke:#333!important;stroke-width:2px!important;} LLM 意图改写
短句 1
短句 2
短句 3
结果汇总
结果汇总
结果汇总
Rerank 重新打分
组装上下文 Prompt
输出
用户原始输入
改写为 1~3 条
不同视角查询短句
Hybrid 检索 Top N chunks
Hybrid 检索 Top N chunks
Hybrid 检索 Top N chunks
按 kb_id 聚合去重
收敛至 Top K 知识库
LLM 终审裁判 K 选 1
最终目标知识库 + Top chunks
4. 前提:文档内容的质量要求
路由准确率直接取决于向量库中文档内容的质量。以下是对知识库文档维护的要求:
| 要求 | 说明 | 影响 |
|---|---|---|
| 每个知识库至少有 5 条 FAQ | FAQ 的 question 是最接近用户提问形式的内容,是路由命中的核心锚点 | FAQ 不足会导致口语化提问无法命中 |
| 文档章节标题清晰具体 | H2 标题应体现具体话题("Redis 满容排查"而非"问题处理") | 模糊标题降低语义匹配精度 |
| 包含业务专有名词 | 文档中明确出现系统名、组件名、错误码等专有名词 | 缺失专有名词导致 BM25 精确匹配失效 |
| 文档角色分类准确 | 每篇文档按角色类型(desc / overview / architecture / api / business / product / ops / runtime)分类 | 分类错误导致按类型过滤时漏检 |
| chunk 元数据完整 | 每个 chunk 携带 kb_id、layer、doc_type 等元数据 |
元数据缺失无法做 kb_id 聚合和类型过滤 |
5. 在线路由管线
5.1 基于文档角色类型的 Query 改写
用户的一条提问可能涉及多种文档角色类型。通过 LLM 识别最相关的意图类型,并为每种意图生成一条优化后的查询短句,实现按类型精准召回。
文档角色类型定义:
| 类型 | 含义 | 典型内容 |
|---|---|---|
desc |
实体描述 | 服务、组件或概念的定义与说明 |
overview |
术语表与项目总览 | 项目整体介绍、术语定义、名词解释 |
architecture |
系统架构文档 | 架构设计、模块拆分、技术选型、部署拓扑 |
api |
API 接口文档 | 接口定义、请求/响应格式、参数说明 |
business |
业务逻辑文档 | 业务流程、规则引擎、状态机、数据流转 |
product |
产品手册 | 功能说明、使用指南、产品特性 |
ops |
运维手册 | 部署流程、监控告警、故障排查、变更操作 |
runtime |
服务运行时 | 运行时配置、性能调优、日志分析、资源监控 |
改写 Prompt:
text
System:
根据用户问题,从以下文档角色类型中识别出最相关的意图,
并为每种意图生成一条优化后的搜索查询短句。
文档角色类型:
- desc: 实体描述 --- 对某个服务、组件或概念的定义与说明
- overview: 术语表与项目总览 --- 项目整体介绍、术语定义、名词解释
- architecture: 系统架构文档 --- 架构设计、模块拆分、技术选型、部署拓扑
- api: API 接口文档 --- 接口定义、请求/响应格式、参数说明
- business: 业务逻辑文档 --- 业务流程、规则引擎、状态机、数据流转
- product: 产品手册 --- 功能说明、使用指南、产品特性
- ops: 运维手册 --- 部署流程、监控告警、故障排查、变更操作
- runtime: 服务运行时 --- 运行时配置、性能调优、日志分析、资源监控
要求:
1. 从上述类型中选出与问题最相关的意图,最少 1 条,最多 {max_count} 条,不要重复
2. 如果用户意图非常明确且只涉及单一文档类型,返回 1 条即可,不必凑数
3. 每条查询是完整的句子,适合语义搜索
4. 保留原问题中的关键专有名词
5. category 值必须使用上述英文标识(如 desc、api、ops 等)
输出 JSON:
[
{"category": "ops", "query": "优化后的搜索查询短句"}
]
User:
用户问题:{user_query}
改写示例:
| 用户原始提问 | 改写结果 |
|---|---|
| "Redis 满容了怎么办" | ① ops: "Redis 满容故障排查与处理步骤" ② runtime: "Redis 内存使用监控与容量分析" |
| "配置中心的推送链路是怎么设计的" | ① architecture: "配置中心推送链路架构设计" |
| "payment-service 的接口返回格式" | ① api: "payment-service 接口返回格式与参数说明" |
| "Hubble 怎么创建自定义监控大盘" | ① product: "Hubble 自定义监控大盘创建指南" |
改写产出的 category 字段可在后续 Hybrid 检索中作为元数据过滤条件,进一步提升精准度。
5.2 Hybrid 检索
每条改写查询分别执行 Hybrid 检索(Dense + Sparse),直接命中已有向量库中的文档 chunks。改写产出的 category 可作为元数据过滤条件,缩小检索范围。
检索参数:
| 参数 | 值 | 说明 |
|---|---|---|
| Dense 权重 | 0.6 | 语义匹配为主 |
| Sparse 权重 | 0.4 | BM25 关键词匹配,保障专有名词精确召回 |
| 每条查询 Top-K | 10 | 单次检索返回 10 个 chunks |
| 相似度下限 | 0.3 | 低于阈值的结果丢弃,避免噪声 |
| 元数据过滤 | doc_type == category |
按改写产出的文档角色类型过滤(可选) |
为什么 Sparse 权重较高(0.4):
知识库路由场景中,专有名词(Hubble、CMDB、配置中心)是强区分信号。BM25 对精确词匹配的优势在这个场景下比通用 RAG 更有价值。
元数据过滤策略: 当改写只产出 1 条查询时,启用 doc_type 过滤以提高精度;当产出多条查询时,每条查询按各自的 category 独立过滤,结果合并后覆盖面更广。
5.3 kb_id 聚合与去重
将 1~3 条改写查询的检索结果汇总,按 kb_id 聚合为知识库级别的候选:
- 按
kb_id聚合 :同一知识库被命中的所有 chunks,取最高分 chunk 作为该知识库的代表得分 - 命中数加权:同时记录每个知识库被命中的 chunk 数量,作为辅助排序信号(命中越多说明覆盖面越广)
- 综合排序 :
最终分 = 代表得分 × 0.7 + 归一化命中数 × 0.3
聚合后通常得到 5~15 个候选知识库。
5.4 Rerank 精排(可选环节)
使用 Rerank 模型(BGE-Reranker-v2 / Cohere Rerank)对候选结果重新打分:
- 输入:用户原始提问 + 每个候选知识库的代表 chunk 内容
- 输出:每个候选的 Rerank 分数
- 收敛:取 Top 3~5 个知识库进入终审
既然有 LLM 终审,为什么还需要 Rerank?
Rerank 并非必要环节,最终裁决权在 LLM。但它在以下场景中有明确优势:
| 维度 | 直接跳到 LLM 终审 | 先 Rerank 再 LLM 终审 |
|---|---|---|
| 候选数量 | 聚合后通常 5~15 个候选全部送入 LLM,Prompt 较长 | Rerank 先收敛到 3~5 个,LLM 只需在少量候选中裁决 |
| LLM 成本 | 候选越多 Token 越多,成本线性增长 | 减少 60~70% 的 LLM 输入 Token |
| LLM 准确率 | 候选过多时 LLM 容易被干扰项分散注意力 | 少量高质量候选让 LLM 裁决更稳定 |
| 延迟 | LLM 处理长 Prompt 耗时增加 | Rerank(~150ms)+ 短 Prompt LLM 总耗时更低 |
建议策略: 当聚合后候选数 ≤ 5 时,可跳过 Rerank 直接进 LLM 终审;候选数 > 5 时,先 Rerank 收敛再终审。
5.5 LLM 终审裁判
Rerank 收敛后,仍可能存在多个高置信候选。用 LLM 做最终 N 选 1 裁判。
终审 Prompt:
text
System:
你是一个知识库路由裁判。用户提了一个问题,系统检索到了多个候选知识库。
请判断用户的问题最应该由哪个知识库来回答。
候选知识库(按初步相关性排序):
{candidates}
每个候选包含:
- kb_name: 知识库名称
- kb_description: 知识库范围描述
- matched_chunk: 命中的代表性文档片段
- score: 初步相关性分数
判断规则:
1. 优先选择与用户问题语义最匹配的知识库
2. 当多个知识库都可能相关时,选择范围最聚焦(而非最宽泛)的那个
3. 如果没有任何知识库能回答用户问题,输出 null
输出 JSON:
{
"selected_kb_id": "kb-xxx" 或 null,
"confidence": 0.0~1.0,
"reason": "简短的选择理由"
}
User:
用户问题:{user_query}
5.6 全链路延迟预估
| 环节 | 预估耗时 | 是否可并行 |
|---|---|---|
| Query 改写(LLM) | 300~500ms | --- |
| Hybrid 检索 × 3 | 50~100ms × 3 | 三条查询并行执行 |
| kb_id 聚合 + 去重 | < 10ms | --- |
| Rerank | 100~200ms | --- |
| LLM 终审 | 300~600ms | --- |
| 端到端 P95 | ~1.2s | --- |
优化手段:
- Query 改写使用轻量模型(DeepSeek-V3 / GPT-4o-mini),降低改写延迟
- 当改写只产出 1 条查询时,跳过聚合,直接进 Rerank
- LLM 终审当 Rerank Top1 分数 > 0.9 且 Top1 与 Top2 差距 > 0.2 时,跳过终审直接输出
6. 路由结果的使用
路由输出 selected_kb_id + confidence 后,影响后续 RAG 检索的范围:
| 置信度 | 检索策略 |
|---|---|
| 高置信(> 0.8) | 仅在选定知识库内检索,按 L1~L5 分层过滤 |
| 中置信(0.5~0.8) | 在选定知识库 + 相邻知识库内检索 |
| 低置信(< 0.5) | 全库检索,不做知识库过滤 |
路由过程中命中的 Top chunks 可以直接复用到后续 RAG Pipeline,避免重复检索。
7. 兜底与异常处理
| 场景 | 处理策略 |
|---|---|
| Rerank Top1 置信度 < 0.4 | 不路由,直接回复用户"未找到匹配的知识库,请补充更多信息" |
| LLM 终审返回 null | 同上,并记录该 query 到未覆盖日志,作为知识库缺口信号 |
| LLM 终审 confidence 在 0.4~0.6 | 路由到选定知识库,但答案附带"此回答可能不完全匹配,建议联系 {kb_owner}" |
| 改写 LLM 超时 | 降级为用户原始 query 直接检索(跳过改写) |
| Rerank 模型不可用 | 降级为按 Hybrid 检索原始分数排序 |
| 用户 query 过短(< 3 字) | 追问用户补充信息,不直接路由 |
8. 总结
本方案的核心设计决策:
| 决策 | 选择 | 理由 |
|---|---|---|
| 路由索引 | 复用已有向量库,不建独立索引 | 文档已全量向量化,额外索引增加维护成本且容易与源数据脱节 |
| 离线增强 | 人工维护文档和 FAQ,不用 AI 生成合成数据 | 合成 QA 质量不可控,且与真实文档内容脱节 |
| Query 改写 | 基于文档角色类型改写,而非通用角度改写 | 与文档分类体系对齐,改写结果可直接用于元数据过滤 |
| 检索方式 | Dense + Sparse 混合检索 | 兼顾语义匹配和专有名词精确匹配 |
| Rerank | 可选环节,候选 > 5 时启用 | 减少 LLM 终审的输入量和成本,提升裁决稳定性 |
| 最终裁决 | LLM 终审 | 向量分数和 Rerank 分数是局部最优,LLM 能综合判断全局最优 |
一句话总结:直接复用已有向量化文档,叠加"多角色改写 → Hybrid 检索 → kb_id 聚合 → LLM 终审"的在线路由管线,无需额外建设离线索引,以最小改造量解决知识库路由问题。