# RAG 延迟优化：检索到 TTFT 全链路拆解

> 来源：[RAG 为什么越用越慢？从检索、上下文到 TTFT 讲清楚](https://blog.csdn.net/xx_nm98/article/details/161121400)

## 核心问题

RAG（Retrieval-Augmented Generation，检索增强生成）应用上线初期轻快，但随文档量、权限复杂度、检索链路增加，体验变差：提交问题后空等几秒（首 token 慢），后续输出也一段段挤出来（token 间隔不稳）。

**关键判断**：RAG 变慢不能简单归因于"模型慢"或"向量库慢"，它是一条完整链路——query 改写 → embedding → 向量检索 → rerank → 上下文拼接 → LLM prefill → decode → KV Cache 管理，每一段都可能推高延迟。需要拆开看，而不是当成"检索模块 + 大模型"两个黑盒。

## 两个关键指标

- **TTFT（Time To First Token）**：决定用户等多久才看到第一段输出。RAG 最容易先拉高这个指标，因为生成第一个 token 前要先把检索到的内容塞进 prompt，模型必须先处理这一大段上下文（Prefill 阶段）。
- **ITL（Inter-Token Latency）**：决定后续 token 流出来的间隔，受 decode 阶段的显存带宽、KV Cache 管理影响。

## RAG 的初衷

Lewis 等人 2020 年提出 RAG：模型参数里存的知识难更新、难溯源，也不一定覆盖企业自己的文档，所以让模型生成前先检索相关文档，把预训练生成模型和外部非参数记忆结合起来。这个思路今天依然成立（企业知识库、客服系统、代码助手都需要），但 RAG 一旦进生产系统，就从 NLP 问题变成了端到端系统问题。

## 三层慢的来源

### 第一层：检索链路变长

最简单的 RAG：问题 → embedding → 向量库 top-k 检索 → chunk 拼进 prompt → 模型回答。但生产系统为了提高准确率会不断加组件：

- **query rewrite**：改写问题使其更适合检索
- **hybrid search**：向量检索 + 关键词检索
- **reranker**：对候选 chunk 重新排序
- **metadata filter**：按时间/权限/部门/文档类型过滤
- **contextual compression**：只保留与问题相关的句子
- **citation checker**：检查答案能否回溯到来源

每加一个模型调用、一次 rerank、一次跨服务请求，端到端延迟都会增加。**工程判断**：如果慢主要发生在 LLM 调用之前，优化模型没用，应先查 retrieval latency、rerank latency、外部服务调用和权限过滤。

### 第二层：上下文变长，TTFT 被 Prefill 拉高

检索本身可能很快，但拼进 prompt 的内容太多。LLM 生成第一个 token 前必须先处理完整输入，这个阶段叫 **Prefill**（DistServe 论文：TTFT 对应 prefill 阶段，decode 阶段对应 TPOT/ITL）。prompt 从 2K token 扩到 20K token，模型在第一个 token 出来前就要先处理这 20K token。

"多召回一点总没错"在 RAG 里是危险的假设，会带来三类成本：
1. 输入 token 增加，Prefill 变慢
2. 相关信息被埋在中间，模型不一定用得好
3. 无关信息干扰答案，质量反而下降

**Lost in the Middle** 研究结论：模型在长上下文里不总能稳定使用所有信息，相关信息在开头或结尾时表现更好，落在中间时性能明显下降。→ 上下文不是越长越好，排序和裁剪同样重要。

### 第三层：KV Cache 让长上下文影响并发

模型生成时会缓存历史 token 在每层 attention 的 Key/Value（**KV Cache**），后续 token 复用避免重复计算。vLLM/PagedAttention 论文指出：KV Cache 很大且动态增长，管理不好会因碎片和重复占用浪费显存，限制 batch size。

RAG 往往先塞一大段检索上下文 → 上下文越长，Prefill 后留下的 KV Cache 越大。用户端体感是"回答变慢"，服务端实际是"显存被缓存占住，并发被挤压"。

**RAGCache** 论文：知识注入带来长序列生成，导致高计算和内存成本；通过缓存检索知识的中间状态优化，实验报告 TTFT 最多降低 4 倍、吞吐最多提升 2.1 倍。说明 RAG 性能瓶颈不只是检索，还包括检索内容进入 LLM 后的缓存和推理成本。

## 排查顺序（变慢时先做什么，而不是先换模型）

1. **拆端到端耗时**：把一次请求拆成 query rewrite / embedding / retrieval / rerank / context build / LLM TTFT / LLM ITL，没有这些指标讨论"RAG 慢"就是猜测。
2. **看 top-k 和 token budget**：不只记录召回多少文档，要记录最终塞进 prompt 的 token 数——很多系统 top-k 不高，但 chunk 太大，上下文照样膨胀。
3. **看相关信息的位置**：呼应 Lost in the Middle，rerank 后要把最关键证据放到模型容易利用的位置，删除弱相关 chunk。
4. **做检索质量评估**：CRAG 提醒 RAG 依赖检索文档相关性，检索错了模型会基于错误材料生成；RECOMP 强调压缩和选择性增强——无关或无增益内容应被压缩甚至返回空内容。
5. **看缓存和复用**：重复问题、稳定文档、热门知识块都可能有缓存空间。RAGCache、PagedAttention 说明缓存是高并发 RAG 的基础设施，不是锦上添花。

## 排查清单

| 症状 | 先看指标 | 可能原因 | 优化方向 |
|---|---|---|---|
| 用户提交后空等 | TTFT | prompt 太长、chunk 太多、Prefill 成本高 | 减少上下文、压缩 chunk、缓存前缀 |
| 检索前就慢 | rewrite / embedding latency | 多次模型调用、embedding 服务慢 | 合并步骤、缓存 query、异步化 |
| 检索后还慢 | rerank latency | 候选太多、cross-encoder 太重 | 先粗排再精排、降低候选数 |
| 答案质量变差 | recall / faithfulness | 无关 chunk 干扰、信息在中间 | rerank、裁剪、证据重排 |
| 首 token 快但输出慢 | ITL | Decode 受内存带宽/KV Cache 影响 | 量化、PagedAttention、优化 serving |
| 并发上不去 | batch size / KV memory | 长上下文缓存占显存 | 限制 token budget、KV cache 管理 |

## 结论

长上下文模型的出现不会让 RAG 消失，但"能读更多"不代表"读得更快、更便宜、更稳定"，也不代表模型能在大量弱相关材料中自动抓住关键证据。

真正稳定的 RAG 系统目标不是"尽量多召回"，而是**用最少、最准、位置最好的上下文，让模型回答问题**——同时改善 TTFT、答案聚焦度、显存与并发压力三件事。

再遇到 RAG 变慢，先问：**是不是把太多检索内容变成了模型必须在 Prefill 阶段读完的上下文？** 把这个问题答清楚，RAG 性能优化才从玄学变成工程。

## 关键术语

| 术语 | 说明 |
|---|---|
| TTFT (Time To First Token) | 首 token 延迟，主要受 Prefill 阶段影响 |
| ITL (Inter-Token Latency) | token 间延迟，主要受 decode 阶段影响 |
| Prefill | 生成第一个 token 前处理全部输入 prompt 的阶段 |
| KV Cache | 缓存 attention 中历史 token 的 Key/Value，避免 decode 时重复计算 |
| PagedAttention | vLLM 提出的 KV Cache 分页管理方案，减少显存碎片 |
| RAGCache | 缓存检索知识中间状态以降低 TTFT、提升吞吐的系统 |
| Lost in the Middle | 长上下文中信息位于中间时模型利用率下降的现象 |
| CRAG | 强调检索质量对 RAG 效果的影响的方法 |
| RECOMP | 通过压缩/选择性增强减少无效上下文的方法 |
