
最近一个月我大部分时间都花在“生产级知识库”和“Agent 网关”这两件事上。先直接说结果知识库检索召回率从 0.53 提到 0.81RAG 问答的准确率从 61% 涨到 78%Agent 网关的 P95 延迟从 2800ms 降到 900ms多 Agent 调用的错误率从 4.7% 压到 0.6% 以下。整个过程没用任何新模型纯粹是对现有组件的调优和架构逻辑的重新整理。如果你也在维护 RAG 系统或者多 Agent 平台这篇文章应该能省你不少试错时间。为什么把这两件事放在一起说因为我发现知识库召回不准Agent 下游所有工具调用都会跟着跑偏而网关不稳知识库的检索请求直接超时整个 Agent 就断掉。这俩看着是两个模块本质上是同一个系统稳定性的两面。优化完我最大的感受是生产级这三个字难的不是做出一个能用的 demo而是让它在高并发、脏数据、模型抖动面前依然能稳定输出。这篇就按我的实际优化路线来复盘把思路、参数、踩坑记录都放出来。1. 项目拆解生产级知识库和 Agent 网关到底在优化什么1.1 知识库的“三宗罪”网上搜“知识库”能看到一堆词rag知识库、llm wiki知识库、obsidian知识库搭建、dify知识库流水线……工具五花八门但生产环境里的问题非常集中我总结成三个典型症状第一个是切分粗糙。早期图省事按字符数硬切一篇 PDF 导出成 Markdown 后用 1000 字符一刀切下去段落、表格、引用关系被拦腰斩断。结果就是用户问“上季度营收是多少”检索系统召回的内容是“上季度营收”这个标题和下一段完全无关的表格碎片模型拿着碎片只能编。第二个是向量召回单薄。只靠 embedding 做余弦相似度遇到专业术语、产品代号、中英文混写向量空间里距离很近的候选根本不对。比如知识库里写的是“SKU-2048B”用户问的是“那款蓝色高配版”字面上零重合但语义上同一个东西。纯向量检索在这种场景下经常把不相关的文档排到前面。第三个是缺少元数据过滤。知识库一旦有多个业务线、多个文档版本检索时不按租户、版本、状态过滤召回结果就会混入过期内容甚至别的部门的数据。这在生产环境不只是效果问题还是合规风险。1.2 网关才是那个隐藏瓶颈再说 Agent 网关。这个“网关”跟我们网上常刷到的网络网关设备、反垃圾邮件网关、电信猫超级密码完全不是一回事我这里说的是 LLM 语义网关是 Agent 调用模型、工具、知识库之前的统一入口。一开始我以为网关就是个反向代理把请求转发到不同模型供应商再返回结果。真正被生产环境教育之后才发现网关要干的事至少包括协议适配OpenAI 格式转内部协议、多模型路由、上下文组装、工具调用编排、限流熔断、费用审计、敏感词拦截还有多 Agent 之间的隔离。最让我头疼的是上下文组装。同一个用户在一次会话里既问了知识库的问题又让 Agent 调了内部 API还让它把结果存到数据库。网关要把知识库返回的文档片段、工具返回的结构化数据、历史对话记录统一拼装成模型可用的上下文而且 Token 预算有限。这块如果设计得不好会话一长Token 直接爆炸成本翻倍不说模型的响应质量还会明显下降。1.3 优化范围与优先级排序我梳理了一张优化清单按照投入产出比和风险等级排序优化项现状目标优先级文档切分策略固定字符切分语义割裂严重按结构语义混合切分高召回策略纯向量检索向量BM25混合召回高重排序无Top-K直接送模型引入reranker精排高元数据过滤未启用多租户状态过滤高网关路由静态规则动态意图路由中网关上下文全量历史拼接Token预算压缩高网关容错无超时控制超时重试降级高这个优先级排序的思路很简单知识库的召回质量是地基地基不牢后面网关再稳也没用。所以整个优化周期我先啃知识库再动网关。2. 知识库优化召回质量不是换个模型就能解决的2.1 切分策略别再按固定字符数硬切先把最常见的坑堵上。固定字符切分fixed-size chunking最大的问题是它完全无视文档结构。生产环境里的知识库文档大多是 Markdown、PDF、Word 转出来的 HTML天然带有标题、段落、列表、表格结构。按结构切分是更合理的起点。我最后采用的方案是混合切分可以细分为三层结构锚点切分优先按 Markdown 的标题层级H1、H2、H3切出语义完整的段落。语义边界检测对没有标题的纯文本用句号、问号、换行符做边界检测配合滑动窗口避免切断一个完整语义单元。父子文档分块小段落用于精确检索比如 256512 token同时保留它所属的大段落比如 1500 token作为上下文补充。这样既保证召回精度又给模型足够的上下文理解空间。参数上我最终选了 chunk_size750 token、overlap150 token。为什么是 750这个值是我测出来的字数太少上下文信息不足重排序的效果不稳定字数太多单条 chunk 的向量表示被稀释检索时噪声大而且送入模型的 Token 成本上升。150 token 的 overlap 是为了覆盖段落之间的过渡内容避免一句话被两个 chunk 边界各切一半。补充一个细节表格类内容要单独处理不能直接切成文本。我把每张表格转成一个带表头描述的 KV 结构比如“商品价格表 | 商品名称 → 价格、库存”检索到表格时整体召回。直接切表格文本模型根本看不懂。2.2 召回策略向量加关键词混合别只押一条路纯向量召回的问题我在前面说过了字面不重叠但语义相关的场景下容易漏召。生产环境里另一类问题是专业术语的简写和全称比如“检索增强生成”和“RAG”向量模型如果不是专门微调过的这两个词的向量距离其实不近。所以我把召回改成了混合召回向量召回跑一路BM25 关键词召回跑一路两路结果做加权融合然后统一进重排序。BM25 本身就是经典的信息检索算法对精确术语匹配非常有效实现起来也快Elasticsearch 里直接就有。融合公式不复杂但要注意两类分数不在同一个尺度上。向量余弦相似度一般在 01 之间BM25 的分数却可能到十几甚至更高直接相加的话 BM25 会主导结果。我做了一步归一化向量分先转成 01 的 min-max 归一化分数BM25 分也做同样的归一化然后按权重 0.6向量 0.4关键词混合最后取 Top-50 进入重排序。这里踩过的坑是权重不是拍脑袋定的我是拿标注好的测试集跑出来的。一批历史问题 人工标注正确答案分别测了 0.5/0.5、0.6/0.4、0.7/0.3 三组权重0.6/0.4 在“术语精确匹配”和“语义泛化”之间最均衡。你不能反过来先定权重再测效果那只会自我感觉良好。2.3 重排序Top-50 到 Top-5 的关键一跳召回 50 条候选如果全部塞给模型Token 成本高不说模型还容易抓错重点。所以我引入了独立的 reranker 模型做精排。方案对比下来我选了 bge-reranker-base 作为主力它对付专业领域的中文/英文混合场景效果不错单条推理延迟能控制在 30ms 左右。输入是“用户问题 候选文档”输出一个相关性分数再按分数取 Top-5 作为最终上下文。重排序这个环节有一个容易被忽略的细节reranker 的输入长度有限制我用的 base 版最长 512 token。之前我把一整篇长文档塞进去超过长度直接被截断导致长文档永远排在后面。解决办法是重排序时只取每篇文档的前 256 token 加上命中的片段附近 256 token这样既不会超长又能保留关键信息。2.4 元数据过滤生产环境多租户隔离的刚需知识库到了生产环境一定是多人、多业务线、多版本共存的元数据过滤属于必须做但是大家经常最后才补的一块。我在每个文档入库时就打上了固定的元数据标签包括tenant_id租户/业务线doc_status当前版本 / 历史归档doc_type手册 / FAQ / 报表last_updated最后更新时间检索时把租户和状态条件直接下推到查询语句里保证任何一次召回都不会跨租户、不会把归档版本当现行版本。这听起来像常识但很多系统的检索请求确实是在 ES 或向量库外面过滤这样既慢又容易漏。这里有个性能细节元数据过滤要在向量检索和 BM25 检索的索引层面完成而不是召回完再过滤。道理很简单比如按租户过滤后命中只有 1000 条你先全量算相似度再过滤等于浪费了 90% 的计算量。生产环境数据到百万级以后先过滤还是后过滤延迟能差出好几倍。3. Agent 网关优化路由、上下文与容错设计3.1 网关不该只是一个反向代理网关这层我踩的坑比知识库多其核心问题是容易把它做成“转发器”。如果网关只做 HTTP 转发那 Agent 之间的复杂交互逻辑就全部堆在业务代码里到后面根本维护不动。生产级的 Agent 网关至少要具备四层能力。第一层是协议接入接收 OpenAI 兼容的 SSE 流式请求转换成内部统一的 Message 结构这样上游不绑定特定模型供应商。第二层是模型路由同一个请求在不同模型之间做分发类似 LLM 网关的做法简单问题走便宜的小模型复杂推理走强模型可以省不少成本。第三层是工具编排把用户请求拆解成模型调用、知识库检索、外部 API 调用等子任务按依赖关系调度。第四层是治理能力包括限流、鉴权、审计、敏感词汇拦截、调用链追踪。如果你现在的网关连这四层能力的影子都没有别急着一步到位。先把协议适配和路由做了工具编排和治理逐步迭代一次性重写到位的风险太高。3.2 动态路由让请求找到对的 Agent早期我用静态规则路由比如 URL 里带/search 就走搜索 Agent带/qa 就走问答 Agent。这在 Agent 只有两三个的时候没问题到后面十几个 Agent 混在一个网关后面静态规则就变成了一堆 if-else 的垃圾场。我改成了三层路由设计按优先级从高到低显式指定 语义意图识别 兜底规则。显式指定是说业务侧调用时主动带上目标 Agent 的 ID这是最高优先级的必须支持。语义意图识别是网关自己接一个快速分类模型把用户请求分类到对应的 Agent 类别这个分类模型用轻量级模型就够了成本低延迟也能控制在 100ms 以内。兜底规则是维护一个关键词表比如用户问题里带“订单”就走订单 Agent这是最后一道保障防止分类模型走眼。三层路由组合下来路由准确率从静态规则的 74% 提到了 92%剩下的 8% 基本就是需要人工处理的长尾问题。关于路由表的更新节奏我建议意图识别模型固定两周重训一次关键词表每天增量同步这样既能跟上业务变化又不会为了一个临时需求频繁重训模型。3.3 Token 预算与上下文压缩控制成本的关键网关层最容易失控的是 Token 消耗。一次对话里历史记录会积累知识库召回的文档会占 Token工具返回的结构化数据也会占 Token。全部塞进去用不了几个来回单次请求的 Token 数就冲到几千上万费用直接起飞。我的做法是给每次请求设一个明确的 Token 预算按照比例分配上下文组成预算占比说明系统提示词10%固定内容控制在预算以内历史对话摘要20%旧会话压缩成摘要当前用户输入20%本轮的问题和附带信息知识库/工具结果35%召回片段做硬截断模型输出预留15%给生成结果留的余量这个比例不是绝对的但“输出预留”必须留够不然模型生成到一半被 Token 截断整个回复缺胳膊少腿。预算实现上我用一个 Token 计数函数实时统计每当内容累加超过预算就触发压缩逻辑。压缩逻辑分两级历史对话超过阈值就做摘要压缩用模型把之前的对话提炼成一段话知识库召回片段超过阈值就做硬截断只保留每个片段的前 256 token。实测下来单次请求的 Token 数降了 46%而回答质量基本没有下降。3.4 容错设计超时、重试、降级是三类事容错是我之前欠账最多的地方。早期网关没有超时控制模型供应商一抖动请求全部挂着上游服务一个接一个被拖垮。这次的网关容错我梳理成三层第一层是超时控制。每个下游调用单独设超时时间模型调用 60s知识库检索 3s工具 API 5s。超时的请求直接返回可读的兜底话术不无限等待。超时时间必须通过压测定太短容易误杀正常请求太长起不到保护作用。第二层是重试策略。只有幂等请求才可以自动重试而且必须有退避机制。我用的是指数退避加抖动第一次失败等 200ms 重试第二次等 400ms最多 3 次。如果下游是模型供应商重试还会换一个模型实例防止单点故障反复命中。第三层是降级方案。模型接口全挂的时候网关返回预设的提示同时把故障信息写进监控看板。知识库检索失败时网关可以降级成纯关键词回复而不是直接报错。这些降级策略要在 30 分钟内就能改配置生效不能每家下游挂了都去改代码。这里有一个容易忽略的配套点必须记录每次调用的 trace_id把网关收到请求、转发模型、调用工具、返回结果的时间线全串起来。没有这个出问题之后你只能靠猜。我后面排查故障时这个 trace 日志至少省了我一半的时间。4. 实测数据与关键参数记录4.1 知识库端到端效果对比知识库的优化效果我用三类指标评估召回率Recall10、命中准确率Top-1 正确的比例、以及端到端的问答准确率用标注集人工评估。优化前后的对比如下指标优化前优化后Recall100.530.81Top-1 命中准确率38%61%RAG 问答准确率61%78%单次检索 P95 延迟420ms560ms注意最后一行检索延迟其实是上升的因为加了 BM25 混合和重排序两步这是预期的代价。真实收益在问答准确率涨了 17 个百分点换算成生产环境的实际体验就是用户明显觉得“回答靠谱多了”。4.2 网关性能对比网关的压测数据我记录了一组测试条件是 32 并发、模拟混合流量包含问答、工具调用、知识库检索持续压测 15 分钟指标优化前优化后P95 延迟2800ms900msP99 延迟5100ms1800ms错误率4.7%0.6%Token 消耗/会话约 4800约 2600Token 消耗降了 46% 主要来自上下文压缩那一步。错误率降下来主要归功于超时和重试没加这些之前下游一抖就是一批 504加了之后大部分故障在网关层就被吸收了。4.3 值得保存的参数表我把关键参数整理成一张表可以直接抄作业但记得用你自己环境的测试集重新校准别直接套类别参数取值备注文档切分chunk_size750 token按结构锚点滑动窗口文档切分overlap150 token覆盖跨段落内容混合召回向量:关键词权重0.6:0.4用标注集测试后确定混合召回召回数量Top-50进入重排序的候选数重排序模型bge-reranker-base输入截取 512 token重排序最终保留Top-5送入模型的片段数路由意图模型轻量分类模型每两周重训一次上下文Token 预算分配10/20/20/35/15按系统/历史/输入/结果/输出容错超时模型60s/检索3s/工具5s分别设置容错重试最多3次,指数退避抖动仅幂等请求5. 排查实录三个让我熬夜的生产故障5.1 故障一召回返回“请让我查一下”而不是内容症状生产环境里用户问具体参数模型回答“请让我查一下相关资料后确认”而不是给出答案。排查 trace 后发现知识库确实返回了 5 条片段但片段内容全都是文档的小标题正文内容没被召回。根因小标题被结构切分单独切成了 chunk重排序时因为标题和问题高度相似都是短文本分数很高排到了前面正文反而被挤掉了。标题类 chunk 的分数虚高这是一个很阴的坑。修复方式入库阶段把标题 chunk 和正文 chunk 合并存储不让标题成为独立检索单元并关闭标题类片段的独立召回。这让我意识到生产级知识库的问题往往不是某一个环节坏了而是环节之间的配合出了问题。5.2 故障二Agent 网关超时导致批量任务失败症状当天下午批量任务开始后 10 分钟任务失败率飙升到 20%。排查发现是某个第三方工具 API 响应变慢从平均 300ms 变成 12s网关没有超时控制请求全部排队积压最终雪崩。根因网关没有任何保护机制最基础的超时都没设。小流量的时候没事一旦某个下游抖动整个网关就被拖死。修复方式给所有下游调用配置了分级超时并加了一个快速失败的熔断逻辑某个下游 5 分钟内错误率超过 30%直接熔断 1 分钟不再转发请求1 分钟后再试探性恢复。之后批量任务稳定跑了一周错误率维持在 0.5% 以下。5.3 故障三多 Agent 共用网关时上下文串扰症状用户 A 在问订单问题系统回答里却出现了另一个用户的隐私信息。这个故障让我冷汗都下来了。根因Agent 网关的会话上下文是按内存里的 session_id 存的但我在一个分流节点把两个用户的请求错误映射到了同一个 session。这属于数据隔离的 bug如果不是测试环境碰巧复现真要出事故。修复方式会话隔离改成了双因子校验请求头里的 session_id 和用户的 user_id 必须同时匹配才能命中上下文任何一项不匹配就重新创建会话。同时加了审计日志每次会话切换都记录下来。这次之后我立了一个规矩凡是涉及多租户数据的功能上线前必须做隔离性测试用真实的多用户数据验证。5.4 常见问题速查表症状可能原因排查思路知识库答非所问切分切断语义 / 召回数量不足查召回片段看命中内容是否完整模型说“不知道”但库里有内容重排序把标题类片段排前检查 chunk 是否混入标题独立单元响应延迟突然变高下游 API 变慢 / 无超时控制看 trace 的瓶颈节点批量任务大规模失败单点故障引发雪崩检查熔断和重试策略会话内容串号上下文隔离失效检查 session_id 与 user_id 绑定逻辑Token 消耗异常高上下文无预算控制看单会话历史是否无限拼接召回结果混入过期数据缺少状态过滤检查元数据过滤是否在索引层执行6. 工具链选型与落地建议6.1 Dify 这类平台能用到什么程度网上关于 Dify 知识库流水线的资料很多我也用过。Dify 确实把知识库的管道做得很完整数据导入、切分、向量化、检索都能快速搭起来适合验证原型和中小规模场景。但这次优化之后我必须说一句反共识的话生产级知识库的瓶颈不在管道而在数据治理和检索策略。Dify 这类平台在可视化配置上很友好但遇到自定义切分逻辑、混合召回调权重、多租户隔离这类需求时定制开发成本反而比自研更高。它不是不能用而是要用在合适的场景。我的建议是知识量在几十万文档以下可以用 Dify 搭底子一旦涉及多业务线、复杂的权限模型、超大规模文档集还是要逐步上自研的检索服务。6.2 自研网关的灰度演进Agent 网关我建议自研但这种自研不要从零开始写基础框架而是基于成熟的 Web 框架和中间件做业务组装。网关这种组件的核心业务价值全在路由逻辑、上下文策略、容错策略上框架只占 20% 到 30% 的代码量。演进路径我推荐三步走。第一步先做一个最小可用的网关只有协议适配、转发、日志三个功能跑通流量。第二步加上路由、上下文压缩、超时重试。第三步引入熔断、降级、审计、动态配置中心。每一步都要有灰度不能一步到位否则出了故障根本定位不了是网关的问题还是业务的问题。6.3 本地知识库Obsidian 等的接入方式如果只是想把自己 Obsidian 里的笔记做成知识库接 RAG根本不需要搞那么重。Obsidian 的 Markdown 文件导出后按文件夹结构做简单的标题切分然后用现成的向量库做检索个人知识库完全够用。个人场景和企业级场景的本质区别在于数据规模和治理复杂度。个人知识库几十个文件纯向量召回就够了重排序都可以省掉。企业级知识库有权限模型、版本管理、多源异构数据、合规审计这些才是复杂度的大头。工具不该决定你的架构场景才决定。写在最后优化完之后的一点体会这套优化做完之后我的一个核心体会是生产级的 RAG 和 Agent 系统难点从来不在“能回答”而在“每次都稳定地回答”。知识库的切分、召回、重排网关的路由、上下文、容错每一个环节单独看都是可以接受的方案但串联在一起任何一个环节的偏差都会被放大成用户可感知的糟糕体验。第二个体会是所有参数必须用标注好的测试集来校准。我前面表格里的所有数值没有一个是靠经验直接定了上线就完事的全部跑过对比实验。这不是技术洁癖而是生产环境的特性决定了——你的文档、你的用户问题分布是独一无二的别人的参数只能当起跑线不能当终点。最后分享一个小技巧每次优化上线前固定抽 100 个历史真实问题和标注答案跑一遍完整的离线评测把准确率、召回率、Token 消耗记录到一个表里。坚持一段时间你会清楚地看到哪些优化是实际有效的哪些只是自己感觉有效。这套评测机制比任何玄学的“提示词调优”都管用。