很多人第一次写 RAG,向量库跑通了,一上真实数据就出问题:检索偶尔超时、并发一高就限流、答错时不知道是召回还是生成的问题。这篇不讲概念,只按工程顺序把要做的动作列出来,每一步都告诉你卡住时先查什么。数字和模型版本一律到站内页面看当时的口径。
很多人第一次写 RAG,向量库跑通了,一上真实数据就出问题:检索偶尔超时、并发一高就限流、答错时不知道是召回还是生成的问题。这篇不讲概念,只按工程顺序把要做的动作列出来,每一步都告诉你卡住时先查什么。
数字和模型版本一律到站内页面看当时的口径。你在代码注释里写死的那些数,过一阵就对不上,反而误导后面接手的人。下面按链路骨架、超时并发、降级、日志评测四段走,每段末尾都给了排查顺序。
先把链路骨架搭直,别急着调效果
一次提问先拆成固定几段:清洗后的文档进库、问题改写、召回、重排、拼上下文、生成、返回引用。每段之间用统一的中间结构传递,方便单独替换某一段而不动其他代码。
- 跑最小闭环先用一条真实问题端到端打通,只保留向量召回加生成,确认能出答案并带引用。
- 补重排与多路召回闭环稳定后再加,一次只动一处,每加一段都保留加之前的输出样本做对照。
- 存输入输出样本每一段各存一份输入输出,后面排查「答案不对」时能一眼定位是召回没拿到还是生成跑偏。
顺序反了会同时面对太多变量,调不动。查链路位置和名词口径可以对照 /glossary 里的基础概念词条,别自己另起一套叫法,否则和同事沟通时对不上。
超时、重试与并发:按段分别设,不要一个值套到底
串行链路里任何一段拖长都会顶到上游网关。先量出请求进入时间与响应时间,算出各段延迟基线,再定超时值;没有基线就设超时,只能靠猜,一上线就被误杀。
| 环节 | 超时怎么定 | 重试要不要开 |
|---|---|---|
| 向量库查询 | 按历史 P95 延迟上浮一档 | 可开,确认不会重复计费或重复写入 |
| 重排模型 | 按批量大小分档设,别一个值套所有批量 | 可开,限制次数与总预算 |
| 生成模型 | 按上下文长度分档,长上下文给更宽裕的值 | 可开,但要在总预算内统一计次 |
- 并发按最窄的那一环设上限,通常不是你的应用,而是向量库连接数、嵌入接口或生成接口的限流;超过就排队,别硬冲。
- 限流报错不要靠无脑退避硬扛,按失败比例触发降级,分档做法参考站内已有的限流与退避清单。
- 重试要确认幂等性:查询类一般安全,写入类如果不带去重键,重试可能产生重复文档。
- 给整条链路设一个总预算,单段重试次数再多也不能让总耗时突破它,否则用户早走了。
降级路径:答不出比答错好
降级的目标是让系统在部分组件不可用时仍能给出可用的回答,而不是硬撑着输出一个可能错的答案。准备三档:重排超时就退回纯向量召回;生成接口不可用就换备用通道;全部不可用就返回「暂无法回答加可看的原文片段」。
| 触发条件 | 降级动作 | 返回给用户什么 |
|---|---|---|
| 重排连续失败超阈值 | 退回纯向量召回,跳过重排 | 正常答案,标注召回方式 |
| 生成接口不可用 | 切备用通道 | 答案加「备用通道」标记 |
| 全部不可用 | 返回原文片段 | 暂无法回答加可查阅的片段 |
触发条件要写进代码而不是靠人临时判断,否则高峰期没人来得及切。降级时要标记返回值来源,让前端能显示「这条是降级结果」,避免用户把残缺答案当权威答案。备用通道的能力差异要提前验证:换模型后提示词里的格式要求可能不再成立,先跑一遍小样本用例再挂上去。
日志与评测:让每次答错都能复盘
一次请求至少记这些字段:问题原文、召回命中的文档 ID 与分数、拼进上下文的片段长度、实际用的模型与通道、各段耗时、token 用量、是否降级。字段够全,复盘时才不用靠回忆。
- 建一条固定的小评测集,用几十条真实问题加参考答案要点,改分块、换嵌入模型、换生成模型时都跑一遍,只看通过率有没有掉。
- 上线前用评测集确定「够用」的线,别用单条对话的观感做决策。
- 线上再用量榜与监控页核对延迟与错误率的整体水位,看是否偏离基线。
- 分块策略改过之后,旧评测集要重新确认还适用,否则你会拿过期的标准判断新链路。
词条、模型规格和价格口径变化快,写文档时引用站内页面而不是抄数字进代码注释。想对照不同嵌入模型与生成模型的规格,去 /models 看当时的口径;想确认调用成本再定预算,去 /price-watch 核对当时的数字。
下一步把上面四段拆成 checklist 逐项过一遍,再对照站内的模型规格与价格口径确定你的技术选型。→常见问题
RAG 一定要用重排吗,不用行不行?
不一定。数据量小、问题表达和文档用词高度一致时,纯向量召回就够。只有当「检索回来的前几条经常不含答案」时才值得加重排,而且要先确认是召回问题而非分块问题,否则重排只是把错的东西重新排一遍。
检索偶尔超时,先改哪里?
先测各段延迟,确认是向量库还是生成接口拖慢;再检查并发是否超过最窄环节的上限;最后才考虑降超时值。盲目调小超时会推高失败率,调大又会顶到上游网关限制,得先有延迟基线数据再动手。
为什么本地跑得好,上线就答错?
多半是并发和降级路径没测过:线上触发限流后静默降级,返回的是残缺上下文。先在日志里区分正常返回和降级返回,再复现一次高并发场景,看降级条件是不是设得太松。