参考文章:
[1] 来自工业界的知识库 RAG 服务(二),RagFlow 源码全流程深度解析 - 易迟的博客 | Bryan Blog
[2] 赢得企业RAG挑战赛的秘诀 —— 冠军方案剖析与感悟
[3] https://abdullin.com/ilya/how-to-build-best-rag/
[4] RAG进阶:Embedding Models嵌入式模型原理和选型指南 - 文章 - 开发者社区 - 火山引擎
之前已经学习了RAG的基本理论知识:详见RAG综述
现在做一个简单的实操项目:基于图文PDF的多模态问答RAG项目:2025 iFLYTEK AI开发者大赛
一、项目概述:
根据提供的图文混排的公司研报pdf,进行问题回答;需要完成的具体任务如下:
1)多模态信息理解:能够同时理解用户提出的自然语言问题以及知识库中的图像和文本内容。
2)跨模态检索:从给定的多个PDF文档中,高效地检索与用户查询相关的图像、图表、文本段落或它们的组合。
3)图文关联推理:将检索到的图像信息与文本信息进行有效关联和融合,进行深层次的逻辑推理,以回答需要多模态理解的问题。
4)答案生成:基于检索和推理的结果,生成准确、简洁、符合上下文的答案。答案中应清晰指出信息来源(例如,来自哪个PDF的哪一页,甚至哪个图表)。
使用模型服务平台:硅基流动:SiliconCloud
本地部署模型:使用ollama
本机配置:32G内存,16核,集显:AMD 780M Graphic
二、baseline:
baseline的得分为:0.00338,属于基本都答不出来的情况。
baseline虽然简单,但是可以简明地学习到RAG的主要步骤流程的实际操作方法。
1. 评价指标:
-
a.页面匹配度(满分0.25 分)
-
b.文件名匹配度(满分0.25 分)
-
c.答案内容相似度(满分0.5 分):将字符交集大小除以并集大小,得到 Jaccard 相似系数,范围在 0 到 1 之间。
注意:提交结果时需要匹配使用到的pdf的名称和块对应的页码,所以分块时需要保存页码。
2. baseline的基本思路:
- 文本解析分块:fitz库,基于PyMuPDF的解析pdf库,直接基于页码进行的分块,结果保存为json文件
- embedding:免费模型:BAAI/bge-m3,智源的嵌入模型
- 本地构造简单的向量库:读取分块后的json文件,存储了原始文本和embedding后的文本,search功能:用余弦相似度进行的全量检索,并排名
- query:使用聊天模型:Qwen/Qwen3-8B,为免费模型,阿里的。
- prompt工程如下:
-
prompt = (f"你是一名专业的金融分析助手,请根据以下检索到的内容回答用户问题。\n"f"请严格按照如下JSON格式输出:\n"f'{{"answer": "你的简洁回答", "filename": "来源文件名", "page": "来源页码"}}'"\n"f"检索内容:\n{context}\n\n问题:{question}\n"f"请确保输出内容为合法JSON字符串,不要输出多余内容。")
- 输出后处理:将回答整理为json格式,舍弃不需要进行评分的信息。
三、常见的RAG系统改进思路:
可以改进的地方:
- 文本解析、分块技术:chunk_size, overlap
- embedding模型选择;
- 文本信息进一步处理:数据清洗、文本摘要添加、知识图谱等等
- 检索方法优化(多路检索,多路召回等)
- 检索后优化:rerank
- LLM回答控制:prompt工程等
1. 使用开源框架ragflow:
由于笔者近期在研究ragflow,所以想使用这种开源框架,尝试解决一些实际问题:
使用Ragflow提供的API进行本场比赛的全流程搭建,看效果如何,选择的模型不动。(也可以使用提供的python SDK)
需要使用的API顺序:创建知识库 -> 上传文档 -> 解析文档(包括解析、分块、上传至向量库)-> 创建聊天助手 -> 和助手聊天
最后再自行进行回答结果后处理即可。
遇到的问题:
1)ragflow找不到ollma服务:RagFlow添加OLLAMA模型失败--解决方案-CSDN博客 ;
2)ragflow的deepdoc方案没有保存pdf的页码,不符合提交要求,需要二开。
3)回答速度缓慢(一个问题大概花费30-160s不等),解析的速度也缓慢(这个可以理解为提前做好数据库,可以容忍)。
评分结果:
中间结果:
ragflow提供了文件解析后的分块的示意图:(好奇这个是如何实现的?):看上去效果不错。

