☰
生产级知识库与Agent网关架构选型及工程实践
2026/9/28 17:19:11 网站建设 项目流程

1. 生产级知识库与 Agent 网关的架构选型思路

1.1 为什么单机 RAG 方案撑不住生产环境

我最早接触知识库是从个人笔记工具开始的,Obsidian 加个插件做本地检索,几十篇文档跑得飞快。后来帮团队搭内部问答,文档量从几百涨到几万,问题就全暴露出来了:检索延迟从毫秒级飙到好几秒,召回结果开始出现明显漂移,同一个问题今天答得对、明天答得偏。这不是模型的问题,是架构的问题。

单机 RAG 的本质是“把向量库和模型塞进一个进程里”,它假设了三件事:文档量可控、并发量极低、检索质量不需要持续调优。生产环境里这三条全不成立。文档会持续增长,用户会同时提问,业务方会不断要求“这个问题的答案不对,你得改”。所以生产级知识库的核心矛盾不是“能不能跑通”,而是“能不能在文档增长、并发上升、质量要求提高的同时,保持可维护、可观测、可迭代”。

Agent 网关是另一个维度的东西。它解决的是“多个 Agent 怎么统一管理”的问题。你不可能给每个业务场景单独部署一套 Agent 服务,那样运维成本会爆炸。网关要做的是路由、鉴权、限流、日志、降级这一整套事情。知识库和 Agent 网关放在一起优化,是因为它们天然耦合:Agent 需要调用知识库检索,知识库的检索质量直接决定 Agent 的回答质量,而网关是两者之间的调度层。

1.2 选型时我重点权衡的三个维度

第一个维度是检索链路的可控性。很多开源方案把 embedding、向量检索、重排序、生成打包成一个黑盒,你只能调几个参数。生产环境里这远远不够。你需要能单独替换 embedding 模型、能单独调整分块策略、能单独观察每一路的召回结果。所以我倾向于把链路拆开,每一段都可以独立替换和观测。

第二个维度是部署形态的灵活性。国内企业环境有个现实约束:很多场景要求私有化部署,数据不能出内网。这就要求整个方案能在离线环境里跑起来,不能依赖外部 API。Ollama 加本地向量库的组合就是为这个场景准备的。但私有化不等于低性能,你仍然需要支持并发、需要支持水平扩展。

第三个维度是与 Agent 框架的对接成本。知识库不是孤立的,它要被 Agent 调用。如果知识库的接口设计和 Agent 框架的调用习惯不匹配,中间就要写大量胶水代码。我倾向于让知识库暴露标准的检索接口,网关层做协议转换,这样 Agent 侧不需要关心知识库的具体实现。

1.3 我最终采用的架构分层

整体分成四层。最底层是存储层,文档原文、分块后的文本、向量索引、元数据分开存储。中间是检索层,负责 embedding、向量召回、关键词召回、重排序。上面是服务层,把检索能力包装成 HTTP 接口,处理鉴权、限流、缓存。最上面是网关层,对接 Agent 框架,做路由和编排。

这个分层的好处是每一层可以独立演进。比如检索层想换 embedding 模型,只需要重新生成向量索引,服务层和网关层完全不用动。网关层想加一个新的 Agent 接入,也不需要改检索逻辑。生产环境最怕的就是“改一处动全身”,分层是控制变更影响面的基本手段。

2. 知识库核心链路的细节拆解与实操要点

2.1 文档解析与分块:最容易被低估的环节

很多人把精力全花在选模型上,结果文档解析这一步就埋了雷。PDF 里的表格、扫描件里的文字、Markdown 里的代码块,如果解析阶段就丢了结构,后面检索再准也救不回来。我的做法是按文档类型走不同的解析器,而不是用一个通用解析器吃所有格式。

对于 Markdown 和纯文本,直接按标题层级切分,保留层级信息作为元数据。对于 PDF,先用版面分析把正文、表格、页眉页脚分开,表格单独走结构化提取。对于扫描件,OCR 之后要做一次文本清洗,把识别噪声去掉。这一步的投入产出比极高,解析质量提升一点,后面检索质量提升一大截。

分块策略上,我试过固定长度、按句子、按语义几种方式。实测下来,按语义分块加适当重叠效果最稳。具体做法是先用句子边界做粗切,再把相邻的短句合并到目标长度,块与块之间保留百分之十到十五的重叠。重叠的作用是防止一个完整语义被切断后两边都召回不到。块大小我一般控制在五百到八百个 token,太小会丢上下文,太大会稀释语义。

