大模型知识体系 第 29 章 🎮 交互版(含动画演示与测验)
← Retrieval & RAG Engineering:让模型用上外部知识🏠 回到交互版首页SFT / LoRA:一次真实实验闭环怎么读 →
你正在浏览静态阅读版(无 JavaScript 也可阅读,便于搜索与 AI 抓取)。交互演示与答题功能请前往 交互版。
第 29 章 · 第七部分 · 工程查阅

RAG 服务化:检索管线怎样对外提供接口

FastAPI(/chat、SSE、超时、线程池)、检索与生成分开评测、Dockerfile 纪律。历史服务实现仅作可选对照。

对应课件:工程查阅(原 Job-Ready)

第 29 章 · RAG 服务化:检索管线怎样对外提供接口

💡 一句话

把第 28 章的检索管线做成服务时,要处理 /chat 的 answer + citations、SSE 流式、超时和失败定位。这是服务化原理,不是当前主线的 Checkpoint 作业。

📘 本章在当前主线中的位置

当前主线不是再交付一个 RAG 服务。本章保留 FastAPI / SSE / 超时 / 线程池原理,以及当时的评测数字。projects/rag-service/ 是可选历史参考,非当前作业。

历史运行:课程内容 28 篇 → 841 chunks;22 条标注查询;Qwen2.5-0.5B 生成(CPU)。

29.1 从「跑通检索」到「能对外服务」

当时那次实验覆盖过这些能力(作为阅读清单,不是现在要交的验收表):

能力 验收物
完整管线 documents → chunk → BM25+向量 → hybrid → rerank → LLM → answer+citations
评测 检索侧 4 组对照(Hit@k / Recall@k / MRR / nDCG@k,doc 级)+ 生成侧失败分类
服务 FastAPI:/health /retrieve /chat /chat/stream(SSE)
工程 Pydantic schema、超时保护(504)、并发线程池、pytest 27 用例
部署 Dockerfile(本机无 Docker,标注 NOT EXECUTED)

29.2 Pipeline 架构与实测数字

ingest:  文档目录 → chunk(recursive 512/64)→ BM25 索引 + BGE 向量(FAISS)
query:   BM25 top-20  ┐
          向量 top-20  ┴→ RRF 融合 → cross-encoder 精排 top-5 → prompt → Qwen → answer + [n] 引用

真实评测结果(22 条标注查询,全部可复现):

模式 Hit@10 Recall@10 MRR nDCG@10
BM25 100.0% 100.0% 0.9091 0.9329
Dense 86.4% 86.4% 0.6417 0.6991
Hybrid(RRF) 100.0% 100.0% 0.8485 0.8884
Hybrid + Reranker 100.0% 100.0% 0.9318 0.9497

调用链路的服务端耗时拆解(每个回答都返回):

"latency_ms": { "retrieve": 42, "rerank": 1850, "generate": 13000, "total": 14900 }

rerank 占大头、generate 更甚——这就是服务优化时该先动的地方(更小的 reranker / 更快的模型 / 缓存)。

29.3 FastAPI 服务化:五个工程要点

@app.post("/chat")
async def chat(req: ChatRequest) -> ChatResponse:
    result = await asyncio.wait_for(
        run_in_threadpool(pipeline.answer, req.question, req.mode, req.top_k, req.top_n, req.use_rerank),
        timeout=pipeline.cfg.request_timeout_s,
    )
    return ChatResponse(answer=..., citations=..., sources=..., latency_ms=...)
要点 做法 为什么
Pydantic schema ChatRequest / ChatResponse / RetrieveRequest 等 请求校验 + 自动文档 + 类型安全
线程池 run_in_threadpool 包装同步 CPU 任务 不阻塞事件循环,服务还能响应健康检查
超时 asyncio.wait_for(..., 60s) → 504 模型生成慢,必须有上限,防拖垮服务
SSE 流式 /chat/stream 先发 sources 事件再发 token ChatGPT 式体验;引用在生成前就可见
健康检查 /health 返回 docs/chunks/模型名 部署探活、容器编排必需

测试思路(无需真实 API key):用 FastAPI TestClient 打端点——健康检查、检索、chat、SSE 首事件、超时 504。历史参考实现把这些写在测试里;那不是当前作业。