2. 每个环节可以优化的地方:
1)pdf文档解析处理:
- 解析:
参考1:一种方法是使用现在市面上已经开源的库方法:(但是针对比赛做一些任务理解之后的针对性代码撰写,可能会会更为高效)
|
工具类型 |
代表工具 |
核心特点 / 解决痛点 |
|---|---|---|
|
基于深度学习的“新锐” |
MinerU(该赛事推荐的方法),deepdoc(ragflow开发的模块),LLMSherpa |
能理解文档版面布局(如多栏、页眉页脚),识别章节、表格、公式等语义元素,输出带结构的Markdown/HTML,力求保留原始语义;适合要求精细地解析文档的场景,算力配置较高 |
|
传统解析库(规则驱动) |
PyMuPDF,PDFPlumber,pypdf |
baseline里用到的解析方法,比较粗糙,但速度快,对复杂版面(如多栏)的阅读顺序还原、语义结构理解能力有限 |
|
OCR驱动 |
Tesseract + OCRmyPDF,PaddleOCR | 核心解决扫描件/图片型PDF的文字识别问题 |
|
统一解析框架 |
Unstructured,Docling(IBM的,IIya Rice大赞) | 提供统一接口处理多种格式(PDF, Word, PPT等),内置文本清洗和分块功能。 |
思考1:pdf文档解析的原理:可以粗糙的理解为先要解析为text,困难的地方在于图文混排,排版形式多样,且需要找到不同块之间的语义关系。这里我产生一个好奇,就是pdf自带的文档编辑器已经把文本分成块了,这里的传统驱动是基于这个层面的理解做到吗?

可以看到pdf编辑器里如上,问题是图表分块这个供我们识别是不合理的,文本上需要找到相关的块以及它们的语义关系。
思考2:文档解析之后的输出形式:json:ragflow的处理(?)
- 解析后的清洗:正则表达式来清理不正确的解析(有解析错误的、无意义的语段)、OCR来识别一些无法解码的部分
?这个部分如何判断是乱码的,是该清洗的部分?
- 表格
在表格中,横标题和竖标题通常距离远,所以造成语言关联困难。做表格序列化的处理,即把表格转换成小的、独立的文本字符串。有点像给大语言模型解释表格的意思,而非直接递给他表格数据。这里IIya Rice的做法是将html格式的表格喂给LLM,让LLM理解表格,输出的结果放置在原始表格内容下方作为补充解释。
相关研究的论文:2402.17944
思考1:其它成形框架里没有做表格的解析处理吗?是如何做的?
- 分块:
由于比赛要求,回答时需要关联到文档和相关的页面,所以最直接的做法是分块的基础在每一页page进行。
参考1:IIya,在每页的基础上又进行token分割,大概300token,overlap是50,而他的实验结果是chunks的精度对他的检索系统几乎没有影响。
参考2:Googel "Semantic splitter."
- embedding模型选择:
模型选择上也需要根据需求来,这里暂时不进行深入研究:记录一下通用型的模型:
BGE系列:如BGE-large-zh-v1.5 针对中文,BGE-M3(多语言通用)。
2)存储入库:
市面上的向量库即配套了检索方法
- 参考1:不同的向量库:
|
数据库名称 |
类型 / 核心特点 |
关键优势 |
典型适用场景 |
|---|---|---|---|
|
Milvus |
开源原生向量数据库 |
分布式架构,支持十亿级向量规模;索引算法丰富(HNSW/IVF)
|
超大规模AI搜索、推荐系统、图像/视频检索
|
|
Chroma |
轻量级开源向量数据库 |
API简单易用,集成快速;非常适合原型开发和实验
|
大型语言模型(LLM)应用原型、小规模语义搜索
|
|
Pinecone |
全托管商业向量数据库 |
免运维,开发者无需管理底层基础设施;提供高性能的实时检索
|
需要快速上线、高并发实时检索的商业应用,如推荐系统
|
|
Qdrant |
开源原生向量数据库(Rust编写) |
高性能,毫秒级延迟;支持高效的混合搜索(向量+元数据过滤)
|
生成式AI应用(如聊天机器人)、高并发实时检索
|
|
Weaviate |
开源向量数据库 |
结合了向量搜索和图数据库特性,支持多模态数据和复杂语义查询
|
知识图谱、智能问答系统、需要复杂混合查询的应用
|
|
Pgvector |
PostgreSQL扩展插件 |
无缝集成到现有PostgreSQL数据库中,可同时处理结构化数据和向量
|
中小规模向量检索,尤其适合已在使用PG生态、希望平滑升级的项目
|
|
Faiss |
开源向量搜索库(Facebook开发) |
搜索速度极快,专注于高效的相似性搜索和聚类
|
大规模图像识别、文档聚类等对纯搜索性能要求极高的场景 |
思考1:如何选择向量库?
- 参考2:IIya,对每个pdf单独创建一个数据库,因为它认为一般一个回答可以从一个文件里得到。(这样的话对于这个比赛可以考虑一个公司创建一个数据库)
IIya选择了使用Faiss,embedding模型是text-embedding-3-large
参考3:主流的embedding模型:
???
参考4:向量库原理大致学习:
在存储token时还涉及一个环节是选择不同的indice(索引),在向量库中存在不同层级的索引,它帮助后续的检索操作快速的定位有用的token的位置:
- chunk索引,单个 Chunk 内部的向量,后续用于计算chunk和query的相似度;是直接搜索;
- segment索引,语言索引,多个语言相似的token被聚类后有一个索引,类似看书先找相关的章节;
- 数据库级别的索引,类似去图书馆找书
IIya考虑到比赛的文本数量少,选择了Flat indice,
|
索引类型 |
核心原理 |
适用场景 |
特点 |
|---|---|---|---|
|
扁平索引 (Flat Index) |
暴力计算查询向量与数据库中所有向量的距离 |
数据量小(通常<1万),要求100%准确率 |
精度最高,但速度随数据量线性下降 |
|
IVF 索引 (Inverted File Index) |
先将向量聚类(如K-means),搜索时只在最近几个簇内计算 |
百万级数据量,平衡速度与精度 |
速度提升明显,精度略有牺牲 |
|
HNSW 索引 (Hierarchical Navigable Small World) |
基于图结构构建多层网络,实现快速近似最近邻搜索 |
十亿级数据量,追求极高搜索速度 |
查询速度快,内存占用较高 |
|
PQ 索引 (Product Quantization) |
将高维向量压缩成短编码,大幅减少存储和计算量 |
超大规模数据(十亿级以上),资源受限场景 |
极大节省存储,精度损失需权衡 |
- 交叉编码:Jina Reranker
- 使用LLM进行rerank:再使用一个大语言模型把检索到的tokens和query喂入大模型,再次进行相关性评判,关键是选好大模型和写好prompt就行。

