2.PDF长文档完整读取

作者:HappyAcmen日期:2026/6/6

一、先知道什么是RAG

1. 一句话给 RAG 下定义

RAG = 给大模型(比如 ChatGPT、豆包、开源大模型)外挂了一个你自己的私有知识库,让大模型能精准、无差错地回答你私有数据里的内容,不会瞎编、不会用过时的知识。

你可以把 RAG 理解成:

  • 大模型是一个超级会说话、会总结的「秘书」,但她的知识是固定的、过时的,而且完全不知道你电脑里的 PDF、文档、笔记这些私有内容
  • RAG 就是你给这个秘书配的一个专属智能文件柜 + 精准检索神器,你把自己的所有文档、PDF、笔记都放进这个柜子里,秘书回答问题前,会先去这个柜子里精准找到对应的内容,再基于这些内容给你回答,绝对不会瞎编。
2. RAG 到底解决了什么核心痛点?

大模型本身有两个致命缺陷,RAG 就是专门解决这两个问题的:

  • 大模型的知识是「过时的、固定的」
    比如 2024 年的大模型,不知道 2025 年的新政策、新行业动态、你公司最新的产品手册,你问她,她要么瞎编,要么说「我的知识截止到 XX 时间」。
  • 大模型完全不知道你的「私有数据」
    你电脑里的公司的内部制度、项目文档、个人笔记,这些内容从来没有在互联网上出现过,大模型完全没见过,你直接问她,她 100% 会瞎编乱造。
  • RAG 的核心价值,就是让大模型能用上你的私有数据、最新的知识,回答 100% 贴合你的需求,不会出错、不会瞎编

二、实操及讲解

模块 1:前置知识与核心认知

核心基础概念认知

  • 文本块(Chunk):拆分后的每一段独立的短文本,是 RAG 系统中检索和向量化的最小单元
  • 块大小(Chunk Size):每个文本块的最大长度(通常按字符数 /token 数计算),是分块最核心的参数
  • 块重叠(Chunk Overlap):相邻两个文本块之间重复的内容长度,解决分块导致的上下文断裂问题
模块 2:文本分块核心规则与策略学习
1. 核心参数选型规则

块大小(Chunk Size)选型

场景推荐块大小(字符数)对应 token 数(约)核心原则
通用知识库 / 网页文本100-50050-250兼顾检索精度和上下文完整性
论文 / 长文本 / 书籍500-1000250-500保证段落语义完整,避免拆分核心内容
问答 / 精准知识库50-20025-100最大化提升检索匹配精度
通用红线必须小于向量模型最大输入长度,预留 10%-20% 冗余空间-绝对不能超出模型输入限制

块重叠(Chunk Overlap)选型

  • 通用规则:重叠长度为块大小的10%-20%,比如块大小 500 字符,重叠 50-100 字符
  • 高连贯性长文本场景:重叠比例提升到20%-30%,彻底避免上下文断裂
  • 核心原则:重叠部分必须包含完整的句子 / 语义单元,不能只重叠单个无意义的词语
2. 主流分块策略详解
分块策略核心逻辑优点缺点适用场景
固定长度分块(最基础)按固定的字符数 /token 数拆分文本,达到指定长度就拆分实现简单、速度快、长度完全可控,适配绝大多数向量模型可能拆分完整的句子 / 段落,导致语义断裂通用知识库、网页文本、PDF 文档等绝大多数场景
语义单元分块(按段落 / 标题)先按文本天然结构拆分(标题、段落、句子),再对超长段落二次拆分完全保留文本语义结构,不会拆分完整句子,语义完整性拉满拆分后块长度不均匀,可能出现超长块,需要二次处理结构化文档、论文、书籍、带标题层级的文档
带重叠滑动窗口分块(推荐)固定长度分块的基础上,相邻块之间保留指定长度的重叠内容解决了上下文断裂问题,同时保留长度可控的优点,是 RAG 项目首选少量内容冗余,轻微增加计算量长文本、高连贯性要求的所有 RAG 场景
智能语义分块(进阶)基于大模型 / 语义相似度模型,自动识别文本语义边界,在转折处拆分拆分效果最贴合语义逻辑,块之间语义独立性最强实现复杂、速度慢、需要调用模型,成本高法律 / 医疗等专业知识库、对检索精度要求极高的场景

PS: 带重叠的滑动窗口是什么意思?

带重叠滑动窗口(RAG 必懂)
一句话:切文本不一刀断开,后一块开头重复带上前一块末尾一小段文字,两块内容有重叠区域,防止一句话被劈成两半、关键信息丢失。

