ChatGPT与PDF处理实战:从文本解析到智能问答系统搭建

1次阅读
没有评论

共计 2789 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

痛点分析

在处理 PDF 文档时,开发者常常会遇到几个棘手的问题:

ChatGPT 与 PDF 处理实战:从文本解析到智能问答系统搭建

  1. PDF 格式复杂性:PDF 本身是为打印设计的格式,其内部结构复杂,包含文字、图片、表格等多种元素混合排布,导致简单的文本提取方法往往无法完整保留原始文档的结构和内容。

  2. 大文档处理时的 token 限制:像 ChatGPT 这样的模型有 token 限制(通常是几千个),而很多 PDF 文档的文本量远超这个限制,需要有效的分块策略来分割文本。

  3. 特殊元素处理:表格、数学公式、图表等特殊元素在 PDF 中通常以非文本形式存在,常规的文本提取方法无法正确处理这些内容。

技术方案对比

PDF 解析库比较

  • PyPDF2:轻量级,基本文本提取功能,但对复杂格式支持有限
  • pdfplumber:提供精确的文本定位和表格提取,保持原始布局
  • pdfminer:强大的文本提取能力,支持复杂布局分析,但 API 较复杂

集成方式选择

  • 直接 API 调用:灵活但需要自行处理分块、上下文管理等
  • LangChain 框架:提供预构建的文档加载、分块和问答链,开发效率高但灵活性稍低

文本分块策略

  • 固定窗口分块:简单按字符 / 字数分割,可能切断语义
  • 语义分割:基于句子或段落边界分割,保持语义完整性但实现复杂

核心实现

使用 pdfplumber 提取文本

import pdfplumber

def extract_text_with_coords(pdf_path: str) -> list[dict]:
    """
    提取 PDF 文本及其坐标信息
    :param pdf_path: PDF 文件路径
    :return: 包含文本和坐标的字典列表
    """
    try:
        with pdfplumber.open(pdf_path) as pdf:
            pages = []
            for page in pdf.pages:
                text = page.extract_text()
                words = page.extract_words()
                pages.append({
                    'text': text,
                    'words': words,
                    'page_number': page.page_number
                })
            return pages
    except Exception as e:
        print(f"Error extracting text: {e}")
        return []

递归式文本分块算法

from typing import List

def recursive_text_split(text: str, 
                        chunk_size: int = 2000, 
                        overlap: int = 200) -> List[str]:
    """
    递归式文本分块,保持段落完整
    :param text: 输入文本
    :param chunk_size: 目标分块大小
    :param overlap: 块间重叠量
    :return: 文本块列表
    """
    if len(text) <= chunk_size:
        return [text]

    # 尝试在段落边界分割
    split_pos = text.rfind('\n\n', 0, chunk_size)
    if split_pos == -1:
        split_pos = text.rfind('\n', 0, chunk_size)
    if split_pos == -1:
        split_pos = chunk_size

    current_chunk = text[:split_pos].strip()
    remaining_text = text[max(0, split_pos - overlap):]

    return [current_chunk] + recursive_text_split(remaining_text, chunk_size, overlap)

构建问答链

from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 初始化组件
llm = OpenAI(temperature=0)
embeddings = OpenAIEmbeddings()
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=2000,
    chunk_overlap=200,
)

# 处理 PDF 文本
def build_qa_system(pdf_text: str):
    """
    构建 PDF 问答系统
    :param pdf_text: 提取的 PDF 文本
    :return: 问答链实例
    """
    try:
        # 文本分块
        chunks = text_splitter.split_text(pdf_text)

        # 创建向量存储
        vectorstore = FAISS.from_texts(chunks, embeddings)

        # 构建问答链
        qa = RetrievalQA.from_chain_type(
            llm=llm,
            chain_type="stuff",
            retriever=vectorstore.as_retriever(),
            return_source_documents=True
        )
        return qa
    except Exception as e:
        print(f"Error building QA system: {e}")
        return None

生产级优化

  1. OCR 集成:对于扫描件 PDF,可以使用 Tesseract OCR 进行文本识别
import pytesseract
from pdf2image import convert_from_path

def ocr_pdf(pdf_path: str) -> str:
    """
    对扫描件 PDF 进行 OCR 识别
    :param pdf_path: PDF 文件路径
    :return: 识别出的文本
    """
    try:
        images = convert_from_path(pdf_path)
        text = ""
        for img in images:
            text += pytesseract.image_to_string(img)
        return text
    except Exception as e:
        print(f"OCR error: {e}")
        return ""
  1. 异步化改造:使用异步框架如 FastAPI 或 Sanic 提高并发处理能力

  2. 敏感信息过滤:实现基于正则表达式或 NER 的敏感信息检测

避坑指南

  1. 编码问题:PDF 可能使用 GB18030 等编码,需检测并正确解码
  2. 字体缺失:嵌入字体缺失时考虑备用字体或 OCR 方案
  3. 成本监控:记录 API 调用次数和 token 使用量,设置预警阈值

延伸思考

  1. 如何评估不同分块策略对问答准确性的影响?
  2. 对于专业领域 PDF(如法律、医学文档),如何提升问答的专业性?
  3. 在多文档问答场景中,如何实现跨文档的关联问答?

完整可执行代码可在 Colab Notebook 中获取。

正文完
 0
评论(没有评论)