RAG 项目实战:MCP 协议 + GraphRAG + Cross-Encoder 精排从零搭建(附完整代码)
前言大模型落地难难在三个问题知识滞后、幻觉严重、多跳推理弱。RAG检索增强生成是目前最主流的解法但市面上多数 RAG 教程停在向量检索 LLM 生成的玩具级别。本篇记录我从零搭建一个工业级 RAG 智能体的完整过程覆盖 2024-2025 主流技术栈包含完整代码和我踩过的所有坑。适合做 RAG / Agent 方向项目、准备大模型岗位面试的同学。你能学到混合检索向量 BM25 RRF HyDE Cross-encoder 精排GraphRAG 知识图谱三元组抽取 → 实体消歧 → text2cypher多 Agent 编排路由 → 检索 → 推理 → 反思MCP 协议工具层ragas 标准评估LLM-as-judge一、系统架构整个系统分 5 层21 个核心脚本层组件说明用户层Gradio / FastAPI / 图谱可视化交互界面MCP 协议层FastMCP 工具服务器标准化工具调用Agent 编排层路由 → 检索 → 推理 → 反思Corrective RAG 范式检索层向量 图谱 层次摘要 Reranker多路召回 两阶段精排数据层ChromaDB / NetworkX / bge-m3存储与模型二、技术栈选型组件选型为什么LLMMiniMax-M3OpenAI 兼容中文强Embeddingbge-m31024 维MTEB 排行榜前列中英多语言Rerankerbge-reranker-v2-m32024 SOTA cross-encoder向量库ChromaDB轻量零运维图谱NetworkX可切 Neo4j内存图降级评估ragas 0.2.15GitHub RAG 评测标准三、混合检索从 70% 到 91%这是技术含量最高的模块。逐步叠加策略每加一层测一次准确率3.1 第一步纯向量检索Hit5 70%# 04_embedder.py 核心 model SentenceTransformer(./models/bge-m3) # 文档入库不加前缀查询加前缀bge 官方推荐 coll chromadb.PersistentClient(pathDB_PATH).get_or_create_collection( namerag, metadata{hnsw:space: cosine})3.2 第二步加 BM25 RRF 融合→ 82%向量检索对专有名词和数字不敏感。加 BM25 补精确匹配# RRF 融合只看排名不看绝对分数解决量纲不一致 def rrf_fuse(ranked_lists, k60): scores {} for lst in ranked_lists: for rank, doc in enumerate(lst): scores[doc] scores.get(doc, 0) 1 / (k rank) return sorted(scores, keylambda d: -scores[d])3.3 第三步加 HyDE→ 88%让 LLM 先生成假设答案用假设答案检索拉平 query 和 chunk 的长度差距# HyDE假设答案比短 query 更接近文档形态 hypothetical llm.generate(f假设答案{query}) results vector_search(hypothetical)3.4 第四步Cross-encoder 精排→ 91%# _reranker.pybge-reranker-v2-m3 pairs [[query, c[text][:1000]] for c in candidates] inputs tokenizer(pairs, paddingTrue, truncationTrue, return_tensorspt) logits model(**inputs).logits.squeeze(-1) # 用原始 logit 排序sigmoid 后值域太窄区分度差两阶段架构双塔粗召回 top-50 → cross-encoder 精排 top-5。这是搜索引擎标配。四、GraphRAG多跳推理4.1 三元组抽取08_kg_extractor.py# LLM 抽三元组三道防幻觉 # 1. 实体必须出现在原文 # 2. 关系长度 ≤ 15 字 # 3. 两次抽取取并集 频次过滤4.2 实体消歧09_kg_builder.py三层瀑布过滤从便宜到贵# 层1子串包含免 API # 层2相似度 ≥ 0.88免 APIdifflib.SequenceMatcher # 层30.75~0.88 灰度区调 LLM 仲裁限量 20 对4.3 text2cypher10_kg_query.py# LLM 把自然语言翻译成 Cypher # MATCH (e:Entity {name:MiniMax-M1})-[:RELATES_TO]-(t) RETURN t踩坑text2cypher 的 RETURN 列每次都不同F1 在 60%~100% 随机跳。解法是standardize_projection()——强制重写成固定 subject/predicate/object 三列投影。LLM 只定位子图选列由服务端固定。五、踩坑实录重点坑 1MiniMax-M3 的think块系统性陷阱这个坑我踩了两次第一次推理模块用错误正则剥离 think 块没剥掉吃光 max_tokens答案截断。第二次ragas 评测Faithfulness 假低到 38%。think 块占满输出ragas 的 JSON 被截断解析失败记 0 分。解法推理模块用re.sub(rthink.*?/think, , text, flagsre.DOTALL)正确剥离评测模块extra_body{thinking:{type:disabled}}关掉 thinking修复后 Faithfulness38% → 95.21%。坑 2双栏 PDF 文字错乱pdfplumber 处理不了多栏左右栏文字混在一起。自写分栏检测 3 版全失败标题Scalable RL被切成Scal。错乱现象解析结果左栏/右栏按行交错拼接标题 Scalable RL 被切成 Scal整段读不通。解法换 PyMuPDFfitz的get_text(text)按视觉块排序天然支持多栏。10 分钟解决。修复效果换库后先读完整个左栏再读右栏标题完整、段落连贯。教训别在一个库里死磕。先调研社区方案。坑 3反思重检索反而退步设计了反思 Agent置信度低就重检索。实测准确率反而下降——重检索 query 从无依据断言抽本身就偏。解法砍掉重检索反思只打分、不干预。少做反而更好。坑 4ragas 和项目 numpy 版本冲突ragas 依赖 numpy 1.26项目用 numpy 2.x。解法把 ragas 装到独立 venv数据中转 JSON两个环境不混。六、评估结果6.1 ragas 标准指标LLM-as-judge指标总体bge_paperMiniMax_M1Context Recall97.92%100.00%95.83%Faithfulness95.21%96.93%93.49%6.2 4 篇论文端到端 F1泛化验证论文领域F1bge_paperEmbedding91~100%MiniMax_M1LLM83.33%Attention Is All You NeedNLP 经典100%ResNetCV 经典93.75%七、源码与总结核心技术亮点混合检索 Cross-encoder 两阶段搜索引擎标准架构GraphRAG 向量 RAG 融合多跳推理 语义检索互补多 Agent 编排路由 → 检索 → 推理 → 反思ragas 原版评估LLM-as-judge 科学评测MCP 协议工具层标准化工具调用完整代码开源在 Apageoflove/mcp-rag-agent含 21 个核心脚本 ragas 评测数据 中英双版 README。如果觉得有帮助点个赞 收藏后续会持续分享 RAG / Agent / 大模型落地的实战经验。