⚠️ 常见错误:async def 里跑 CPU 密集任务

async def chat() 里直接调用同步的 pipeline.answer()(里面是模型推理)会阻塞整个事件循环——所有请求排队,健康检查都会超时。 正确做法:把同步推理放进 run_in_threadpool。CPU 密集 + async 是 FastAPI 服务最常见的坑。

29.4 流式(SSE):为什么 ChatGPT 是"打字机"

def event_source():
    for kind, payload in pipeline.answer_stream(question, ...):
        if kind == "sources":
            yield "data: " + json.dumps({"type": "sources", "sources": payload}) + "\n\n"
        else:
            yield "data: " + json.dumps({"type": "token", "text": payload}) + "\n\n"
    yield "data: {\"type\": \"done\"}\n\n"

客户端按 data: 行解析事件。首字节时间(用户感知速度)从「全部生成完」降到「秒级首批 token + sources」。

29.5 Docker:提供但未实测

docker build -t rag-service .
docker run -p 8000:8000 \
  -v $(pwd)/../../content:/corpus \
  -e RAG_CORPUS=/corpus \
  rag-service
📘 Docker build(可选对照,非当前作业)

开发机当时未装 Docker;仓库 CI 里仍保留对历史镜像的 docker build,用来检查还留在磁盘上的代码能否构建,不是要你交一份 RAG 作业。本地命令仅供对照:docker build -t rag-service .

29.6 真实运行:6 题端到端

检索命中 6/6 | 引用规范 4/6 | 耗时中位数 19.1s(CPU,0.5B 模型)

✅ PagedAttention  → 回答了显存碎片机制,引用 [1]
✅ GQA vs MQA     → 回答了分组折中与 KV 压缩,引用 [1]
✅ pass@k         → 写出无偏估计公式,引用 [1]
✅ MinHash        → 回答了签名估计与 LSH,引用 [1]
❌ online softmax → 长回答但未标注引用(uncited_answer)
❌ Chat Template  → 回答了但未标注引用(uncited_answer)

样本外结论(纪律):6 题、CPU、0.5B —— 这是流程验证级实验,不是能力结论;但它完整展示了「如何用失败分类定位系统瓶颈」。

29.7 面试复盘:RAG Service

  1. 为什么 /chat 要把同步推理放进线程池?不放会怎样?
  2. 超时怎么设计?504 和 500 的区别是什么场景?
  3. SSE 相比一次性返回有什么体验差异?服务端怎么实现?
  4. /retrieve 调试端点为什么有价值?(提示:线上定位「检索还是生成」)
  5. 响应里的 latency_ms 拆解对性能优化有什么用?
  6. Docker 镜像里模型权重怎么处理?(提示:构建时下载 vs 运行卷挂载)
  7. 如果并发上来了,哪些环节可以缓存?(提示:索引、embedding、rerank 结果、LLM 响应)
📝 小测验
你的 RAG 服务上线后,高峰期所有请求都超时,但单次请求测试正常。最可能的原因是?
A模型变小了
B同步推理阻塞了事件循环 / 线程池被打满,请求排队
C检索索引坏了
Dprompt 写错了
显示答案与解析

正确答案:B

单请求正常、并发崩——典型的资源阻塞问题。若在 async 端点里直接跑同步推理(未放线程池),或线程池/并发配置太小,请求全部排队直至超时。修法:推理放线程池 + 限制并发 + 必要时多副本扩展。
🔑 本章必须记住
站点 核心结论
管线 chunk → BM25+向量 → RRF → rerank → LLM → answer+citations
实测 reranker 收益最大(MRR 0.849→0.932,超越 BM25 的 0.909);检索 6/6、生成 4/6
服务 FastAPI + Pydantic + 线程池 + 超时 504 + SSE
坑 async 里跑同步推理 = 阻塞事件循环
部署 Dockerfile 已提供;本机无 Docker → NOT EXECUTED
定位 /retrieve + latency 拆解 + 失败分类三件套
← Retrieval & RAG Engineering:让模型用上外部知识🏠 回到交互版首页SFT / LoRA:一次真实实验闭环怎么读 →
本章完。原始 Markdown:content/29-capstone-rag.md