注意:分块不是越细越好。我见过有人把块切到一百 token,结果检索出来的片段全是半句话,生成阶段根本没法用。块大小要和你的 embedding 模型的最大输入长度匹配,一般取模型上限的百分之六十到八十比较合适。

2.2 Embedding 模型的选择与本地化部署

Embedding 模型决定了“语义空间”的质量。选型时我主要看三个指标:检索准确率、推理速度、模型体积。准确率看公开榜单只能做参考,真正靠谱的是拿你自己的业务数据做一次小规模评测。我一般会准备两百到五百条“问题-正确文档”的配对,跑一遍召回率,哪个模型高就用哪个。

本地化部署用 Ollama 是最省事的方案。它把模型下载、加载、推理服务都封装好了,一条命令就能起一个 embedding 服务。但有几个坑要注意。第一,Ollama 默认的并发数很低,生产环境要调高OLLAMA_NUM_PARALLEL这个环境变量。第二,模型加载后会常驻内存,要提前算好内存占用,别把机器撑爆。第三,不同模型的输出维度不一样,换模型必须重建索引,不能混用。

如果对延迟要求极高,可以考虑用更小的模型做粗筛,再用大模型做精排。这个思路和推荐系统里的“召回-排序”两阶段是一样的。粗筛阶段用轻量模型快速缩小候选集,精排阶段用重量模型精确打分。实测下来,这个组合能在保证质量的前提下把延迟降低百分之四十左右。

2.3 混合检索:向量召回加关键词召回

纯向量检索有个天然缺陷:它对精确匹配不敏感。用户问“XX 接口的超时时间是多少”,向量检索可能召回一堆讲超时机制的文档,但就是找不到那个具体数值。这时候关键词召回就派上用场了。

我的做法是向量召回和关键词召回并行执行,然后融合结果。向量召回负责语义相似,关键词召回负责精确匹配。融合算法用倒数排名融合,就是把两路结果按排名取倒数再相加,最后重新排序。这个算法不需要调参,对两路结果的分数尺度不敏感,实测很稳。

关键词召回我一般用 BM25 或者基于倒排索引的方案。如果文档量不大,直接用数据库的全文索引也能凑合。但要注意中文分词的问题,通用分词器对专业术语的切分经常出错,最好能加载自定义词典。我踩过的坑是:一个产品名叫“智能网关”,分词器把它切成“智能”和“网关”,结果搜“智能网关”反而搜不到。后来加了自定义词典才解决。

2.4 重排序:提升精度的最后一道关卡

召回阶段追求的是“不漏”,重排序阶段追求的是“不误”。重排序模型会对召回结果重新打分,把真正相关的排到前面。这一步对最终质量的影响非常大,我实测下来,加了重排序之后,Top3 命中率能提升百分之二十以上。

重排序模型一般比 embedding 模型大,推理也慢,所以只对召回的前几十条做重排,不要对全量做。模型选择上,交叉编码器效果最好,但速度慢;双编码器速度快,但精度略低。生产环境我一般用交叉编码器,因为重排序的候选集不大,延迟可以接受。

实操心得:重排序的输入是“查询-文档”对,输出是一个相关性分数。这个分数是相对的,不同查询之间的分数不可比。所以不要设一个全局阈值来过滤,而应该按排名截断,比如只取前五条。

3. Agent 网关的实操过程与核心环节实现

3.1 网关的核心职责与请求生命周期

Agent 网关不是简单的反向代理,它要处理的事情比普通网关多得多。一个请求进来,网关要做的事包括:解析请求、识别意图、选择 Agent、调用知识库、组装上下文、调用模型、处理流式输出、记录日志。这一整套流程下来,任何一个环节出问题都会影响用户体验。

我把网关的请求生命周期拆成六个阶段。接入阶段做鉴权和限流,防止恶意请求打垮后端。路由阶段根据请求内容选择对应的 Agent 和知识库。检索阶段调用知识库接口获取相关文档。组装阶段把检索结果和用户问题拼成模型输入。生成阶段调用模型并处理流式返回。收尾阶段记录日志、更新缓存、上报指标。

每个阶段都要有超时控制和降级策略。比如检索阶段超时了,不能直接报错,应该降级成“不使用知识库直接回答”,并给用户一个提示。生成阶段超时了,要把已经生成的部分返回给用户,而不是全部丢弃。这些细节决定了系统的健壮性。

3.2 路由策略:怎么把请求分给正确的 Agent

路由是网关最核心的逻辑。最简单的做法是按 URL 路径路由,比如/agent/customer走客服 Agent,/agent/tech走技术 Agent。但生产环境往往需要更智能的路由,比如根据用户问题的内容自动选择 Agent。

