ML 工程师的 RAG + 评测栈
十件生产级资产,给真正要把 RAG 上线的 ML 工程师:分块、embedding 服务、向量库(pgvector + Qdrant)、检索框架、reranker、评测、漂移监控、tracing。没评测 = 没进步。
这个 pack 包含什么
这是 demo RAG 跑通了、老板很兴奋、现在你必须把它放到真用户面前还不能让它幻觉到惹官司时,你会搭的那一套。每个资产都是生产级、活跃维护,代表 ML 工程师必须亲自负责的某一层 —— 不是用胶水代码把困难抹平的库。
这个 pack 刻意区别于 TokRepo 上已有的 rag-pipelines pack。那一套是端到端 RAG 框架横评(Quivr / RAGFlow / GraphRAG / Kotaemon / Verba —— 想要现成 RAG app 时用)。这一套是底下的基础设施层:当任何现成框架都给不了你团队需要的延迟、控制力或评测严谨度时,你自己拼起来的组件。
所有这些资产串起来回答同一个问题,也是大多数 ML 团队在 RAG 上线第 3 周才痛苦意识到的:demo 指标无意义,唯一重要的数字是 faithfulness + answer-relevance,在真实评测集上每次改动前后都跑一遍。 这里一半的资产存在的意义就是让这个 loop 跑得快。
推荐安装顺序(分块 → embedding → 向量库 → 检索 → 评测 → tracing)
- Unstructured —— 文档 ETL。从这里开始,因为 garbage in 仍然等于 garbage out。Unstructured 处理带表格的 PDF、扫描表单、HTML、.docx、.pptx、.eml,返回干净的 chunk 加上元素级元数据(
Title/NarrativeText/Table),下游的过滤和 rerank 都靠这个信号。 - Text Embeddings Inference(Hugging Face)—— 你的 embedding 服务。自托管、低延迟、批处理,BGE / E5 / GTE / Jina / Nomic 都开箱支持。一张 GPU 跑一个服务,下游所有微服务都 POST 过去。别在 12 个微服务里各自调 OpenAI 的 embedding API。
- Sentence Transformers —— 大多数值得跑的 embedding 背后的模型库。你会用它做离线批量 embedding、训练自己领域微调的模型、以及在你自己的语料上 benchmark BGE-large vs E5 vs nomic-embed(唯一有意义的 benchmark)。
- pgvector —— 向量库方案 A。如果你已经在跑 Postgres,5000 万向量以内最便宜的正确答案就是 pgvector + HNSW。一个数据库、一套备份、事务性写入、能 join 你已有的元数据表。在 pgvector 真正撑不住之前别加独立向量库。
- Qdrant —— 向量库方案 B。pgvector 顶不住之后(1 亿+ 向量上的过滤查询、低延迟混合搜索、动态 schema),Qdrant 是开源升级路径。Rust 内核、索引期 payload 过滤、分片集群、MIT 协议。
- Haystack —— 生产级 RAG 与 agent 框架。Pipeline graph 抽象,每个组件都可替换,原生 async。当 LangChain 让你觉得在跟它打架、你想要可追踪可测试的显式 DAG 时,就拿它。
- LlamaIndex —— LLM 应用的数据框架。强在入库和检索侧:150+ 数据加载器、可组合的 query engine(router → sub-question → response synth)、
LlamaParse处理硬 PDF。配 Haystack 用,或单独用都行。 - Cohere Rerank —— 你这辈子能用最便宜的方式拿到的 +10–20 分检索质量提升。双编码器检索 top-50,cross-encoder 重排到 top-5。几乎每个一开始没有 reranker 的生产 RAG 团队,都在一个季度内加上了。
- Embedding Drift Monitoring —— 检索回归运维手册。当同一个 query 两个月后返回不同的文档,因为你的 embedding 模型悄悄被重新量化了,或者文档分布漂移了,你需要一个漂移看板。这就是抓这件事的生产运维手册。
- Arize Phoenix —— 开源 AI 可观测性 + 评测。OpenInference 兼容,给每个 LLM 调用和检索步骤打 trace;评测框架在每次 commit 时跑 LLM-as-judge 对你的测试集打分。Tracing + eval 这个闭环不可妥协,Phoenix 是用一套工具同时做到这两件事的开源方案。
它们怎么协同(生产级 RAG 流水线)
┌─────────────────────────────────────────────────────────────┐
│ 入库(INGESTION) │
│ Unstructured ──► chunks + 元素元数据 │
│ │ │
│ ▼ │
│ EMBEDDING │
│ Text Embeddings Inference(服务) │
│ ▲ (模型 = Sentence Transformers / BGE) │
│ │ │
│ ▼ │
│ 向量库 │
│ pgvector(≤50M) 或 Qdrant(>50M、混合搜索、过滤) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 查询时 │
│ LlamaIndex / Haystack ──► retrieve top-50 │
│ │ │
│ ▼ │
│ Cohere Rerank ──► cross-encoder 重排到 top-5 │
│ │ │
│ ▼ │
│ LLM(你的生成器) ──► 答案 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 可观测性 │
│ Arize Phoenix tracing ◄── 每一个 span(检索、rerank) │
│ Phoenix evals(LLM-as-judge) ◄── 每个 PR 跑评测集 │
│ Embedding Drift Monitoring ◄── 每晚 cron,触发告警 │
└─────────────────────────────────────────────────────────────┘
这个切分是故意的:入库是批处理(Unstructured → TEI → 向量库),查询时是热路径(向量库 → LlamaIndex/Haystack → Rerank → LLM),可观测性裹在最外面。没有第三个框,你根本看不出上周改的 prompt 是帮了你还是悄悄把 faithfulness 拉低了 8%。
你会遇到的取舍
- pgvector vs Pinecone vs Qdrant —— Postgres 上的 pgvector 在运维简洁性上赢(一个 DB、一套备份、能 join 已有元数据),到 1000–5000 万向量配 HNSW 都是真的够用。Pinecone 赢在「我不想跑基础设施」和弹性扩展,但成本叠加很快、锁定也重。Qdrant 赢在大规模上需要带过滤的混合搜索而你想自托管。默认 pgvector。过滤延迟扛不住时切 Qdrant。运维人手是硬约束才动 Pinecone。
- OpenAI embedding vs 开源(BGE / E5 / Nomic) —— OpenAI
text-embedding-3-large通用英语很强、上手简单。开源 embedding 走 Text Embeddings Inference 在量大时便宜 ~10 倍、能离线、能在自己领域上微调。决策通常归结到一个问题:你有没有一个足够好的评测集能 A/B 它们?有的话开源经常赢。没的话先用 OpenAI,把评测集先建起来。 - Haystack vs LlamaIndex vs LangChain —— Haystack:显式 pipeline graph,更好测,稍微 verbose 一些。LlamaIndex:入库 + 检索组合更强,全 agent loop 的抽象稍弱。LangChain:表面积最大、原型最快,多数生产团队最终都重构掉它。比较成熟的栈最后往往是 LlamaIndex 做入库 + 检索 + Haystack 或纯 Python 做编排。
- Reranker 延迟 —— Cohere Rerank 加 100–250ms。几乎总是值得。如果真的扛不住,就在自家 GPU 上跑一个小的开源 reranker(BGE-reranker-base)。
常见踩坑
- 分块太激进 —— 512-token chunk + 50-token overlap 是默认值,通常是错的。技术文档 Q&A 上,更大的语义 chunk(1000–1500 token,用 Unstructured 元素元数据按标题边界切)一致表现更好。在你自己的评测集上测,别猜。
- 没评测集 = 没进步 —— 最常见的死法。没有 50–200 个手工标注的 query / 期望 context 对,每次改动都是凭感觉。第一周就把评测集建起来。每次有真实用户反馈坏答案,就更新一个。这是任何 RAG 项目里单点 ROI 最高的工程投入。
- 换 embedding 模型不重建索引 —— 把
text-embedding-ada-002悄悄换成text-embedding-3-small,你的旧向量瞬间变成垃圾。给 embedding 打版本号。换模型就重建索引。 - 单一检索策略 —— 纯 dense 检索会漏掉词汇型 query(产品 SKU、错误码、版本号)。加一个 BM25 / 关键词检索器并行跑然后合并。LlamaIndex 和 Haystack 两行代码就支持。
- 没 reranker —— 双编码器快但有损。在 top-50 候选上跑 cross-encoder 重排是整个 RAG 栈上最稳的质量提升。「太花延迟」这个理由在 faithfulness 才是瓶颈的时候是假经济。
- Tracing 当事后补救 —— 等你出了质量问题再装 Phoenix,比第一天装难 5 倍。在第一个老板 demo 之前就把它装上。
10 个资产打包就绪
常见问题
这个 pack 跟 TokRepo 已有的 `rag-pipelines` pack 有什么区别?
rag-pipelines 是框架横评 —— Quivr / RAGFlow / GraphRAG / Kotaemon / Verba —— 想直接部署一个现成 RAG app 时用的。这个 pack 是底下的基础设施层:分块(Unstructured)、自己跑的 embedding 服务(Text Embeddings Inference)、自己运维的向量库(pgvector / Qdrant)、reranker、漂移监控、可观测性层。受众不同、workflow ID 零重叠。两个搭配用:从 rag-pipelines 选一个框架,等你要把它推过 demo 阶段时再回这里挑组件。
我真的需要同时装 pgvector 和 Qdrant 吗?
不需要,选一个。pack 里两个都列是因为答案真的取决于规模和已有基础设施。如果你已经在跑 Postgres、向量数少于 5000 万,pgvector + HNSW 是正确答案,加第二个 DB 是无谓的复杂度。如果你需要在几亿向量上做低延迟的过滤混合搜索,Qdrant 的运维成本是值的。先 pgvector,只有真实负载上的 benchmark 显示它跟不上时再切。
为什么这个 pack 里没有 LangChain?
LangChain 做原型没问题,多数团队第一个 RAG 仓库里都有它。这个 pack 反映的是成熟 ML 团队真正留在生产里的那些资产 —— Haystack 的显式 pipeline 和 LlamaIndex 的检索组合,在项目超过半年之后,可测试性和可维护性一致更胜出。能跑通原型就用什么。已经用 LangChain 上线且工作良好就别动;2026 年新起项目,这里的资产更经得住时间。
Reranker 真的值这点延迟吗?
值,几乎总是。双编码器向量检索 20ms 返回 top-50 候选,但有损,因为它比较的是预计算的独立表示。Cross-encoder reranker 对每个 query/doc 对联合重打分,常规能把 NDCG@5 拉高 10–20 分。Cohere Rerank 加 ~100–250ms,是整个栈上最便宜的质量跳跃。如果 100ms 真的不能接受,就在 CPU 上跑一个小的开源 reranker(BGE-reranker-base),50ms 内搞定 —— 但一定要上 reranker。
最小可用的评测集是多少?
50 个 query 手写理想 context 和可接受答案就足以有意义地度量 faithfulness、answer-relevance、context-precision。在调任何东西之前的第一周就把它建起来。然后接 Ragas / Phoenix Evals / DeepEval 之类的工具自动化打分循环。每个真实用户投诉都变成一条新的评测 case。三个月之后你会有 200–500 个 case,每个 PR 都在 CI 里跑一遍 —— 这才是真正驱动 RAG 质量的 loop。