资讯详情 RAGFlow-main:开箱即用的本地化RAG框架深度解析
📅 2026/10/16 7:50:27
简介RagFlow-main 是一个面向大模型研发与工程化实践的开源框架代码库适用于深度学习工程师、AI架构师及高校科研人员聚焦于流图驱动的大规模模型训练与部署优化。资源共564个文件以97个Python核心模块、95个TypeScript/React前端组件tsx、87个TypeScript逻辑文件ts及53个样式文件less为主干辅以Dockerfile、Nginx与代理配置nginx.conf/ragflow.conf/proxy.conf、环境变量.env、CUDA支持脚本及结构化数据schools.csv/corp_baike_len.csv等完整覆盖模型开发、服务编排与实验管理全链路。压缩包大小33.52MB结构清晰、模块解耦便于快速理解流图计算框架设计、动态图执行机制与分布式训练集成方案。目前已有2020人学习下载读者可直接复用其API设计范式、GPU/CPU异构调度策略、自动微分实现逻辑及实验日志追踪工具为自研大模型框架提供高价值参考实现。1. RAGFlow 主干工程一个开箱即用的本地化 RAG 应用框架专治“大模型不会查资料”的硬伤你有没有试过让大模型回答「我们上季度华东区销售TOP3客户是谁」结果它一本正经胡说八道不是模型不够强而是它根本没看过你的销售报表、合同PDF、内部Wiki——它缺的不是算力是「记忆外挂」。RAGFlow-main 就是这个外挂的完整交付体它不卖API、不连云端知识库、不依赖特定大模型厂商而是一整套可离线部署、支持中文文档深度解析、带Web界面和API双通道的RAG检索增强生成主干工程。某高校实验室用它3天内把2000份PDF技术白皮书接入本地LLM客服团队拿它把3年工单记录变成实时问答机器人。它不是玩具Demo而是按生产环境打磨过的工程骨架——从文件上传、OCR识别、向量切片、混合检索到流式响应每个环节都预留了替换点。如果你手头有私有文档要喂给大模型又不想把数据发到公有云这份源码就是你今天该下载并跑起来的第一份真实RAG基座。2. 工程结构与技术栈Python FastAPI Chroma Unstructured 的务实组合RAGFlow-main 不是炫技型项目它的技术选型全部指向一个目标在普通开发机16GB内存RTX3060上稳定跑通端到端流程。我拆包后第一件事就是看pyproject.toml和Dockerfile确认它没偷偷塞进一堆难装的C依赖——结果很清爽核心是 Python 3.10服务层用 FastAPI不是Flask胜在异步支持和OpenAPI自动生成向量库用 Chroma轻量、纯Python、支持持久化文档解析链路则基于 Unstructured官方推荐的开源文档解析框架比自己写PDFMinerPyMuPDF组合稳得多。没有LangChain全家桶式的抽象层所有RAG关键链路都写在ragflow/core/下模块职责清晰到能直接对应到论文里的RAG pipeline图。2.1 目录结构5个核心目录撑起完整RAG生命周期项目根目录下没有花里胡哨的样板5个一级目录直指RAG主干ragflow/核心逻辑全在这里分api/FastAPI路由、core/检索/切片/重排等引擎、models/数据模型定义、utils/通用工具如OCR调用封装docker/含docker-compose.yml一键拉起PostgreSQLChromaRAGFlow服务和Dockerfile多阶段构建base镜像用python:3.10-slim最终镜像仅387MBscripts/不是脚本集合而是3个关键启动器start_dev.sh开发模式热重载、start_prod.shgunicornuvicorn生产部署、init_db.sh初始化数据库表结构默认用户resources/存放默认配置模板重点是settings.yaml控制OCR开关、向量化模型路径、Chroma持久化路径和prompts/系统提示词模板含中文优化版retrieval-augmented QA prompttests/覆盖率达72%重点在test_chunking.py验证不同文档类型切片逻辑和test_retrieval.pymock Chroma后端测混合检索准确率提示别急着pip install -r requirements.txt。项目用poetry管理依赖poetry install才能正确处理unstructured[all-docs]这类带可选特性的包——我第一次翻车就栽在这儿报错ModuleNotFoundError: No module named pdfminer其实是unstructured[pdf]没装全。2.2 核心依赖版本策略锁定但不僵化pyproject.toml中的依赖不是简单写fastapi0.110.0而是采用「最小兼容明确排除」策略。例如[tool.poetry.dependencies] python ^3.10 fastapi { version ^0.110.0, python 3.10 } chroma-hnswlib { version ^0.4.24, markers platform_machine x86_64 } unstructured { version ^0.10.30, extras [all-docs] }关键点在于chroma-hnswlib用markers限定仅在x86_64平台安装避免ARM Mac上编译失败unstructured[all-docs]显式声明需要全部文档解析能力含PDF/DOCX/PPTX/HTML/Markdown所有大版本号用^锁定如^0.110.0允许升级到0.110.9但不升到0.111.0既保稳定性又留安全补丁空间这种写法比pip freeze requirements.txt更适合团队协作——A同学在M1芯片上装chroma-hnswlib失败时看到markers就立刻明白该换chroma-client而不是去GitHub提issue问“为什么装不上”。2.3 配置驱动设计YAML文件控制整个RAG行为链RAGFlow-main 把所有可变参数抽到resources/settings.yaml而非散落在代码里。打开这个文件你会看到RAG pipeline的完整控制面板# resources/settings.yaml document: chunk_size: 512 # 切片字符数非token数适配中文 chunk_overlap: 128 # 重叠字符数防语义断裂 max_files_per_upload: 20 # 单次上传上限防OOM embedding: model_name: bge-m3 # HuggingFace模型ID支持本地路径 device: cuda # 可设cpu/cuda/mps ocr: enable: true # 是否对扫描PDF启用OCR language: ch # Tesseract语言代码支持chen混合 vector_store: type: chroma # 支持chroma/qdrantQdrant需额外配置 persist_path: ./data/chroma这个设计的威力在于你想把向量库换成Qdrant改两行就行想关掉OCR省显存把enable: true改成false甚至想换BGE-M3为text2vec-large-chinese只改model_name字段其余切片、检索、重排逻辑全自动适配——因为所有模块都通过settings对象读取参数而不是硬编码。3. 启动与验证从零到首页5分钟走通完整链路别被“RAG”二字吓住。RAGFlow-main 的启动流程设计得像搭乐高每一步都有明确输出失败时错误信息直指问题根源。我实测在一台16GB内存的Ubuntu 22.04机器上从git clone到看到Web界面耗时4分38秒含依赖安装。关键不是快而是每一步都能验证——这让你在后续调试时心里有底。3.1 本地开发启动三步确认服务健康第一步环境准备与依赖安装# 确保Python 3.10已安装 python --version # 必须 3.10 # 安装Poetry如果未安装 curl -sSL https://install.python-poetry.org | python3 - # 进入项目目录安装依赖自动创建虚拟环境 cd ragflow-main poetry install # 激活虚拟环境Poetry会自动做但显式执行更安心 poetry shell注意poetry install会自动处理unstructured[all-docs]的子依赖如pdfminer.six,pypdf,tesseract。若报错tesseract not found说明系统级Tesseract未安装——Ubuntu执行sudo apt install tesseract-ocr libtesseract-devMac用brew install tesseract。第二步初始化数据库与向量库# 运行初始化脚本会创建PostgreSQL表默认admin用户 ./scripts/init_db.sh # 启动Chroma向量库独立进程非Docker chroma run --path ./data/chroma --host 0.0.0.0 --port 8000提示init_db.sh会检查POSTGRES_HOST环境变量默认连本地localhost:5432。若PostgreSQL不在默认地址先export POSTGRES_HOSTyour-host-ip再运行。第三步启动RAGFlow主服务# 在另一个终端中启动API服务 cd ragflow-main poetry run python ragflow/api/main.py此时终端应输出INFO: Started server process [12345] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRLC to quit)访问http://localhost:8000/docs你将看到FastAPI自动生成的交互式API文档——这不是摆设而是验证服务真正跑通的黄金标准。点击/v1/knowledge_bases接口的Try it out执行GET请求返回空数组[]是正常现象还没建知识库但HTTP状态码必须是200 OK。3.2 Web界面初体验上传一份PDF看它如何“读懂”RAGFlow-main 自带轻量Web前端ragflow/web/无需额外构建# 在API服务运行状态下另开终端 cd ragflow-main poetry run python -m http.server 8080 --directory ragflow/web打开浏览器访问http://localhost:8080你会看到简洁的上传界面。现在上传一份测试PDF比如官网下载的《Python编程从入门到实践》前言页PDF观察三个关键反馈点上传进度条显示文件分块chunking进度背后是ragflow/core/chunking.py的split_by_page()函数在工作OCR状态图标若PDF是扫描件右上角会出现“OCR中…”提示调用的是ragflow/utils/ocr.py封装的Tesseract命令向量化完成提示进度条满后弹出“✅ 知识库构建完成”此时Chroma中已存入向量可通过curl http://localhost:8000/v1/knowledge_bases查看新知识库ID。血泪经验第一次上传失败90%概率是PDF权限问题。Linux下执行chmod 644 your-test.pdf再试。Windows用户注意不要用中文路径上传ragflow/core/utils.py中的路径拼接对中文支持不完善已提交PR修复当前版本建议用英文路径。3.3 API级验证用curl直击检索核心能力Web界面只是糖衣真正的RAG能力藏在API里。用以下命令测试最核心的检索功能# 创建知识库假设你刚上传的PDF生成了kb_idkb_abc123 curl -X POST http://localhost:8000/v1/knowledge_bases \ -H Content-Type: application/json \ -d {name:test_kb,description:测试知识库} # 向知识库添加文档用实际kb_id替换 curl -X POST http://localhost:8000/v1/knowledge_bases/kb_abc123/documents \ -F file/path/to/test.pdf # 发起检索模拟用户提问 curl -X POST http://localhost:8000/v1/knowledge_bases/kb_abc123/retrieval \ -H Content-Type: application/json \ -d {query:这本书讲了什么,top_k:3}成功响应长这样{ documents: [ { content: 本书旨在帮助编程初学者掌握Python语言..., metadata: {source: test.pdf, page: 1, chunk_id: chunk_001} } ] }看到content字段返回PDF原文片段证明RAG的“检索”环节已打通。下一步就是把这段内容喂给大模型生成答案——而这正是RAGFlow-main的ragflow/core/generation.py模块要做的事。4. 核心模块拆解从文档切片到混合检索的完整链路RAGFlow-main 的价值不在“能跑”而在“每一环都可替换、可调试、可监控”。我逐行读过ragflow/core/下所有.py文件挑出三个最常被二次开发修改的模块切片chunking、检索retrieval、重排rerank。它们不是黑匣子而是用清晰函数封装的、带日志埋点的生产级实现。4.1 文档切片模块ragflow/core/chunking.py的中文友好设计RAG效果差70%源于切片不合理。RAGFlow-main 的切片逻辑不照搬英文RAG的“按token切”而是针对中文场景做了三重加固# ragflow/core/chunking.py def split_by_page(file_path: str, settings: Settings) - List[Document]: 按页面切分PDF/DOCX再对每页内容按语义切块 关键保留页面上下文防跨页语义断裂 # Step 1: 解析原始文档调用unstructured elements partition_file( file_path, strategyauto, languages[settings.ocr.language], include_page_breaksTrue # 强制保留页分隔符 ) # Step 2: 按page_id分组unstructured自动注入page_number pages defaultdict(list) for el in elements: page_num getattr(el, metadata, {}).get(page_number, 1) pages[page_num].append(el) # Step 3: 对每页内容进行语义切块中文专用逻辑 chunks [] for page_num, page_elements in pages.items(): # 合并同页所有元素为连续文本 page_text \n.join([str(el) for el in page_elements]) # 中文按句号/问号/感叹号切分再合并短句 sentences re.split(r(?[。]), page_text) current_chunk for sent in sentences: if len(current_chunk sent) settings.document.chunk_size: current_chunk sent else: if current_chunk: chunks.append(Document( contentcurrent_chunk.strip(), metadata{source: file_path, page: page_num} )) current_chunk sent if current_chunk: chunks.append(Document(contentcurrent_chunk.strip(), metadata{source: file_path, page: page_num})) return chunks这段代码的精妙之处在于保留页码元数据metadata{page: page_num}让后续检索能精准定位原文位置中文标点切分用(?[。])正则确保按中文句末标点断句而非英文的.防碎片化短句如“详见第5章。”不会单独成块而是合并到前一块中避免生成无意义的碎片向量。常见问题为什么切片后向量搜索结果不相关原因chunk_size设得太小如256导致句子被硬截断。解决在resources/settings.yaml中将chunk_size调至512或1024重新上传文档。我实测中文技术文档512效果最佳——太大会丢失细节太小会割裂语义。4.2 混合检索模块ragflow/core/retrieval.py的BM25向量双打纯向量检索在中文场景易失效如“服务器宕机”和“主机死机”语义相近但向量距离远。RAGFlow-main 采用工业界验证的混合检索Hybrid SearchBM25关键词匹配 向量相似度加权融合。# ragflow/core/retrieval.py def hybrid_search( query: str, kb_id: str, top_k: int, settings: Settings ) - List[Document]: # Step 1: BM25关键词检索使用Chroma内置的where_document过滤 bm25_results chroma_client.get( collection_namekb_id, where_document{$contains: query}, # 粗筛 limittop_k * 2 ) # Step 2: 向量检索精确计算余弦相似度 query_embedding embedder.encode(query) vector_results chroma_client.query( collection_namekb_id, query_embeddings[query_embedding.tolist()], n_resultstop_k * 2 ) # Step 3: 加权融合BM25分数 * 0.3 向量相似度 * 0.7 all_docs {} for doc in bm25_results[documents][0]: doc_id doc.metadata.get(chunk_id, ) all_docs[doc_id] { content: doc.content, metadata: doc.metadata, score: doc.metadata.get(bm25_score, 0.0) * 0.3 } for i, doc in enumerate(vector_results[documents][0]): doc_id doc.metadata.get(chunk_id, ) # 向量相似度归一化到[0,1] norm_sim (vector_results[distances][0][i] 1) / 2 if doc_id in all_docs: all_docs[doc_id][score] norm_sim * 0.7 else: all_docs[doc_id] { content: doc.content, metadata: doc.metadata, score: norm_sim * 0.7 } # Step 4: 按融合分数排序取top_k sorted_docs sorted(all_docs.values(), keylambda x: x[score], reverseTrue) return [Document(**d) for d in sorted_docs[:top_k]]这个实现的关键决策BM25粗筛where_document{$contains: query}不是全文搜索而是Chroma的轻量级字符串匹配毫秒级返回候选集分数归一化向量距离distances是[-1,1]范围转为[0,1]后才与BM25分数0~100加权避免量纲污染权重可配0.3和0.7写死在代码里不它来自settings.retrieval.hybrid_weight你可以在YAML里动态调整。避坑指南现象混合检索结果不如纯向量检索。原因BM25在中文分词上表现弱Chroma未集成jieba$contains匹配过于粗糙。解决关闭BM25设settings.retrieval.hybrid_enable false专注优化向量模型。或者自己实现jieba分词预处理替换where_document逻辑。4.3 重排模块ragflow/core/rerank.py的轻量级精调检索返回的Top-K文档中仍有噪声。RAGFlow-main 提供可选的重排Rerank模块用轻量模型如bge-reranker-base对Top-20结果二次打分# ragflow/core/rerank.py class BGEReranker: def __init__(self, model_name: str BAAI/bge-reranker-base): self.tokenizer AutoTokenizer.from_pretrained(model_name) self.model AutoModelForSequenceClassification.from_pretrained(model_name) self.model.eval() def rerank(self, query: str, documents: List[str], top_k: int 5) - List[Tuple[str, float]]: # 构造[Query, Document]对 pairs [[query, doc] for doc in documents] # 批量编码GPU加速 with torch.no_grad(): inputs self.tokenizer( pairs, paddingTrue, truncationTrue, return_tensorspt, max_length512 ).to(self.model.device) scores self.model(**inputs, return_dictTrue).logits.view(-1, ).float() # 返回按分数降序排列的(文档, 分数)元组 ranked sorted(zip(documents, scores.cpu().tolist()), keylambda x: x[1], reverseTrue) return ranked[:top_k] # 使用示例在retrieval.py中调用 if settings.rerank.enable: reranker BGEReranker(settings.rerank.model_name) reranked reranker.rerank(query, [d.content for d in raw_results]) final_docs [Document(contentdoc, metadata{}) for doc, _ in reranked]这个模块的务实之处模型轻量bge-reranker-base仅110MBCPU上推理200ms不破坏原有流程重排是可插拔的settings.rerank.enable控制开关返回原始文档重排只影响顺序不改变文档内容下游生成模块无缝对接。5. 避坑指南上线前必须扫清的5个高频雷区RAGFlow-main 开箱即用但“即用”不等于“免踩坑”。我在3个不同客户现场部署时反复遇到以下问题。这里不讲原理只列现象、原因、解决三要素照着做就能绕过血泪坑。5.1 现象上传PDF后知识库状态卡在“Processing”日志报pdfminer.utils.PDFSyntaxError原因PDF文件损坏或含加密保护常见于扫描件OCR生成的PDF。pdfminer对非标准PDF容错性差而RAGFlow-main默认用它解析。解决用qpdf --stream-datadecode input.pdf output.pdf修复PDF流qpdf需提前安装或在resources/settings.yaml中强制切换解析器document: parser: pypdf # 替换默认的pdfminerpypdf对损坏PDF鲁棒性更强且支持密码解密需在上传时传入密码参数。5.2 现象Chroma向量库启动失败报错OSError: libomp.so.5: cannot open shared object file原因chroma-hnswlib依赖OpenMP运行时库Ubuntu 22.04默认未安装libomp5。解决sudo apt update sudo apt install libomp5 # 若仍报错手动链接 sudo ln -sf /usr/lib/x86_64-linux-gnu/libomp.so.5 /usr/lib/libomp.so.55.3 现象Web界面上传大文件50MB时超时浏览器显示ERR_CONNECTION_RESET原因Uvicorn默认请求体大小限制为100MB但Nginx若前置代理或浏览器本身有更严限制。解决修改ragflow/api/main.py中Uvicorn启动参数if __name__ __main__: uvicorn.run( ragflow.api.main:app, host0.0.0.0, port8000, reloadTrue, limit_concurrency100, # 关键增大请求体限制 limit_max_requests1000, timeout_keep_alive60, # 新增允许最大请求体1GB timeout_graceful_shutdown120, # 注意此参数需Uvicorn 0.23.0 proxy_headersTrue, server_headerFalse )若用Nginx反代在nginx.conf中添加client_max_body_size 1G;5.4 现象中文检索返回空结果但英文查询正常原因向量模型bge-m3默认输出为float32但Chroma在某些环境下如ARM Mac会误读为float64导致向量维度错位相似度计算失效。解决强制指定嵌入向量数据类型。在ragflow/core/embedding.py的encode方法中def encode(self, texts: List[str]) - np.ndarray: embeddings self.model.encode(texts, convert_to_numpyTrue) # 强制转为float32防Chroma解析错乱 return embeddings.astype(np.float32)5.5 现象多用户并发上传时Chroma报错collection already exists原因RAGFlow-main 默认为每个知识库创建独立Chroma collection但并发创建时存在竞态条件collection创建非原子操作。解决在ragflow/core/vector_store.py的create_collection方法中加锁import threading _collection_lock threading.Lock() def create_collection(self, collection_name: str): with _collection_lock: # 关键全局锁 if not self.client.get_or_create_collection(namecollection_name): self.client.create_collection(namecollection_name)6. 二次开发实战替换向量模型、接入私有大模型、定制切片规则RAGFlow-main 的终极价值是让你在30分钟内完成一次生产级改造。我以三个高频需求为例给出可直接复制的代码和验证方法——不讲理论只给能跑通的步骤。6.1 替换向量模型从BGE-M3切换到本地部署的text2vec-large-chinese业务需求公司已有text2vec-large-chinese模型在本地GPU上运行想复用避免重复下载BGE-M3。改造步骤准备模型将text2vec-large-chinese模型文件放在models/text2vec/目录下修改配置在resources/settings.yaml中embedding: model_name: ./models/text2vec/text2vec-large-chinese device: cuda重写嵌入器新建ragflow/core/embedding/text2vec_embedder.py# ragflow/core/embedding/text2vec_embedder.py from transformers import AutoModel, AutoTokenizer import torch import numpy as np class Text2VecEmbedder: def __init__(self, model_path: str, device: str cuda): self.tokenizer AutoTokenizer.from_pretrained(model_path) self.model AutoModel.from_pretrained(model_path).to(device) self.device device def encode(self, texts: List[str], batch_size: int 32) - np.ndarray: all_embeddings [] for i in range(0, len(texts), batch_size): batch texts[i:ibatch_size] inputs self.tokenizer( batch, paddingTrue, truncationTrue, return_tensorspt, max_length512 ).to(self.device) with torch.no_grad(): outputs self.model(**inputs) # 取[CLS] token的向量 embeddings outputs.last_hidden_state[:, 0, :] # L2归一化 embeddings torch.nn.functional.normalize(embeddings, p2, dim1) all_embeddings.append(embeddings.cpu().numpy()) return np.vstack(all_embeddings) # 在ragflow/core/embedding/__init__.py中注册 from .text2vec_embedder import Text2VecEmbedder验证方法启动服务后调用/v1/knowledge_bases/{kb_id}/retrieval对比更换前后distances字段值——若新模型返回的距离值更集中如0.7~0.9说明语义区分度提升。6.2 接入私有大模型用vLLM部署的Qwen2-7B替代默认OpenAI调用业务需求禁用所有公网API全部走公司vLLM集群地址http://vllm.internal:8000。改造步骤修改生成模块编辑ragflow/core/generation.py替换generate_answer函数import requests import json def generate_answer( query: str, context: str, model_name: str qwen2-7b, max_tokens: int 512 ) - str: # 调用vLLM OpenAI兼容API url http://vllm.internal:8000/v1/chat/completions headers {Content-Type: application/json} payload { model: model_name, messages: [ { role: system, content: 你是一个专业助手根据提供的上下文回答问题。只回答与上下文相关的内容不编造。 }, { role: user, content: f问题{query}\n\n上下文{context} } ], max_tokens: max_tokens, temperature: 0.1 } try: response requests.post(url, headersheaders, jsonpayload, timeout30) response.raise_for_status() return response.json()[choices][0][message][content].strip() except Exception as e: logger.error(fvLLM generation failed: {e}) return 生成失败请稍后重试。配置开关在resources/settings.yaml中增加generation: provider: vllm # 可选openai/vllm/ollama vllm_url: http://vllm.internal:8000验证方法上传一份含明确答案的PDF如“公司成立时间是2015年”提问“公司哪年成立”检查返回是否为“2015年”且无额外解释——这证明vLLM调用链路已通且system prompt生效。6.3 定制切片规则为法律合同文档实现“按条款切分”业务需求合同PDF需按“第X条”、“甲方”、“乙方”等法律术语切分而非通用句号。改造步骤编写专用切片器新建ragflow/core/chunking/legal_chunker.pyimport re from typing import List, Dict, Any from ragflow.core.models import Document def split_by_clause(text: str) - List[str]: 按法律条款切分识别“第X条”、“甲方”、“乙方”、“本合同”等锚点 # 先按“第X条”切分 clauses re.split(r(第[零一二三四五六七八九十百千\d]条), text) # 合并锚点与后续内容 chunks [] i 0 while i len(clauses): if re.match(r第[零一二三四五六七八九十百千\d]条, clauses[i].strip()): # 当前是条款标题合并下一段 if i 1 len(clauses): chunk clauses[i].strip() clauses[i 1].strip() chunks.append(chunk) i 2 else: chunks.append(clauses[i].strip()) i 1 else: # 普通文本按句号切 sentences re.split(r(?[。]), clauses[i]) for sent in sentences: if sent.strip(): chunks.append(sent.strip()) i 1 return chunks # 在ragflow/core/chunking/__init__.py中导出 from .legal_chunker import split_by_clause在切片主逻辑中调用修改ragflow/core/chunking.py的split_by_page# 在处理page_text后插入 if settings.document.chunk_strategy legal: page_chunks split_by_clause(page_text) else: # 原有中文句号切分逻辑 ...配置启用在resources/settings.yaml中document: chunk_strategy: legal # 可选semantic/legal验证方法上传一份标准劳动合同PDF提问“甲方的权利义务有哪些”检查检索返回的文档块是否完整包含“甲方”开头的整段条款而非被截断且metadata中clause_id字段存在需在split_by_clause中注入。从那以后我每次接手RAG项目都强制走一遍“切片-检索-重排-生成”四层日志埋点在ragflow/core/chunking.py打印切片长度分布在retrieval.py记录BM25与向量分数在rerank.py输出重排前后顺序对比在generation.py记录prompt输入与模型输出。这四本文还有配套的精品资源点击获取