我实现过两种路由策略。一种是基于规则的路由,用关键词匹配或者正则表达式判断意图。这种方式简单可控,但维护成本高,规则多了之后会互相冲突。另一种是基于分类模型的路由,训练一个小模型来判断问题属于哪个领域。这种方式准确率高,但需要标注数据,冷启动麻烦。

实际生产中我用的是混合策略:先用规则做粗筛,规则覆盖不到的再用分类模型。这样既能保证常见场景的稳定性,又能处理长尾请求。路由结果要记录到日志里,方便后续分析哪些请求被路由错了,持续优化规则和模型。

3.3 上下文组装:把检索结果变成模型能用的输入

检索出来的文档片段不能直接塞给模型,需要经过组装。组装的核心问题是:怎么在有限的上下文窗口里放最多的有效信息。模型有最大输入长度限制,检索结果太多会超限,太少又不够回答。

我的做法是按相关性排序,从高到低填充,直到接近窗口上限。同时要给每个片段加上来源标记,方便模型引用和用户溯源。片段之间用分隔符隔开,避免模型把它们混在一起理解。如果片段之间有重叠内容,要做去重,否则会浪费窗口空间。

还有一个细节是指令和上下文的顺序。我试过把指令放前面、上下文放后面,也试过反过来。实测下来,把指令放前面效果更好,因为模型在处理长上下文时对开头的内容注意力更集中。这个结论和很多论文里的“中间遗忘”现象是一致的。

3.4 流式输出与超时控制

Agent 的回答往往是流式的,一个字一个字往外蹦。网关要正确处理流式输出,不能等模型全部生成完再返回。实现上一般用 Server-Sent Events 或者 WebSocket。SSE 更简单,适合单向推送;WebSocket 更灵活,适合双向交互。

超时控制要分阶段设置。检索阶段一般给三到五秒,生成阶段给三十到六十秒。如果生成阶段超时,要把已经生成的内容返回,并标记为“未完成”。用户看到部分答案总比看到报错好。同时要记录超时日志,分析是模型太慢还是检索太慢,针对性优化。

注意:流式输出时如果中间某个片段检索失败,不要中断整个流。应该跳过失败片段继续生成,并在日志里记录。用户体验的连续性比单次检索的完整性更重要。

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

4.1 检索质量问题的排查思路

检索质量差是最常见的问题,但原因可能有很多种。我一般按这个顺序排查:先看分块是否合理,把检索到的片段打印出来,看是不是完整的语义单元。如果片段是半句话,说明分块有问题。再看embedding 模型是否匹配,用几个典型问题测试,看召回结果是否语义相关。如果召回结果完全不相关,可能是模型不适合这个领域。

然后看混合检索的权重,向量召回和关键词召回的融合比例是否合适。如果精确匹配的问题召回不到,说明关键词召回权重太低。最后看重排序是否生效,把重排序前后的结果对比一下,看排名是否有改善。如果重排序后反而变差,可能是重排序模型和 embedding 模型不匹配。

我整理了一个排查速查表,按现象找原因:

现象可能原因排查方法
召回结果完全不相关embedding 模型不匹配换模型重新评测
精确匹配搜不到关键词召回未生效检查分词和倒排索引
召回结果重复分块重叠过大调整重叠比例
排名靠前的结果不相关重排序未生效或模型不匹配对比重排序前后结果
检索延迟高向量库索引未优化检查索引类型和参数

4.2 网关层的典型故障与处理

网关层最常见的问题是超时和限流。超时往往是下游服务慢导致的,要逐层排查是检索慢还是生成慢。限流则是流量突增导致的,要提前做好容量规划,设置合理的限流阈值。我一般会在网关层做两级限流:单用户限流和全局限流。单用户限流防止单个用户刷爆系统,全局限流保护后端不被压垮。

另一个常见问题是上下文超限。检索结果太多导致拼出来的输入超过模型窗口。解决办法是在组装阶段做截断,按相关性排序后只取前 N 条。N 的取值要根据模型窗口大小和平均片段长度来算。我一般会留百分之二十的余量,防止个别片段特别长导致超限。

还有一个坑是流式输出的连接管理。如果客户端断开连接,网关要能感知到并停止生成,否则会浪费计算资源。实现上要监听连接关闭事件,及时释放资源。这个细节很多框架默认不处理,需要自己加。

4.3 性能优化的几个实操技巧

第一个技巧是缓存。相同的查询可以直接返回缓存结果,不用重新检索和生成。缓存要分两层:检索结果缓存和生成结果缓存。检索结果缓存命中率高,因为很多查询的检索结果是一样的。生成结果缓存命中率低,因为生成有随机性。我一般只缓存检索结果,生成结果不缓存或者只缓存很短时间。