先对比两种切法(一眼看懂区别)

① 无重叠固定切块(普通一刀切)
设定:块长 500 字符,切完直接从 500 位置新开下一块
块 1:0~500
块 2:500~1000
块 3:1000~1500
坑:刚好一句话卡在 500 字符中间,前半句在块 1、后半句在块 2。用户提问只搜到块 2,后半句孤零零,看不懂前文,RAG 回答出错。
示例原文:订单超时未支付时,系统发送消息到RabbitMQ,消费者查询订单状态
一刀切拆分:
块 1:订单超时未支付时,系统发送消息到
块 2:RabbitMQ,消费者查询订单状态
搜 “消费者做什么” 只召回块 2,不知道什么场景下查订单。

② 带重叠滑动窗口(滑动切块)
参数:chunk_size=500(每块总长度),overlap=100(重叠100字符,重叠比例20%)
滑动步长 = 总块长−重叠 = 500−100=400,下一块不从 500 开始,从 400 开始
块 1:0~500(共 500 字)
块 2:400~900(从 400 起步,前面 400~500 这 100 个字和块 1 重复 = 重叠)
块 3:800~1300(800~900 和块 2 重复)
原来被劈断的那句话,后半句在块 2,前半句被重叠带进块 2 里,不管搜到块 1 还是块 2,整句话信息完整。

三个核心参数

  • chunk_size:窗口大小 = 每一块总字符长度(默认 500)
  • overlap:重叠字符数(块末尾复制到下一块开头,一般 10%~20%)
  • step:滑动步长 = chunk_size−overlap(每次往后挪多少)
    举例:500 块长、20% 重叠→重叠 100、步长 400。

为什么 RAG 一定要用重叠?

  • 解决语义断裂:避免关键词、完整句子被切在两块中间,检索缺上下文(最核心目的)
  • 提升检索命中率:跨边界的内容会同时存在两个块里,提问更容易匹配到完整信息
  • 通用行业规范:普通文档重叠 10%~20%;法律 / 医疗专业文档 20%~30%
