☰
生产级知识库与Agent网关融合架构:混合检索与模型路由实战
2026/9/25 3:25:08 网站建设 项目流程

1. 生产级知识库与 Agent 网关的整体设计思路

1.1 为什么要把知识库和 Agent 网关放在一起做

单独做一个 RAG 知识库,或者单独做一个 Agent 网关,这两件事在 Demo 阶段都不难。难的是把它们放到生产环境里,让它们协同工作,还要保证延迟、成本、可观测性都在可控范围内。我最近这段时间主要就是在干这件事,踩了不少坑,也积累了一些还算靠谱的经验。

先说清楚这两个东西各自是什么。知识库负责把非结构化的文档、网页、PDF、Markdown 等资料,经过切块、向量化、索引之后,变成可以被检索的知识源。Agent 网关则是所有大模型调用的统一入口,它要处理路由、鉴权、限流、重试、缓存、日志这些事情。把两者放在一起,是因为 Agent 在回答用户问题时,往往需要先检索知识库,再基于检索结果生成回答,这个链路如果各自为政,排查问题会非常痛苦。

我见过太多团队的做法是:知识库用一套服务,Agent 调用用另一套服务,两边日志格式不一样,trace id 对不上,出了问题只能靠猜。所以我的核心设计思路是——把知识库检索也当成网关的一个上游能力来对待,统一入口、统一日志、统一超时控制。

1.2 核心链路的拆解

一条完整的请求链路,从用户提问到最终回答,大致经过这几个环节:

  1. 用户请求进入 Agent 网关,网关做鉴权和限流
  2. 网关判断这次请求是否需要检索知识库
  3. 如果需要,调用知识库的检索接口,拿到候选文档片段
  4. 对候选片段做重排序(rerank),筛掉不相关的
  5. 把筛选后的片段拼进 prompt,调用大模型
  6. 大模型返回结果,网关做后处理和日志记录
  7. 返回给用户

这个链路里,第 3 步和第 4 步是最容易出问题的地方。检索召回不准,后面再好的模型也救不回来;重排序太慢,整体延迟就上去了。所以我在设计时,把这两步单独拎出来做了性能预算。

1.3 方案选型背后的考量

关于检索方案,我最终选择了BM25 + 向量检索的混合方案,而不是纯向量检索。原因很直接:纯向量检索在语义相似度上表现好,但对精确匹配的关键词、专有名词、编号这类内容经常翻车。比如用户问"XX-2024-001 这个工单的处理流程",纯向量检索很可能召回一堆语义相近但编号不对的文档。BM25 在这种场景下就是补位选手,它对词频和逆文档频率敏感,精确匹配能力强。

混合检索的常见做法是两路召回后做融合,融合算法我用的是 RRF(Reciprocal Rank Fusion),因为它不需要两路分数的量纲对齐,实现简单,效果稳定。这个后面会详细讲。

Agent 网关这边,我选择自己写一层薄薄的网关,而不是直接用现成的 API 网关。原因是现成网关对 LLM 场景的特殊需求支持不够,比如 token 计费、流式响应的中断处理、多模型路由这些,自己写反而更灵活。

2. 知识库构建的核心细节与实操要点

2.1 文档切块策略:切块大小不是拍脑袋定的

切块(chunking)是知识库构建里最容易被忽视、但影响最大的环节。我见过有人直接把整篇文档塞进去,也见过有人按固定 512 字符硬切。这两种做法在生产环境都会出问题。

我的切块策略是这样的:

  • 按语义边界切:优先按段落、标题、列表项切,而不是按字符数硬切
  • 控制块大小在 300-800 token 之间:太小会丢失上下文,太大会稀释语义
  • 保留重叠:相邻块之间保留 10%-15% 的重叠,避免边界处的信息被切断
  • 给每个块加上元数据:来源文档、章节标题、页码、更新时间

为什么是 300-800 token?这是实测下来的经验值。低于 300 token 的块,往往只包含半句话,检索出来也没法用;高于 800 token 的块,向量化之后语义被平均掉了,检索精度反而下降。当然这个值跟你的文档类型有关,技术文档可以偏小,叙述性文档可以偏大。

元数据这块我要特别强调。没有元数据的知识库,在生产环境基本没法用。因为用户问的问题往往带有时间、来源、版本这些约束,没有元数据你就没法做过滤。比如用户问"最新的部署流程是什么",你得能按更新时间过滤。

2.2 向量化模型的选择

向量化模型的选择,我主要看三个维度:中文效果、维度大小、推理成本。

维度大小直接影响存储和检索速度。768 维和 1536 维,在百万级文档下,存储和检索延迟差距是肉眼可见的。我的建议是,如果中文场景为主,优先选针对中文优化过的模型,不要盲目追求维度高。