第二个技巧是预热。服务启动后,embedding 模型和重排序模型需要加载到内存,第一次请求会特别慢。可以在启动后主动跑几个请求做预热,把模型加载到内存里。这个技巧能把首请求延迟从十几秒降到几百毫秒。

第三个技巧是批量处理。如果有多个请求同时到达,可以把它们的 embedding 请求合并成一个批次,一次性送给模型。这样能充分利用 GPU 的并行能力,提升吞吐量。但要注意批次不能太大,否则单个请求的延迟会上升。我一般把批次大小控制在八到十六之间。

4.4 知识库与 Agent 协同的避坑经验

知识库和 Agent 协同最容易出的问题是职责边界不清。有时候 Agent 该做的事推给了知识库,有时候知识库该做的事推给了 Agent。我的原则是:知识库只负责“找到相关文档”,不负责“判断文档是否够用”;Agent 只负责“根据文档生成回答”,不负责“决定要不要检索”。这个边界清晰了,两边都好维护。

另一个坑是检索结果的格式不统一。不同来源的文档检索出来格式不一样,Agent 处理起来要写很多兼容代码。解决办法是在知识库服务层做一次格式化,统一输出结构,包含内容、来源、分数三个字段。Agent 侧只认这个结构,不关心底层是什么文档。

最后一个坑是版本管理。知识库的索引会更新,Agent 的 prompt 会调整,两者版本不匹配会导致行为异常。我一般会给知识库索引和 Agent 配置都打上版本号,网关层记录当前使用的版本组合。出问题时可以快速定位是哪个版本引入的。

5. 从个人知识库到企业级部署的扩展路径

5.1 个人知识库和企业知识库的本质差异

个人知识库和企业知识库看起来都是“存文档、搜文档”,但本质差异很大。个人知识库的用户就是你自己,你知道自己存了什么,检索不准可以手动翻。企业知识库的用户是全体同事,他们不知道你存了什么,检索不准就直接放弃使用。所以企业知识库对检索质量的要求远高于个人知识库。

另一个差异是权限管理。个人知识库不需要权限,企业知识库必须做权限隔离。不同部门、不同职级的同事能看到的文档不一样。这个权限要贯穿整个链路:检索时要过滤无权限的文档,生成时要检查引用来源是否有权限,日志里不能泄露无权限的内容。权限做不好,要么泄露信息,要么误伤正常使用。

还有一个差异是更新频率。个人知识库可能几个月才更新一次,企业知识库每天都在变。这就要求索引更新要自动化,不能靠手动触发。我一般会做一个定时任务,定期扫描文档变更,增量更新索引。全量重建太慢,增量更新才能跟上变化速度。

5.2 私有化部署的硬件选型与容量规划

私有化部署首先要算清楚硬件需求。主要消耗在三个地方:embedding 推理、向量检索、模型生成。embedding 推理和模型生成吃 GPU,向量检索吃内存。如果文档量在十万级,向量维度是七百六十八,那向量数据大约占三百兆内存,加上索引开销,一个 G 内存够了。但如果文档量到百万级,内存需求就上去了。

GPU 选型要看并发量。如果只是内部几十个人用,一张消费级显卡就够了。如果要支撑几百人并发,就需要专业卡。我一般会按“每路并发需要多少显存”来估算,embedding 模型每路大概几百兆,生成模型每路几个 G。算清楚之后留百分之三十的余量。

实操心得:私有化部署不要一步到位买顶配硬件。先按当前需求配,留好扩展槽位。等业务量上来了再加卡加内存。一次性投入太大,审批流程长,反而拖慢项目进度。

5.3 从单机到分布式的演进路线

单机部署跑通了之后,下一步就是分布式。演进路线一般是:先做读写分离,检索和生成分开部署;再做检索层的水平扩展,多个检索节点前面加负载均衡;最后做存储层的分布式,向量库和文档库都支持分片。

每一步演进都要保证兼容性,不能推翻重来。我的做法是接口先行,先把服务间的接口定义好,内部实现可以逐步替换。比如检索服务一开始是单机的,后来换成分布式的,但对外接口不变,网关层完全无感知。这样演进的风险最小。

分布式之后要引入服务发现和配置中心。服务发现让节点之间能互相找到,配置中心让配置变更不用重启服务。这两个组件是分布式的基础设施,早点引入比晚点引入好。我见过很多项目一开始图省事用硬编码配置,后来节点多了改配置改到崩溃。

5.4 监控与持续迭代的落地方法

