很多人第一次接 DeepSeek RAG,跑通一个 demo 就以为完事了,结果一上量就遇到超时、限流、检索回来的上下文塞不进窗口、答案对不上还查不出是哪一步坏的。这篇不讲协议兼容层,也不讲 RAG 原理,只讲工程落地:请求怎么发、参数怎么设、失败了怎么退、日志记什么、怎么用一小批用例判断改动到底有没有变好。
很多人第一次接 DeepSeek RAG,跑通一个 demo 就以为完事了,结果一上量就遇到超时、限流、检索回来的上下文塞不进窗口、答案对不上还查不出是哪一步坏的。这篇不讲协议兼容层,也不讲 RAG 原理,只讲工程落地:请求怎么发、参数怎么设、失败了怎么退、日志记什么、怎么用一小批用例判断改动到底有没有变好。
下面按「一次问答实际发生了几次网络往返」这条主线拆开讲,每一步都给出可执行动作和判断标准,涉及价格、窗口大小、限流额度这类会变的数字,一律让你到对应的站内页面去核对当时的数字,不写死在文章里。
一次 RAG 请求到底发了几次调用
先想清楚链路:嵌入模型把用户问题向量化,向量库按向量召回若干片段,可选地过一次重排模型,再把片段拼进上下文交给 DeepSeek 生成回答。也就是说,用户眼里的「一次问答」,在系统里通常是两到三次网络往返,任何一段慢或失败,用户看到的表现都是「卡住」或「答不出来」。
- 嵌入调用
- 把问题转成向量,用的是嵌入模型,和生成模型是两套 Key、两套限流,别混在一个超时预算里
- 向量召回
- 拿向量去库里查,返回的是片段和距离,这一步慢通常是索引或查询写法的问题
- 重排(可选)
- 对召回结果再排序,质量收益明显但多一次调用和延迟,看场景决定要不要加
- 生成调用
- system 放约束、user 放检索片段加原始问题,片段要带来源标识以便回答里引用
动手前先把三个组件选型对齐:到 /stacks/vector-dbs 挑一个向量库,到 /embeddings 看嵌入模型的口径,再回 /official-api/providers/deepseek 核对当前的接口规格、窗口和价格。选型定下来再写代码,不然中途换库换模型,前面的拼接逻辑和评测基线都要重做。
检索片段怎么塞才不爆窗口、不白花钱
常见的坑是「能召回多少就塞多少」,结果上下文爆窗口直接报错,或者塞了一大堆低相关片段,答案反而被带偏,单价和首字延迟一起上去。正确顺序是先定预算、再定条数。
- 算可用额度拿生成模型的输入上限,扣掉 system 提示词和用户原始问题的 token,剩下的才是片段能用的额度;具体上限去 /official-api/providers/deepseek 核对当时的数字
- 按相关性截断片段从高到低排,累加到额度用满为止,超出的一条都不要,宁少勿滥
- 保留可追溯字段每条片段带上标题、来源、时间戳,方便让回答带出处,也方便事后追是哪条资料答错的
- 当场算成本拼完的上下文大概多少 token、大概花多少,去 /tools/token-price-calculator 现场算一遍,别凭感觉
判断标准很简单:如果减少片段条数之后答案质量没明显下降,那说明之前塞进去的大部分是噪音;反过来,如果减到某条之后开始答错,那条就是关键资料,值得单独提权重。什么情况下不适用:如果任务本身要求覆盖式汇总(比如「把这几份文档的要点都列出来」),就不能一味截断,这时应该分多次调用再合并,而不是硬塞进一个窗口。
超时怎么分档:把三次往返拆开设
用一个全局超时兜住整条链,是最容易埋雷的做法:你根本不知道是检索慢还是生成慢,调参也无从下手。把三段拆开,各自设值,日志里各记各的耗时。
| 链路分段 | 超时怎么设 | 重试值不值得 |
|---|---|---|
| 向量化 | 单次短超时即可,这段通常最快 | 值得,多半是网络抖动 |
| 向量库检索 | 按索引规模设,冷启动可略放宽 | 意义有限,多半是查询写法或索引问题 |
| 生成(非流式) | 整条链里最慢的一段,单独放宽 | 值得,但要配上退避和总时长封顶 |
| 生成(流式) | 分别设首字超时和整体超时两个值 | 首字超时不适合盲目重试,先看是不是上游排队 |
设完别凭感觉填秒数,拿 /tools/speed-test 量一遍自己的延迟基线,把每段的 P50 和尾部延迟记下来,超时值设在基线之上留出余量,而不是拍脑袋写一个整数。
并发、限流与降级路径
按账号实际额度定并发上限,本地再压一层信号量或队列,避免瞬时打满被限流。额度是动态的,去 /official-api/providers/deepseek 和相关页面核对当时的限制,不要照抄别人博客里的数字。
- 遇到限流类错误走指数退避加重试,退避要有上限和总时长封顶,否则请求会堆在队列里越积越久
- 降级路径必须提前设计并写进代码:生成失败时是回退到只返回检索原文,还是换一个备用模型,还是直接报错让上层兜,三选一
- 换模型或换源之后一定要重新验证连通性,别默认改了配置就能用
- 把被限流、被降级的次数打点上报,这是判断容量够不够的最直接依据
日志该记哪几个字段,才能复盘是谁的锅
出问题时最怕的是「只知道答错了,不知道错在哪一步」。每次问答至少要有一条能串起来的记录,字段不多,但要够用。
| 字段 | 记什么 | 用来干什么 |
|---|---|---|
| 请求 id | 一次问答的唯一标识 | 把三段调用的日志串起来 |
| 召回片段 id | 命中的片段标识列表 | 复盘是不是检索没召回到正确资料 |
| 上下文 token 量 | 拼进生成调用的实际 token 数 | 定位是不是塞太多导致贵或超窗 |
| 各段耗时 | 向量化、检索、生成分开记 | 区分是检索拖慢还是生成拖慢 |
| 最终状态 | 成功、降级、报错及错误类型 | 统计失败率和降级率 |
注意别把完整原文和 API Key 写进日志,检索片段存 id 就够,需要还原时再去库里取。把慢请求和贵请求单独打点,复盘时先看这两类,多数问题都能快速收敛到具体某一段。
改动之后到底变好没有:一条小评测
没有基准就没法判断,「感觉好点了」不是结论。准备一小批固定用例,覆盖有答案、无答案、需要引用这三种情况,每次改检索参数或提示词就跑一遍,把当前版本的数字先记下来当基线。
- 固定用例集覆盖有答案、无答案、需要引用三类,数量不用多,但每次跑的是同一批
- 两个维度判定答案对不对(人工或半自动判定),以及单次问答的 token 消耗和耗时有没有变;前者看质量,后者看代价
- 先记基线再改跑之前把当前版本的两个维度数字记下来,改完再跑同一批用例对比
- 把改动做成配置检索参数、提示词、模型选择都当成可回退的配置项,而不是改死的代码,出问题能一键回滚
什么情况下不适用:如果用例集本身在变,或者每次都换一批问题,那这个评测就没有可比性,得先把用例固定下来再谈优化。
首次接入的检查顺序
第一次接入别一上来就串整条链,按依赖顺序一层一层验证,出问题才容易定位。下面这个顺序可以直接照着做。
- 单跑向量化确认嵌入模型通、Key 有效、返回向量维度符合预期
- 单跑向量库查询用固定向量或真实问题查一次,看召回内容是否合理,不合理就先调索引和查询写法
- 接上生成前两步都通之后,再把片段拼进上下文调 DeepSeek,确认能基于片段作答并带出处
- 逐步加工程件超时、重试、日志一次只加一层,每加一层复跑一遍固定用例,确认没有回归
- 量基线再上量跑出耗时和 token 基线,测一遍限流和降级路径,确认被限流时系统不会整体卡死
涉及价格、窗口、限流额度的具体数字,全部以站内页面当时显示为准:生成侧看 /official-api/providers/deepseek,成本估算用 /tools/token-price-calculator,延迟基线用 /tools/speed-test,选型参考 /stacks/vector-dbs 和 /embeddings。把这几页当作你接入过程中的检查锚点,比记死数字靠谱。
下一步先去看看目前各家中转站的连通与稳定性表现,再决定你的 RAG 链路要不要配一个备用源;顺带用价格追踪页核对一下当时的调用成本口径。→常见问题
DeepSeek 能不能直接喂文档做检索,不用自己搭向量库?
接 RAG 通常还是走「自己向量化 + 自己召回 + 调补全接口」这条链路,模型侧只负责最后一步生成。文件上传这类能力有它自己的适用范围和限制,别把它当成通用检索方案,具体看 /official-api/providers/deepseek 上的接口说明再决定。
一次 RAG 问答该按几次调用算成本?
至少要算三段:问题向量化、向量库检索、生成。前两段是嵌入和检索的账,第三段才是 DeepSeek 的 token 账,两者单价口径不同,不能混着估。按你实际每次拼进去的片段量算一遍更准,可以去 /tools/model-cost-calculator 试。
检索回来的片段很长,截断到多少条合适?
没有通用答案,取决于你给生成模型留了多少输入额度以及片段本身多长。做法是先定输入预算,再按相关性从高到低填,填满为止,而不是固定条数。条数写死会在资料长度变化时突然爆窗口或突然不够用。
生成阶段报超时,先怀疑模型还是先怀疑检索?
先把三段耗时分开打点。如果检索段就已经很慢,问题多半在向量库查询或索引上;如果只有生成段慢,再看是不是片段太长导致输入过大,或者当前时段服务本身拥塞。不分段就只能瞎猜。
小团队没有测试资源,评测怎么做?
不用做完整评测体系,准备一二十条固定问题即可,覆盖答得出、答不出、该带引用三类。每次改检索或提示词就跑一遍,人工扫一眼对错,同时记下耗时和 token。够用来判断这次改动是进步还是退步。