4)prompt工程:
参考1:IIya将不同功能的prompt做了模块化处理,再使用函数进行组合,方便调整和优化;
参考2:
5)生成过程增强:(Generation)
参考1:IIya的思路:
- 通过公司名过滤数据库:因为问题都是关于某公司or多公司研报的
- CoT: 使用思维链拆解复杂问题,这个做法的底层逻辑是,LLMs处理复杂逻辑的能力较差,且如果在prompt里设置较多的规则,LLMs对问题本身的回应能力会变差。简单的 Think step by step 是不够的,必须清晰地指导模型如何进行推理。解释推理步骤、目标并提供示例,明确提示模型从不同的角度分析内容。
- 结构化输出:json;Pydantic ;每一段的推理都使用结构化输出,这种结构化输出的要求被封装在prompts里
- 并非所有的模型都支持结构化输出(部分小模型做不到),此时可以写一个回退方法,使其重新回答
- one-shot:提供的示例需要和指令严格对齐 细化指令prompt工程:(劳动密集)
- QA分析:按道理说,搭建一个好的问答系统的前提是,搭建该系统的人对QA的需求非常明确,并且知道如何才能获得合理准确的A。所以说针对比赛而言,需要先分析一下提供的QA。(但是这一部分工作量太大)
- 问答系统要求的回答风格是什么样的:类似于,唯一精确的回答 or 尽量详尽的完整的解释等;模型回答的自由度是多少?等等
- 通过对QA进行分析后,可以对不同分类的Q写不同的prompt
- 指令微调:官方提供了train的QA集,可以用来微调用来回答的LLM
总结感想:
- 对于任何一项工程来说,明确需求总是高质量完成系统建设的第一步;而对于问答系统而言,这意味着永远没有任何一套系统可以适应所有的知识体系。
- RAG系统的高质量在于细节。
四、简单搭建的优化line:
由于时间有限,本人不打算对上述的优化思路进行全方位探索,只针对可以为简历增添亮点的部分进行工作,以及对于一个RAG系统来说是基本操作的部分,也就是做出一个大概还行的系统:
1)文档解析:针对赛题,打算做一个基于页面分割后的mineru;有时间的话看看ragflow的文档分块可视化是如何实现的。
2)入库:暂定chroma、Faiss(本人只打算在本机上进行实验,想着节省预算)
3)检索算法:
4')指令微调:如果有时间的话
4)推理优化:
- CoT
- prompt工程
思考:
- 如何评估每一步的效果?
- 如何评估一个RAG系统: RAGAS
五、思考:比赛与实际应用场景的不同: