(从文件切分 → 数据集标注/微调数据集构建 → 模型选型 → 模型微调 →Agent Workflow架构 → Agent自动生成报告)

1 MarkDown语义分段 切分 数据清洗 不一定要用Dify 可以使用LangChain框架来处理

2 检索召回 Prompt 技术细节

3 微调模型应用场景:写报告 评估报告的分数(先搞语料 搞完后再去决定用大or小模型)

4 爬虫最好用Firecrawl去抓取 否则法律 服务器IP被封

数据/语料标注

根据具体任务选择合适的数据格式:

1
2
3
4
5
6
7
简单任务:使用基础的instruction-input-target格式

复杂分析:使用结构化的输出格式

多任务学习:在metadata中标注任务类型

质量保证:添加数据验证和清洗步骤

数据集如何构建的?

首先是文本提取,通过对多源异构数据进行文本解析,然后对提取到的文本内容进行清洗与预处理(提取错了直接扔掉,或者编写一些特定的逻辑进行文本清洗),然后对语义进行切分,最后通过自动标注(基于规则/模型)进行辅助标注,或者借助Doccano平台人工进行标注。

然后使用大模型对测试样本进行质量审核,确保数据具备清晰性和可溯源性。

image-20251114170323815

文本怎么切分的?

什么是OCR?项目中用的哪个框架?

OCR 技术,全称是光学字符识别(Optical Character Recognition),是一种将图像中的文字内容进行识别并提取的技术。

我们项目使用的是PaddleOCR,它是由百度飞桨(PaddlePaddle)团队基于其深度学习框架 PaddlePaddle 开发并开源的一个全流程、超轻量、高精度的 OCR 工具库。

百度飞桨(PaddlePaddle)团队在今年10月中旬开源了PaddleOCR-VL,在GitHub上是中国唯一一个Star数超过50k的OCR项目

image-20251114155605853

你们项目是如何来加载pdf的?

主要思路:

  • 继承LangChain中的BaseLoader类,实现init方法和lazy_load方法,将整个 pdf 的提取结果加上元数据信息作为一个 Document yield 出去。
  • 内容提取的核心逻辑是:使用PyMuPDF模块中的fitz加载PDF,用来提取文字和图片元数据信息,对于图片信息使用PaddleORC(使用的rapidocr_paddle 模块下的 RapidOCR类)识别图片中的文字,最终将结果拼接到一起。

主要流程:

  1. 使用 fitz.open() 打开 PDF。
  2. 逐页 (page) 处理。
  3. 使用 page.get_text() 提取原生文本。
  4. 使用 page.get_image_info(xrefs=True) 获取页面上的图片信息。
  5. OCR 应用: 对获取到的图片,检查其尺寸是否超过预设阈值 PDF_OCR_THRESHOLD(默认为页面宽高的 60%)。仅对大于阈值的图片执行 OCR。
  6. 处理页面旋转 (page.rotation),确保 OCR 时图像方向正确。
  7. 调用 get_ocr() 获取的 OCR 实例识别图片文字。
  8. 合并原生文本和 OCR 结果。

你们项目是如何来加载doc的?

主要思路:

  • 继承LangChain中的BaseLoader类,实现init方法和lazy_load方法,将整个 word文档 的提取结果加上元数据信息作为一个 Document yield 出去。
  • 内容提取的核心逻辑是:使用python-docx模块加载word文档,然后获取该文档的块信息(Paragraph或Table),接下来去处理每一个块。如果是段落,先把段落中的文字提取出来,然后段落中的图片使用PaddleORC(使用的rapidocr_paddle 模块下的 RapidOCR类)识别其中的文字;如果是表格,则直接遍历获取表格单元格中的信息。最终将结果拼接到一起。

主要流程:

  1. 使用 docx.Document() 打开 DOCX 文件。
  2. 定义 iter_block_items 辅助函数,用于统一遍历文档中的段落 (Paragraph) 和表格 (Table) 块。
  3. 遍历所有块:
  4. 如果是段落,提取 block.text。同时,使用 XPath (.//pic:pic, .//a:blip/@r:embed) 查找并提取段落内嵌入的图片。对提取的图片执行 OCR。
  5. 如果是表格,遍历所有单元格 (cell),提取单元格内段落的文本。
  6. 合并所有提取的文本和 OCR 结果。

你们项目ppt加载器怎么做的?

主要思路:

  • 继承LangChain中的BaseLoader类,实现init方法和lazy_load方法,将整个 ppt 的提取结果加上元数据信息作为一个 Document yield 出去。
  • 内容提取的核心逻辑是:使用python-pptx模块加载ppt,然后逐张处理PPT。在处理PPT时,首先将PPT上的形状 (shape) 按视觉顺序(top, left 坐标)排序,排序完后依次去处理每个形状。在处理形状时,如果是文本框 ,则直接提取文本;如果是表格,则直接遍历获取表格单元格中的信息;如果是图片,则使用PaddleORC(使用的rapidocr_paddle 模块下的 RapidOCR类)识别其中的文字;如果是组合形状,则进行递归调用。最终将结果拼接到一起。

主要流程:

  1. 使用 pptx.Presentation() 打开演示文稿。
  2. 逐张幻灯片 (slide) 处理。
  3. 顺序处理: 将幻灯片上的形状 (shape) 按视觉顺序(top, left 坐标)排序。
  4. 定义 extract_text 递归函数处理单个形状:
    • 提取文本框 (shape.has_text_frame) 的文本。
    • 提取表格 (shape.has_table) 内所有单元格的文本。
    • 如果形状是图片 (shape.shape_type 13),提取图片数据 (shape.image.blob`),执行 OCR。
    • 如果形状是组合 (shape.shape_type 6),递归调用 extract_text` 处理其包含的子形状。
  5. 遍历排序后的形状,调用 extract_text
  6. 合并所有提取的文本和 OCR 结果。

你们项目图片加载器怎么做的?

主要思路:

  • 继承LangChain中的BaseLoader类,实现init方法和lazy_load方法,将整个图片的提取结果加上元数据信息作为一个 Document yield 出去。
  • 内容提取的核心逻辑是:使用PaddleORC(使用的rapidocr_paddle 模块下的 RapidOCR类)识别其中的文字。

主要流程:

  1. 接收图像文件路径 img_path
  2. 调用 get_ocr() 获取 OCR 实例。
  3. 直接对图像文件执行 OCR。
  4. 将 OCR 结果(所有识别出的文本行)合并成一个字符串。

为什么自定义基于模型的语义切分器?怎么做的?

原因:通过达摩院开源的文档语义分割模型实现对文本的切分,准确度高并且效率高

主要实现逻辑:继承langchain.text_splitter.CharacterTextSplitter,对split_text方法进行了修改。修改的方式就是利用预训练的文档语义分割模型对文本进行切分。具体的切分方式调用 modelscope.pipeline 加载指定的文档分割模型(nlp_bert_document-segmentation_chinese-base,达摩院开源的文档语义分割模型)模型,将输入文本传递给模型 pipeline 进行处理,最后解析模型输出,得到分块的结果。

为什么选用这个模型?

因为 nlp_bert_document-segmentation_chinese-base 是 达摩院开源的文档语义分割模型,通过自适应滑动窗口的序列模型,对输入文本进行语义层面的段落分割。它先将整个文本作为序列输入,然后使用使用 BERT 基础模型预测每个 token 是否是段落边界,通过自适应滑动窗口动态调整处理窗口大小,提高准确性和效率。

Easy Dataset

image-20251114173411129

语料标注工具

Doccano平台 or 规则/模型自动标注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
标准格式Json格式:
{
"text": "持續落實《核心科技領軍人才管理辦法》等制度,系統劃分“中車首席科學家、中車科學家、中車首席專家、中車資深專家、中車專家”五個層級,扎實推進“兩年一選拔、四年一聘期”的動態管理機制,構建管理和技術“Y”型職業發展路徑。健全實施“八級工”職業技能等級(崗位)評價機制,完善以職業能力為導向、以工作業績為重點、注重工匠精神培育和職業道德養成的技能人才評價體系,構建“8+5”(即首席技師、特級技師、高級技師、技師、高級工、中級工、初級工、學徒工,中華技能大獎獲得者、首席技能專家、資深技能專家、技能專家、子公司技能專家)技能人才發展通道,加大“雙師型”人才培養力度。",

"议题": {
"员工":上交所合规议题,
"发展及培训":港交所合规议题,
"绿色航运":行业特色议题,
"国有资产保值增值":体制议题
},

"议题重要性": "财务重要性/影响重要性/双重重要性",

"评分": "总体得分:7 | 披露框架:5 | 披露内容:6 | 语言表达:5",

"rationale": {
"strengths": ["制定了明确制度框架", "职业发展路径系统", "动态管理机制"],
"gaps": ["缺少实施数据", "未见成效评估"],
"suggestions": ["补充培养数据", "增加案例"]
}
},

"行业": ["上交所-制造业/铁路、船舶、航空航天和其他运输设备制造业", "港交所-工业-工业工程-轨道与列车设备"],

"涉及标准": ["上交所第14号文-第五十条(三)", "港交所ESG守则-层面B3:发展及培训"],

"地区/监管框架": ["CN-SSE", "CN-HKEX"],

"年份/报告期": "FY2024",

"来源信息": {"公司匿名ID": "A0001", "页码": "55"},

"问题式指令": "请分析该公司在员工职业发展方面的制度特点,并评估其披露质量。",

"参考答案": {
"议题": ["员工","发展及培训"],
"行业": ["上交所-制造业/铁路、船舶、航空航天和其他运输设备制造业", "港交所-工业-工业工程-轨道与列车设备"],
"标准":
["上海证券交易所上市公司自律监管指引第14号-第五十条(三)","香港交易所《环境、社会及管治报告守则》-层面B3:发展及培训"],
"评分": "总体得分:7 | 披露框架:5 | 披露内容:6 | 语言表达:5",
"rationale": {
"strengths": ["制定了明确制度框架", "职业发展路径系统", "动态管理机制"],
"gaps": ["缺少实施数据", "未见成效评估"],
"suggestions": ["补充培养数据", "增加案例"]
}
}
}

为什么用Doccano平台?

Doccano是documment anotation的缩写,是一个开源的文本标注工具,我们可以用它为NLP任务的语料库进行打标。支持**情感分析,命名实体识别,文本摘要等任务。**操作非常便捷,在小型语料库上,只要数小时就能完成全部的打标工作。

image-20251115211616593

**Doccano 本身不直接支持评分机制,但可以通过多种方式实现标签评分功能。**比如序列标注+属性评分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"text": "华为发布麒麟9020芯片,实现全链路自主可控",
"entities": [
{
"start": 0,
"end": 2,
"label": "企业主体",
"attributes": {
"时效性": 5,
"影响力": 5,
"价值度": 4,
"RFM总分": 14,
"RFM等级": "A"
}
},
{
"start": 8,
"end": 16,
"label": "技术产品",
"attributes": {
"时效性": 5,
"影响力": 4,
"价值度": 5,
"RFM总分": 14,
"RFM等级": "A"
}
}
]
}

自动语音分段

为什么选择VAD?

应用场景宽泛,主要有如下应用场景:

实时通信:视频会议静音检测、VoIP通话质量优化

语音助手:降低误唤醒率(如唤醒词前的语音过滤)

音频预处理:语音识别(ASR)前的噪音过滤

边缘计算:嵌入式设备(如智能音箱、车载系统)

数据分析:通话录音自动分段、客服语音质检

VAD 模型本身非常小(10MB以下),资源消耗占用少,甚至可以在cpu上运行,同时具备超实时的音频处理速度,天然适合高并发场景(负载低,服务器可以提供高并发)

image-20251114183155336

两种ASR中的VAD开源模型

第一种就是FSMN-VAD,这个是达摩院语音团队提出的高效语音端点检测模型,用于检测输入音频中有效语音的起止时间点信息。它使用了一种称为Feedforward Sequential Memory Networks(FSMN)的神经网络结构,该结构能够有效地建模长期依赖关系。FSMN-VAD将输入的音频信号转换为时频表示,并通过FSMN网络对时频特征进行建模和分类,以决定每一帧是否包含有声音。该算法还利用了前后文语境信息,以提高语音活动检测的准确性。

FSMN-Monophone VAD模型结构如下图所示:模型结构层面,FSMN模型结构建模时可考虑上下文信息,训练和推理速度快,且时延可控;同时根据VAD模型size以及低时延的要求,对FSMN的网络结构、右看帧数进行了适配。在建模单元层面,speech信息比较丰富,仅用单类来表征学习能力有限,我们将单一speech类升级为Monophone。建模单元细分,可以避免参数平均,抽象学习能力增强,区分性更好。

image-20251115210558655

Silero-VAD是另一种用于语音活动检测的算法,它是由Silero AI团队开发的。该算法是基于深度学习的,使用了一种称为VGGish的卷积神经网络结构。Silero-VAD首先将输入的音频信号转换为时频表示,并将其作为输入提供给VGGish网络,以进行特征提取和分类。通过训练大量的语音数据,Silero-VAD能够准确地识别出声音活动的存在与否。

Silero VAD 是预训练的企业级语音端点检测模型,一个音频块 (30+ 毫秒) 在单个 CPU 线程上处理的时间不到 1 毫秒。使用批处理或 GPU 也可以显著提高性能。在某些情况下,ONNX 的运行速度甚至可以提高 4-5 倍。Silero VAD 在包含 6000 多种语言的庞大语料库上进行了训练,它在具有不同背景噪音和质量水平的不同领域的音频上表现良好,且Silero VAD 支持 8000 Hz 和 16000 Hz采样率。

FSMN-VAD和Silero-VAD都是用于语音活动检测的先进算法,它们在准确性和效率方面都取得了很好的表现。这些算法在语音识别、音频分类和语音分割等领域都有广泛的应用潜力。

如何使用fsmn-vad

输入音频文件(格式可以为:.mp3, .wav, .pcm),输出内容

有两种框架可供选择分别是阿里的funasr、开源的modelscope

1
2
3
4
5
6
7
from funasr import AutoModel

model = AutoModel(model="fsmn-vad", model_revision="v2.0.4")

wav_file = f"/user/your_wav_path/example.wav"
res = model.generate(input=wav_file)
print(res)

输出结果res:

1
2
3
4
5
6
7
8
9
10
11
12
13
# [
{
'start': 0.68, # 语音段开始的秒数
'end': 3.24, # 语音段结束的秒数
'text': 'xxx' # VAD模型检测到的文本
},
{
'start': 4.12,
'end': 7.85,
'text': 'xxx'
}
# ... 更多语音段
]

使用modelscpoe框架加载模型,进行处理:

1
2
3
4
5
6
7
8
9
10
11
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks

inference_pipeline = pipeline(
task=Tasks.voice_activity_detection,
model='iic/speech_fsmn_vad_zh-cn-16k-common-pytorch',
model_revision="v2.0.4",
)

segments_result = inference_pipeline(input='/user/your_wav_path/example.wav')
print(segments_result)

如果输入音频为pcm格式,需要给定采样率fs:

1
segments_result = inference_pipeline(input='https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/vad_example.pcm', fs=16000)

silero-vad使用

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import torch
torch.set_num_threads(1)

model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad', model='silero_vad')
(get_speech_timestamps, _, read_audio, _, _) = utils

wav = read_audio('/user/your_wav_path/example.wav')
speech_timestamps = get_speech_timestamps(wav, model)

fs = 16000

#转换单位为ms
for segment in speech_timestamps:
segment['start_ms'] = int((segment['start'] / fs) * 1000)
segment['end_ms'] = int((segment['end'] / fs) * 1000)

for segment in speech_timestamps:
print(f" [{segment['start_ms']} ,{segment['end_ms']}],")

议题评分逻辑

image-20251117135118062

为什么是RFM?

RFM模型是一种用于客户价值分析的模型,通过对客户的消费行为进行分析,将客户分为不同的类别,以便企业更好地了解客户、制定更有效的营销策略和提高客户满意度。

RFM模型也叫RFM分析,对于三个维度进行评分,分别为最近一次购买时间(Recency)、购买频率(Frequency)和消费金额(Monetary)

  • 最近一次购买时间(Recency)指客户最近一次购买产品或服务的时间,购买时间越近,说明客户越活跃,对企业的贡献也越大。
  • 购买频率(Frequency)指客户在一段时间内购买产品或服务的次数,购买频率越高,说明客户对企业的忠诚度和购买意愿越强。
  • 消费金额(Monetary)指客户在一段时间内购买产品或服务的金额,消费金额越高,说明客户的购买能力和对企业的贡献越大。

通过借鉴企业中常用的RFM模型,我们可以避免一些人为的主观判断对数据造成评分忽高忽低,数据质量不稳定的影响。

解决方案是什么?

第一种:使用jieba分词,将文章中的内容进行分词,然后针对披露框架、披露内容、语言表达分别搞一些采分点,如果文本中的语句包含这些采分点,那么就算一分,最终通过计算比例将三个维度的评分标准归一化在0-10分之间。

第二种:使用大模型进行评分,通过给一些few shot 和提示词的方式,让大模型去自动打分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
SYSTEM_PROMPT = """你是一个专业的文档质量评估专家,专门评估企业披露文档的质量。请根据以下评分标准对文档进行客观评估:

## 评分维度(总分10分)
1. **披露框架**(5分):评估文档的结构完整性、制度明确性、体系健全性
- 5分:制度框架完整,体系清晰,机制健全
- 4分:框架基本完整,部分环节待完善
- 3分:有基本框架但系统性不足
- 2分:框架零散,缺乏整合
- 1分:几乎无明确框架

2. **披露内容**(6分):评估内容的实质性、数据支撑、案例丰富度
- 6分:内容全面,数据详实,案例丰富、、、5分:内容较丰富,数据支撑较好
- 4分:基础内容完整,缺乏深度
- 3分:内容零散,系统性不强
- 2分:仅表面描述,缺乏实质性

3. **语言表达**(5分):评估表述清晰度、专业性、可读性
- 5分:表达精准,结构优美,易于理解
- 4分:表达清晰,专业性良好
- 3分:基本可读,存在表达问题
- 2分:表达混乱,影响理解
- 1分:难以理解

## 输出格式要求
请严格按照以下JSON格式输出:
{
"quality_score": {
"overall": 总体得分(1-10),
"detailed_scores": {
"disclosure_framework": 披露框架得分(1-5),
"disclosure_content": 披露内容得分(1-6),
"language_expression": 语言表达得分(1-5)
}
},
"evaluation_reasons": {
"strengths": ["优势1", "优势2", ...],
"weaknesses": ["不足1", "不足2", ...]
},
"improvement_suggestions": ["建议1", "建议2", ...]
}
"""

## 学习示例
请参考以下示例学习评分标准:
{
"document_text": "公司建立了完善的人才培养制度框架,包括《人才培养管理办法》《职业发展路径指南》等文件。构建了系统的培训体系,涵盖新员工培训、专业技能提升、管理能力发展等多个层次。建立了动态调整机制,定期评估培训效果并优化方案。",

"expected_output": {
"quality_score": {
"overall": 7,
"detailed_scores": {
"disclosure_framework": 5,
"disclosure_content": 6,
"language_expression": 5
}
},
"evaluation_reasons": {
"strengths": [
"制定了明确的制度框架",
"构建了系统的职业发展路径",
"建立了动态管理机制"
],
"weaknesses": [
"缺乏具体实施数据",
"未提及成效评估"
]
},
"improvement_suggestions": [
"建议补充人才培养的具体数据",
"可增加案例说明实施效果"
]
}
}

数据处理步骤是什么?

使用PyMUPDF加载PDF, 对文档进行切分,按照标点符号进行切块,然后循环遍历分块,有踩分点会打上标记,最后通过归一化处理,得到0-30之间的数字。

1
2
3
r_bins = [-1, 79, 255,365]
f_bins = [0, 2, 5, 130]
m_bins = [1, 69, 1199, 206300]

image-20251117141151998

公众号推文智能体

数据源输入:

  • 公众号(知识最全,附件最多,但是附件的格式可能是链接或者图片)- 效果最好,但是技术实现比较困难
  • 网站ZAKER、其他网站(知识比较一般,可能不会很稳定,附件很少见到)

数据输出:

  • 大模型整理好的文档

Firecrawl 四种抓取方式对比

抓取方式 核心目标 工作方式 适用场景 简单比喻
Scrape(抓取单个页面) 获取单个指定网页的纯净内容 输入一个具体的URL,Firecrawl 会抓取该页面,并提取正文文本,清除广告、导航栏等无关内容。 - 快速提取一篇新闻文章、一篇博客帖子或一个产品页面的内容。 - 分析某个特定网页的信息。 单页阅读器:精准地阅读并总结一本书(单个网页)的核心内容。
Crawl(爬取整个网站) 系统地抓取整个网站的所有可用页面 输入网站的根域名(如 example.com),Firecrawl 会像搜索引擎一样,跟随站内链接,尽可能多地抓取该域名下的所有页面。 - 为某个公司官网建立知识库。 - 批量下载整个文档网站的所有手册。 - 进行全面的网站内容分析。 网站复印机:遍历整栋大楼(整个网站)的每个房间(每个页面),并复印所有文件。
Map(生成网站地图) 探索和发现网站的结构,而不提取详细内容 输入一个URL,Firecrawl 会探索该页面所能链接到的所有页面,并生成一个清晰的、层次化的结构图(sitemap),显示页面之间的关联。 - 在全面抓取前,先了解网站的规模和信息架构。 - 快速发现一个网站有哪些主要栏目和内容。 - 进行SEO分析或信息架构研究。 建筑蓝图:不关心房间里具体放什么(不提取内容),只绘制出整栋大楼的房间布局和走廊连接(页面结构)。
Crawl Job(爬取任务) 执行大规模、可排队和管理的批量爬取任务 这是“Crawl”模式的企业级或高级版本。允许你提交大量、复杂的爬取任务,系统会将其加入队列依次处理,并提供任务状态、管理和结果回调等功能。 - 需要爬取成百上千个网站。 - 任务执行时间较长,需要稳定的异步处理和进度监控。 - 集成到自动化工作流中,需要接收任务完成的通知。 工业生产线:不是单次复印,而是管理一条有排队、调度、状态监控的自动化复印流水线,用于处理海量任务。

Agent Workflow架构

议题Agent搭建

RAG知识库构建:首先对文档进行解析 → 语义切分or字符切分 → Embedding嵌入 →存储到向量数据库中

dify本地化部署知识库,文件上传大小限制15MB,文件个数没有限制

dify在线部署知识库,文件上传大小限制15MB,文件个数最多5个

议题Agent流程是什么样的?

将用户的Excel类型问卷转成Json,然后解析问题和答案为KV键值对,循环获取问题,使用大模型进行查询改写,将问题进行扩写。对每一个问题生成假设答案,针对假设答案进行知识库检索,将检索到的结果进行整理,然后和原始文件一起拼接送入到大模型中进行报告的生成(这一块还有个提示词一块拼接)。

文档不经过解析直接上传到Dify不行,如何解决?

对文档先进行加载,然后解析,针对不同的文档类型使用不同的解析方式,然后拼接成文本

  • 数据增强(分词、停用词过滤,序列长度统一)
  • 元数据信息提取(目录、页面大小标题、文件名称、文件大小、文档字符数量)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
try:
# 方法1: 使用PyMuPDF(更好的文本提取)
doc = fitz.open(file_path)
metadata = doc.metadata
for page_num in range(len(doc)):
page = doc[page_num]
text = page.get_text()
if text.strip():
content_parts.append(f"第{page_num+1}页:\n{text}")
doc.close()

except Exception as e:
# 方法2: 回退到PyPDF2
try:
with open(file_path, 'rb') as file:
pdf_reader = PyPDF2.PdfReader(file)
metadata = pdf_reader.metadata
for page_num in range(len(pdf_reader.pages)):
page = pdf_reader.pages[page_num]
text = page.extract_text()
content_parts.append(f"第{page_num+1}页:\n{text}")
except Exception as e2:
raise Exception(f"PDF解析失败: {e2}")
  • 内容数据清洗(标点符号规范化,移除多余空白字符)

image-20251114200011060