3. 实操代码
1# -*- coding: utf-8 -*-
2"""
3@Created on  2026/6/3 15:11
4@creator  er_nao
5@File :day_86.py
6@Description :PDF长文档完整读取
7"""
8import pdfplumber
9
10pdf_file_path = "C:\\Users\\hp\\Desktop\\NLP学习数据\\arctle2.pdf"
11
12
13# 文本提取代码
14def extract_full_text_from_pdf(pdf_file_path):
15    """
16       提取PDF全量文本内容,返回拼接后的完整长文本
17       :param pdf_path: PDF文件路径
18       :return: 完整长文本字符串
19    """
20    with pdfplumber.open(pdf_file_path) as pdf:
21        full_text_list = []
22        for page in pdf.pages:
23            page_text = page.extract_text(
24                x_tolerance=0.5, y_tolerance=0.5, keep_blank_chars=False
25            )
26            if page_text.strip():
27                full_text_list.append(page_text)
28        # 拼接所有页面的文本,用换行符分隔
29        full_text = "\n".join(full_text_list)
30        print(f"PDF文本提取完成,总字符数:{len(full_text)}")
31        return full_text
32
33
34# 实操任务 1:基础固定长度文本分块代码
35
36def fixed_length_chunking(text, chunk_size=500):
37    """
38       基础固定长度文本分块函数
39       :param text: 输入的完整长文本
40       :param chunk_size: 每个文本块的最大字符数,默认500
41       :return: 分块后的文本块列表
42    """
43    # 初始化结果列表
44    chunk_list = []
45    # 计算文本总长度
46    text_length = len(text)
47    # 按固定步长遍历文本,拆分块
48    for start_index in range(0, text_length, chunk_size):
49        # 计算当前块的结束索引
50        end_index = min(start_index + chunk_size, text_length)
51        # 截取当前块的文本
52        current_chunk = text[start_index:end_index]
53        # 过滤掉空块
54        if current_chunk.strip():
55            chunk_list.append(current_chunk)
56
57    # 输出分块结果统计
58    print(f"===== 固定长度分块完成 =====")
59    print(f"总文本长度:{text_length} 字符")
60    print(f"预设块大小:{chunk_size} 字符")
61    print(f"最终分块数量:{len(chunk_list)} 个")
62    print(f"单个块最大长度:{max([len(chunk) for chunk in chunk_list])} 字符")
63    print("=============================")
64
65    return chunk_list
66
67
68# 实操任务 2:按段落 / 语义单元结构化分块代码
69def paragraph_based_chunking(text, max_chunk_size=500):
70    """
71        按段落/语义单元的结构化分块函数
72        核心逻辑:先按天然段落拆分,再对超长段落二次拆分,保证语义完整性
73        :param text: 输入的完整长文本
74        :param max_chunk_size: 单个块的最大字符数,默认1000
75        :return: 分块后的文本块列表
76    """
77    # 第一步:按换行符拆分天然段落,过滤掉空段落
78    raw_paragraphs = [p.strip() for p in text.split("\n") if p.strip()]
79    print(f"原始段落数量:{len(raw_paragraphs)}个")
80
81    # 第二步:遍历所有段落,拆分超长段落,生成最终块
82    chunk_list = []
83    for paragraph in raw_paragraphs:
84        paragraph_length = len(paragraph)
85        # 如果段落长度小于等于最大块大小,直接作为一个块
86        if paragraph_length <= max_chunk_size:
87            chunk_list.append(paragraph)
88        # 如果段落超长,进行二次固定长度拆分
89        else:
90            for start in range(0, paragraph_length, max_chunk_size):
91                end = min(start + max_chunk_size, paragraph_length)
92                sub_chunk = paragraph[start:end]
93                if sub_chunk.strip():
94                    chunk_list.append(sub_chunk)
95
96    # 输出分块结果统计
97    print(f"===== 段落结构化分块完成 =====")
98    print(f"最终分块数量:{len(chunk_list)} 个")
99    print(f"单个块最大长度:{max([len(chunk) for chunk in chunk_list])} 字符")
100    print(f"单个块最小长度:{min([len(chunk) for chunk in chunk_list])} 字符")
101    print("=============================")
102
103    return chunk_list
104
105
106# 实操任务 3:带重叠的滑动窗口分块代码(RAG 项目推荐)
107def sliding_window_chunking(text, chunk_size=500, overlap_ratio=0.2):
108    """
109       带重叠的滑动窗口分块函数(RAG项目首选方案)
110       核心逻辑:固定长度分块的基础上,相邻块之间保留指定比例的重叠内容,解决上下文断裂问题
111       :param text: 输入的完整长文本
112       :param chunk_size: 每个文本块的最大字符数,默认500
113       :param overlap_ratio: 相邻块的重叠比例,默认20%(0.2),推荐10%-30%
114       :return: 分块后的文本块列表
115   """
116    # 计算重叠的字符数
117    overlap_length = int(chunk_size * overlap_ratio)
118    # 计算每次滑动的步长(块大小 - 重叠长度)
119    step_length = chunk_size - overlap_length
120    # 初始化结果列表
121    chunk_list = []
122    text_length = len(text)
123
124    for start_index in range(0, text_length, step_length):
125        # 计算当前块的结束索引
126        end_index = min(start_index+chunk_size, text_length)
127        # 截取当前块的文本
128        current_chunk = text[start_index:end_index]
129        # 过滤掉空块
130        if current_chunk.strip():
131            chunk_list.append(current_chunk)
132
133    # 输出分块结果统计
134    print(f"===== 滑动窗口分块完成 =====")
135    print(f"预设块大小:{chunk_size}字符")
136    print(f"重叠比例:{overlap_ratio*100}%,重叠长度:{overlap_length}字符")
137    print(f"滑动步长:{step_length}字符")
138    print(f"总文本长度:{text_length}字符")
139    print(f"最终分块数量:{len(chunk_list)}个")
140    print(f"单个快最大长度:{max([len(chunk) for chunk in chunk_list])}字符")
141    print("=============================")
142
143    return chunk_list
144
145
146#  分块结果保存与校验代码
147import json
148
149# -------------------------- 结果保存路径 --------------------------
150output_chunk_path = "C:\\Users\\hp\\Desktop\\NLP学习数据\\RAG文本分块结果.json"
151# -----------------------------------------------------------------
152def save_chunks_to_file(chunk_list, save_path):
153    """
154        将分块结果保存为JSON文件,方便后续RAG系统使用
155        :param chunk_list: 分块后的文本块列表
156        :param save_path: 保存路径
157    """
158    # 结构化保存,包含块序号、块内容、块长度
159    chunk_result = []
160    for idx, chunk in enumerate(chunk_list):
161        chunk_result.append({
162            "chunk_id": idx +1,
163            "chunk_text": chunk,
164            "chunk_length": len(chunk)
165        })
166
167    with open(save_path, 'w', encoding='utf-8') as f:
168        json.dump(chunk_result, f, ensure_ascii=False, indent=2)
169
170    print(f"分块结果已保存至:{save_path}")
171    print(f"共保存 {len(chunk_result)} 个文本块")
172
173
174if __name__ == "__main__":
175    full_text = extract_full_text_from_pdf(pdf_file_path)
176
177    # 1:基础固定长度文本分块代码
178    # fixed_chunks = fixed_length_chunking(full_text, chunk_size=500)
179    # 打印前3个块,预览结果
180    # for i, chunk in enumerate(fixed_chunks[:3]):
181    #     print(f"\n--- 第{i + 1}个文本块 ---")
182    #     print(f"长度:{len(chunk)} 字符")
183    #     print(f"内容:{chunk[:200]}...")  # 只打印前200个字符预览
184
185
186    # 2:按段落 / 语义单元结构化分块代码
187    # paragraph_chunks = paragraph_based_chunking(full_text, chunk_size=500)
188    # 打印前3个块,预览结果
189    # for i, chunk in enumerate(paragraph_chunks[:3]):
190    #     print(f"\n--- 第{i + 1}个文本块 ---")
191    #     print(f"长度:{len(chunk)} 字符")
192    #     print(f"内容:{chunk[:200]}...")
193
194    # 3:带重叠的滑动窗口分块代码(RAG项目推荐)
195    sliding_chunks = sliding_window_chunking(full_text, chunk_size=500, overlap_ratio=0.2)
196    # 打印前4个块,预览相邻块的重叠效果
197    for i, chunk in enumerate(sliding_chunks[:4]):
198        print(f"\n---第{i+1}个文件块---")
199        print(f"长度:{len(chunk)}字符")
200        print(f"内容:{chunk}...")
201
202
203    #  分块结果保存与校验代码
204    save_chunks_to_file(sliding_chunks, output_chunk_path)
205
206


2.PDF长文档完整读取》 是转载文章,点击查看原文


相关推荐


Neo4j + Graph RAG 医疗知识图谱工程实践:患者教育问答真正需要的是“关系可追溯”
swipe2026/5/31

做医疗患者教育问答时,很多团队第一反应是把科普文章、指南摘要、院内宣教材料全部切片,然后丢进向量库。用户问“高血压要看哪个科”“为什么建议做血压监测”“糖尿病患者教育材料应该关联哪些检查”,系统就从文档里召回几段相似文本,再让大模型总结。 这条路能跑通,但很快会遇到一个问题:患者教育知识不是只有“文本相似”,还有大量“实体关系”。 比如“高血压”不是孤立的一段说明,它可能关联到常见表现、风险器官、建议就诊科室、常见检查、生活方式教育材料、随访提醒。用户真正想问的也不一定是“高血压是什么”,而是:


面试通关:JWT 认证与双 Token 机制深度解析
Lee川2026/5/9

面试通关:JWT 认证与双 Token 机制深度解析 本文专为面试准备,以问答形式拆解 JWT 认证体系的核心考点。每个问题都包含"面试怎么说"和"代码怎么写的"两个层面,让你既能侃侃而谈,也能落笔有物。 开篇:为什么面试官爱问 JWT? 在前后端分离成为主流的今天,身份认证 是每个系统必须解决的第一道门槛。JWT(JSON Web Token)作为无状态认证方案的代表,几乎出现在每一份后端/全栈岗位的 JD 中。面试官问 JWT,通常是在考察三个层面的理解:


【动态规划算法】(斐波那契数列模型详解)
承渊政道2026/4/29

🔥承渊政道:个人主页 ❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C++知识内容》 《Linux系统知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》 ✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介: 在算法学习的过程中,动态规划始终是一个绕不开的重要主题.它不仅是解决复杂问题的高效工具,也是面试和竞赛中出现频率极高的核心考点.对于初学者而言,动态规划之所以难,往往不在于代码实现本身,而在于如何理解


从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代
常利兵2026/4/21

从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代 引言:实时通信的需求与挑战 在当今数字化时代,互联网应用的实时交互需求日益增长。从在线聊天、股票行情实时更新,到多人协作办公、在线游戏等场景,实时通信已成为提升用户体验和业务效率的关键因素。传统的 HTTP 协议基于请求 - 响应模式,客户端发起请求,服务器被动响应,这种模式在实时通信场景中存在诸多局限性,如高延迟、高开销以及单向性(服务器无法主动推送数据,需客户端轮询) 。为了满足实时通信的需求,WebSo