推理成本这块,如果文档量大,建议做批量向量化,并且把向量化任务做成异步的。我一开始是同步做的,结果一次全量重建索引把服务拖垮了,后来改成队列 + 批量处理才稳定下来。

注意:向量化模型一旦选定,后续换模型的成本极高,因为所有文档都要重新向量化。所以选型时一定要留足评估时间,别急着上线。

2.3 BM25 索引的构建与参数调优

BM25 的实现我用的是常见的开源库,核心参数是两个:k1 和 b。

  • k1控制词频饱和度,默认 1.2-2.0。值越大,词频的影响越强
  • b控制文档长度归一化,默认 0.75。值越大,长文档的惩罚越重

我的调参方法是:先固定 b=0.75,在验证集上网格搜索 k1;然后固定最优 k1,再搜 b。验证集用真实的用户查询和标注的相关文档构建,规模不用大,几百条就够看出趋势。

中文场景下,BM25 还需要处理分词问题。我用的分词方案是结合词典的,因为通用分词器对专有名词的切分经常不准。词典里我会把业务相关的术语、产品名、编号规则都加进去,这样 BM25 的召回质量会明显提升。

2.4 混合检索的融合策略

两路召回之后,怎么融合是个关键问题。我试过几种方案:

融合方案优点缺点适用场景
加权求和实现简单需要分数归一化,量纲难对齐两路分数分布接近时
RRF无需归一化,稳定丢失分数绝对值信息通用场景,推荐
学习排序效果最好需要标注数据,成本高有充足标注时

我最终用的是 RRF,公式很简单:每个文档的得分是1/(k + rank)的累加,k 一般取 60。这个方案的好处是,不管两路召回的分数是什么量纲,只看排名,融合结果都很稳定。

RRF 之后,我会再取 Top-N 做重排序。重排序模型比向量检索慢,但精度高,所以只对少量候选做。这个"先粗排后精排"的两阶段思路,是生产环境的标配。

3. Agent 网关的实现与关键环节

3.1 网关的核心职责划分

Agent 网关不是一个简单的反向代理,它要承担这些职责:

  • 统一鉴权:所有模型调用都从这里走,API Key 统一管理
  • 模型路由:根据请求特征路由到不同模型,比如简单问题走小模型,复杂问题走大模型
  • 限流与配额:按用户、按租户做限流,防止单个用户打爆服务
  • 重试与降级:上游模型超时或报错时,自动重试或降级到备用模型
  • 缓存:相同或相似的请求直接返回缓存结果
  • 日志与追踪:记录完整的请求链路,方便排查

这六件事里,模型路由和缓存是省钱的两大杀器。我实测下来,加上缓存之后,重复请求的模型调用成本能降 30% 以上。

3.2 模型路由的实现细节

模型路由的规则我分了三层:

  1. 按请求类型路由:检索类请求走便宜模型,生成类请求走强模型
  2. 按复杂度路由:先用一个轻量分类器判断问题复杂度,再决定用哪个模型
  3. 按降级策略路由:主模型不可用时,自动切到备用模型

复杂度分类器我用的是一个小模型,输入是用户问题,输出是"简单/中等/复杂"三档。这个分类器不需要很准,只要能把明显简单的问题分流出去就行。实测下来,简单问题占比通常在 40% 左右,这部分走小模型能省不少钱。

提示:路由规则一定要可配置,不要硬编码在代码里。因为模型的价格和能力变化很快,硬编码会导致每次调整都要发版。

3.3 流式响应的处理

LLM 的流式响应(streaming)在生产环境处理起来比想象中麻烦。主要问题有三个:

  • 中断处理:用户关闭页面时,要能及时中断上游请求,避免浪费
  • 超时控制:流式响应没有明确的结束时间,需要设置首字节超时和总超时
  • 错误恢复:流到一半上游报错,要能优雅地告诉前端

我的做法是,网关层维护一个请求上下文,记录每个流式请求的状态。用户断开连接时,通过上下文取消上游请求。首字节超时设 10 秒,总超时按模型的最大输出长度估算。

3.4 缓存策略的设计

缓存这块,我分了三级:

  • 精确缓存:请求参数完全一致时命中,用哈希做 key
  • 语义缓存:请求语义相似时命中,用向量相似度判断
  • 片段缓存:知识库检索结果缓存,因为检索比生成便宜但也不免费

精确缓存最简单,命中率也最高,但只对完全重复的请求有效。语义缓存命中率更高,但有误判风险,需要设置较高的相似度阈值。片段缓存是我觉得性价比最高的,因为很多问题的检索结果是重叠的。

缓存的失效策略也要考虑。知识库更新后,相关的缓存要失效。我的做法是给缓存加上知识库版本号,版本变了就整体失效。

4. 常见问题与排查技巧实录

4.1 检索召回不准的排查思路