生产系统没有监控就是裸奔。知识库和 Agent 网关要监控的指标分三类:质量指标、性能指标、业务指标。质量指标包括召回率、命中率、用户反馈;性能指标包括延迟、吞吐量、错误率;业务指标包括日活、提问量、解决率。

质量指标最难监控,因为需要标注数据。我的做法是抽样人工评估,每周抽一百条问答,人工判断回答是否正确。这个数据虽然少,但能反映整体趋势。同时收集用户反馈,点赞点踩的数据是免费的标注。把这两个数据结合起来,就能持续跟踪质量变化。

持续迭代的关键是建立反馈闭环。用户反馈的问题要能快速定位到是检索问题还是生成问题,定位到之后要能快速修复。我一般会做一个内部工具,输入一个问题,展示完整的检索和生成链路,包括召回了哪些文档、重排序后排名如何、最终生成了什么。这个工具能大幅缩短排查时间。

6. 几个容易被忽略的工程细节

6.1 文档去重与冲突处理

企业知识库里经常有重复文档,同一份文档在不同目录下存了好几份,内容还略有差异。如果不做去重,检索结果里会出现大量重复片段,浪费上下文窗口。我的做法是在入库阶段做一次相似度检测,相似度超过阈值的文档只保留最新版本。

冲突处理更麻烦。两份文档讲同一件事但说法不一样,检索出来模型不知道该信哪个。这种情况我一般会在元数据里加一个“权威度”字段,权威度高的排前面。权威度可以按文档来源、更新时间、作者职级来综合计算。这个字段不参与语义检索,只在重排序阶段作为加权因子。

6.2 多轮对话中的检索策略

单轮问答的检索很简单,拿用户问题去搜就行。但多轮对话里,用户的问题往往依赖上文。比如用户先问“XX 接口怎么调用”,再问“它的超时时间是多少”,第二个问题里的“它”指代的是第一个问题里的接口。如果直接拿第二个问题去检索,什么都搜不到。

解决办法是查询改写。把多轮对话的历史和当前问题一起送给模型,让模型把当前问题改写成独立完整的查询。改写后的查询再去做检索。这个步骤会增加一点延迟,但对多轮对话的检索质量提升非常明显。我实测下来,加了查询改写之后,多轮场景的召回率能提升百分之三十以上。

6.3 冷启动阶段的数据准备

知识库刚上线时最尴尬:文档太少,检索效果差,用户用了几次觉得不好用就不用了。破解冷启动的关键是先聚焦一个高频场景,把这个场景的文档准备充分,做到这个场景下体验很好。用户在这个场景下建立了信任,才会尝试其他场景。

文档准备不是越多越好,而是要覆盖高频问题。我一般会先收集这个场景下最常见的五十到一百个问题,然后针对每个问题准备对应的文档。这样能保证冷启动阶段就有不错的命中率。等用户量上来了,再根据实际提问补充文档。

6.4 模型更新的平滑过渡

Embedding 模型或生成模型更新时,不能直接替换,否则行为会突变。我的做法是双跑一段时间,新旧模型同时在线,流量按比例分配。对比两个模型的表现,确认新模型不差于旧模型后再全量切换。这个过程中索引要重建,因为新旧模型的向量空间不一样。

重建索引时要注意不能停服。我的做法是新建一个索引,后台慢慢灌数据,灌完之后把流量切到新索引,旧索引保留一段时间做回滚。整个过程用户无感知。这个方案需要向量库支持多索引,选型时要考虑这个能力。

7. 我在实际项目中的几点体会

做生产级知识库和 Agent 网关这几年,最大的体会是:技术选型不是最重要的,工程细节才是。模型换来换去,效果差异可能就几个百分点;但分块策略、检索融合、上下文组装这些细节做不好,效果直接打对折。我见过太多团队在模型选型上纠结几个月,结果上线后发现是分块出了问题。

另一个体会是不要追求一步到位。生产系统是演进出来的,不是设计出来的。先跑通最小闭环,再逐步优化。我一般会先做一个能用的版本,上线收集真实反馈,然后按反馈优先级迭代。这样每一步都有明确的目标,不会陷入过度设计。

最后一个体会是可观测性要尽早做。没有日志和指标,出了问题只能猜。我现在的习惯是,任何新功能上线前,先把日志和监控埋点做好。这样出问题时能快速定位,优化时也有数据支撑。这个习惯帮我省了大量排查时间。

后续如果还要扩展,我会考虑把检索层做成插件化架构,支持多种检索策略动态切换。不同场景可能需要不同的检索策略,插件化能让切换成本降到最低。另外就是探索多模态检索,支持图片和表格的语义检索,这对技术文档场景特别有价值。

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

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

立即咨询