公网 IP、私网 IP、路由表、转发表与 MAC 地址的关系
小红的布丁2026/4/12

引言 学习网络时,最容易混淆的不是协议流程,而是几个看起来相近、其实不在一个层面的概念,比如: 私网 IP 和公网 IP路由表和转发表“在链路上”到底是什么意思MAC 地址和 IP 地址分别属于哪一层 这篇文章把这些概念放到同一条线上梳理清楚,尽量用能直接形成画面的方式去理解。 私网 IP、公网 IP 和 NAT 到底是什么关系 很多人第一次接触家庭网络时,容易把 192.168.x.x 这类地址叫成“虚拟 IP”。这个说法不够准确,更准确的叫法应该是: 公网 IP私网 IPNAT 什么是公网


大模型推理凭什么这么贵?从GRPO到BCR,推理效率之战全解析
陆业聪2026/4/4

一个反常识的观察:推理越强,账单越贵 DeepSeek-R1 横空出世之后,"o1-style 推理"几乎成了大模型进化的标配动作。CoT、长思考链、自我反思……这些机制确实让模型在数学、代码、逻辑推理上表现亮眼。但随之而来的问题是——每一次"深度思考",都是在烧钱。 最极端的案例:让 o1 解一道简单的小学数学题,它会生成好几百个 token 的"内心独白",然后给出一个一眼就能看出来的答案。这就像雇了个博士级顾问,让他帮你决定午饭吃什么——能力没问题,但成本不对等。 这个问题的本质不是"推理


SwiftUI 如何实现 Infinite Scroll?
RickeyBoy2026/3/27

欢迎点个 star:github.com/RickeyBoy/R… 面试题:用 SwiftUI 实现一个无限滚动列表,支持分页加载。 这道题我在面试中遇到过好几次,说实话第一次答的时候以为随便写个 LazyVStack + onAppear 就完事了。后来才发现,面试官真正想考的不是你会不会用 API,而是你对状态管理、性能优化、Task 生命周期这些东西到底理解多深。 我的思路是从最简方案出发,一步步暴露问题、一步步优化。在开始写代码之前,先聊一下架构选型。 为什么选 MVVM? 先说一下


基于 Cloudflare 生态的 AI Agent 实现
Surmon2026/3/19

2026 新年的一个夜晚,窗外炮竹烟花争相闪耀,脑海里灵光一闪:我这快十年的老博客能不能也赶一波时髦,实现一个真正「有用」的智能助手? 有用 的意思是,它不能是一个只会随便聊天的机器人,而是一个真正了解我(博主)、了解博客内容的 AI 分身。它最好能事无巨细地知道我写过哪些文章,了解我的观点、立场和经历,能根据访客的问题去知识库里精准地找到最相关的内容,再结合上下文给出自然又富有意义的回答。 它应该是一张鲜活、灵动的个人名片。 这并不是一个多么复杂的需求,开源工具和商业基建也已经很成熟了,但真正


从零开发一个掘金自动发布 Skill,并上架 Clawhub
小巫debug日记2026/3/10

从零开发一个掘金自动发布 Skill,并上架 Clawhub 本文记录了一次完整的 Skill 开发旅程:从一句「帮我创建一个可以自动发布文章到掘金的 skill」开始,到最终成功上架 Clawhub,全程真实还原每一个关键决策和踩坑过程。 背景:为什么要做这个 Skill? 我日常运营一个 AI 资讯账号,每天需要将 Markdown 格式的文章发布到多个平台,包括微信公众号、小红书、掘金等。其中微信公众号和小红书已经有现成的 Skill 可以用,但掘金没有。 每次发布掘金都要: 打开


Word 中 MathType 启动慢、卡顿、卡死 | 由于某种原因,PowerPoint 无法加载MathType……
斐夷所非2026/3/2

注:本文为 “office 中 MathType 启动、加载异常” 相关合辑。 图片清晰度受引文原图所限。 略作重排,如有内容异常,请看原文。 Word 2013 中 MathType 窗口启动延迟问题分析与解决方案 香蕉君达 发布于 2026-02-19 12:12 1 现象描述 通过快捷键或功能区按钮在 Word 2013 中插入公式时,编辑窗口启动延迟时长约为 3~4 秒,对文档编辑流程造成干扰。 测试表明,若系统中已存在至少一个处于打开状态的 MathType 窗口,后续公式

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读