检索召回不准是最常见的问题,排查时我一般按这个顺序来:

  1. 先看原始文档有没有被正确切块:切块错了,后面全错
  2. 再看向量化有没有问题:拿一个已知相关的查询,看它的向量和文档向量的相似度
  3. 然后看 BM25 分词:专有名词有没有被切碎
  4. 最后看融合和重排序:是不是融合把好的结果排下去了

我遇到过一个典型案例:用户问某个产品型号的问题,检索总是召回不相关的内容。排查后发现是分词器把这个型号切成了几段,BM25 完全匹配不上。把型号加进自定义词典后,问题就解决了。

4.2 延迟过高的优化路径

延迟高的时候,先做链路拆解,看时间花在哪一段。我一般会打点记录这几个时间:

  • 网关接收请求到发出检索请求
  • 检索耗时(向量检索 + BM25 + 融合 + 重排序)
  • 模型调用耗时(首字节 + 总时长)
  • 后处理耗时

实测下来,延迟大头通常在模型调用和重排序。模型调用能优化的空间有限,主要是选更快的模型或者做流式。重排序可以优化,比如减少候选数量、用更轻量的重排序模型、或者对简单查询跳过重排序。

4.3 常见问题速查表

问题现象可能原因排查方法解决方向
检索结果不相关切块不合理/分词错误检查切块边界和分词结果调整切块策略,补充词典
响应延迟高重排序慢/模型慢分段打点减少候选,换轻量模型
缓存命中率低请求参数不稳定看缓存 key 分布归一化请求参数
流式响应中断超时设置不当看超时日志调整首字节和总超时
成本超预算路由规则太粗看模型调用分布细化路由,加缓存

4.4 几个踩过的坑

坑一:向量维度和索引不匹配。换向量化模型时,忘了重建索引,导致检索结果全是乱的。这个坑很隐蔽,因为服务不报错,只是结果不对。

坑二:BM25 索引没更新。知识库更新了文档,但 BM25 索引没重建,导致新文档检索不到。后来我把索引更新做成了文档更新的触发动作。

坑三:网关重试导致重复计费。上游超时后网关自动重试,但上游其实已经处理了,结果计费了两次。后来加了幂等 key 才解决。

坑四:缓存穿透。大量不存在的查询打到后端,缓存完全没起作用。加了空结果缓存和布隆过滤器之后缓解了。

5. 生产环境的可观测性建设

5.1 日志与追踪的设计

生产环境没有可观测性,等于闭着眼睛开车。我的日志设计遵循几个原则:

  • 每个请求一个 trace id,贯穿网关、检索、模型调用全链路
  • 结构化日志,用 JSON 格式,方便后续分析
  • 关键节点打点,记录耗时、token 数、命中缓存等

trace id 的传递我用的是标准的 header 透传,这样跨服务也能串起来。日志里我会记录请求的摘要(不记录完整内容,避免隐私问题)、模型名称、token 消耗、耗时这些。

5.2 关键指标的监控

我重点监控这几个指标:

  • QPS 和错误率:基础健康指标
  • P50/P95/P99 延迟:看长尾,P99 往往暴露问题
  • 缓存命中率:直接影响成本和延迟
  • token 消耗:按模型、按租户统计,控制成本
  • 检索召回率:需要标注数据,定期评估

这些指标我会做成看板,设置告警阈值。比如 P99 延迟超过 5 秒就告警,缓存命中率低于 20% 也告警。

5.3 成本控制的实操经验

成本控制这块,我总结了几个有效的手段:

  1. 缓存优先:能缓存的都缓存,尤其是检索结果
  2. 路由分流:简单问题走小模型,实测能省 30%-40%
  3. prompt 精简:检索片段不要全塞进去,重排序后只留最相关的
  4. 输出长度限制:设置 max_tokens,避免模型啰嗦
  5. 批量处理:非实时任务批量调用,有些模型批量有折扣

这几个手段叠加下来,我的整体成本比最初降了大概一半。当然前提是效果不能降,所以每次优化都要做效果回归。

6. 后续可以继续深挖的方向

这套东西跑起来之后,我还在继续优化几个方向。一个是Agentic RAG,就是让 Agent 自己决定要不要检索、检索几次、怎么改写查询,而不是固定的一次检索。这个方向效果提升空间大,但控制复杂度也高,需要仔细设计。

另一个是知识库的自动更新和版本管理。现在文档更新还是半自动的,理想状态是文档一变,索引自动重建,缓存自动失效,全程不用人工干预。

还有就是多租户隔离。现在租户之间的数据和配额隔离做得还不够细,后续要加上更严格的隔离机制。

这些方向我还在摸索,有新的进展再分享。这套架构不是一蹴而就的,是一点点迭代出来的,每个环节都踩过坑。如果你也在做类似的事情,希望这些经验能帮你少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询