文档处理模块
发布于 2025-12-31
- 1.了解不通类型文档处理的基本逻辑。
- 2.掌握文档加载和分块的基本原理。
3.1 文档解析
Section titled “3.1 文档解析”document_processor.py是EduRAG系统的核心模块之一,用于文档解析。主要负责加载多种格式的文档(如.txt、.pdf等),并对其进行分层切分,生成父块和子块,为后续的向量存储和检索做好准备。
import osfrom langchain_community.document_loaders import TextLoaderfrom langchain_community.document_loaders.markdown import UnstructuredMarkdownLoaderfrom langchain.text_splitter import MarkdownTextSplitterfrom datetime import datetimefrom edu_text_spliter import AliTextSplitter, ChineseRecursiveTextSplitterfrom edu_document_loaders import OCRPDFLoader, OCRDOCLoader, OCRPPTLoader, OCRIMGLoaderfrom base import logger, Config
conf = Config()# 定义支持的文件类型及其对应的加载器字典document_loaders = { # 文本文件使用 TextLoader ".txt": TextLoader, # PDF 文件使用 OCRPDFLoader ".pdf": OCRPDFLoader, # Word 文件使用 OCRDOCLoader ".docx": OCRDOCLoader, # PPT 文件使用 OCRPPTLoader ".ppt": OCRPPTLoader, # PPTX 文件使用 OCRPPTLoader ".pptx": OCRPPTLoader, # JPG 文件使用 OCRIMGLoader ".jpg": OCRIMGLoader, # PNG 文件使用 OCRIMGLoader ".png": OCRIMGLoader, # Markdown 文件使用 UnstructuredMarkdownLoader ".md": UnstructuredMarkdownLoader}
# 定义函数,从指定文件夹加载多种类型文件并添加元数据def load_documents_from_directory(directory_path): # 初始化空列表,用于存储加载的文档 documents = [] # 获取支持的文件扩展名集合 supported_extensions = document_loaders.keys() # 从目录名提取学科类别(如 "ai_data" -> "ai") source = os.path.basename(directory_path).replace("_data", "")
# 遍历指定目录及其子目录 for root, _, files in os.walk(directory_path): # 遍历当前目录下的所有文件 for file in files: # 构造文件的完整路径 file_path = os.path.join(root, file) # 获取文件扩展名并转换为小写 file_extension = os.path.splitext(file_path)[1].lower() # 检查文件类型是否在支持的扩展名列表中 if file_extension in supported_extensions: # 使用 try-except 捕获加载过程中的异常 try: # 根据文件扩展名获取对应的加载器类 loader_class = document_loaders[file_extension] # 实例化加载器对象,传入文件路径 if file_extension == ".txt": loader = loader_class(file_path, encoding="utf-8") else: loader = loader_class(file_path) # 调用加载器加载文档内容,返回文档列表 loaded_docs = loader.load() # 遍历加载的每个文档 for doc in loaded_docs: # 为文档添加学科类别元数据 doc.metadata["source"] = source # 为文档添加文件路径元数据 doc.metadata["file_path"] = file_path # 为文档添加当前时间戳元数据 doc.metadata["timestamp"] = datetime.now().isoformat() # 将加载的文档添加到总列表中 documents.extend(loaded_docs) # 记录成功加载文件的日志 logger.info(f"成功加载文件: {file_path}") # 捕获加载过程中可能出现的异常 except Exception as e: # 记录加载失败的日志,包含错误信息 logger.error(f"加载文件 {file_path} 失败: {str(e)}") # 如果文件类型不在支持列表中 else: # 记录警告日志,提示不支持的文件类型 logger.warning(f"不支持的文件类型: {file_path}") # 返回加载的所有文档列表 return documents
# 定义函数,处理文档并进行分层切分,返回子块结果def process_documents(directory_path, parent_chunk_size=conf.PARENT_CHUNK_SIZE, child_chunk_size=conf.CHILD_CHUNK_SIZE, chunk_overlap=conf.CHUNK_OVERLAP): # 从指定目录加载所有文档 documents = load_documents_from_directory(directory_path) # 记录加载的文档总数日志 logger.info(f"加载的文档数量: {len(documents)}")
# 初始化父块和子块分词器(通用) parent_splitter = ChineseRecursiveTextSplitter(chunk_size=parent_chunk_size, chunk_overlap=chunk_overlap) child_splitter = ChineseRecursiveTextSplitter(chunk_size=child_chunk_size, chunk_overlap=chunk_overlap) # 初始化 Markdown 专用分词器 markdown_parent_splitter = MarkdownTextSplitter(chunk_size=parent_chunk_size, chunk_overlap=chunk_overlap) markdown_child_splitter = MarkdownTextSplitter(chunk_size=child_chunk_size, chunk_overlap=chunk_overlap)
# 初始化空列表,用于存储所有子块 child_chunks = [] # 遍历每个原始文档,带上索引 i for i, doc in enumerate(documents): # print(doc) # 获取文件扩展名 file_extension = os.path.splitext(doc.metadata.get("file_path", ""))[1].lower()
# 选择切分器 is_markdown = (file_extension == ".md") parent_splitter_to_use = markdown_parent_splitter if is_markdown else parent_splitter # print(f'parent_splitter_to_use-->{parent_splitter_to_use}') child_splitter_to_use = markdown_child_splitter if is_markdown else child_splitter logger.info(f"处理文档: {doc.metadata['file_path']}, 使用切分器: {'Markdown' if is_markdown else 'ChineseRecursive'}")
# 使用父块分词器将文档切分为父块 parent_docs = parent_splitter_to_use.split_documents([doc]) # 遍历每个父块,带上索引 j for j, parent_doc in enumerate(parent_docs): # 为父块生成唯一 ID,格式为 "doc_i_parent_j" parent_id = f"doc_{i}_parent_{j}" # 将父块 ID 添加到元数据 parent_doc.metadata["parent_id"] = parent_id # 将父块内容存储到元数据 parent_doc.metadata["parent_content"] = parent_doc.page_content
# 使用子块分词器将父块切分为子块 sub_chunks = child_splitter_to_use.split_documents([parent_doc]) # 遍历每个子块,带上索引 k for k, sub_chunk in enumerate(sub_chunks): # 为子块添加父块 ID 到元数据 sub_chunk.metadata["parent_id"] = parent_id # 为子块添加父块内容到元数据 sub_chunk.metadata["parent_content"] = parent_doc.page_content # 为子块生成唯一 ID,格式为 "parent_id_child_k" sub_chunk.metadata["id"] = f"{parent_id}_child_{k}" # 将子块添加到子块列表中 child_chunks.append(sub_chunk)
# 记录子块总数日志 logger.info(f"子块数量: {len(child_chunks)}") # 返回所有子块列表 return child_chunksif __name__ == '__main__':
chunks = process_documents( '/Users/ligang/PycharmProjects/LLM/ITCAST_EduRAG/data/ai_data', conf.PARENT_CHUNK_SIZE, conf.CHILD_CHUNK_SIZE, conf.CHUNK_OVERLAP, ) print(chunks)- 文档加载:支持多种格式(如
.txt、.pdf),使用专用加载器处理复杂文档。 - 分层切分:采用
ChineseRecursiveTextSplitter生成父块和子块,优化中文文本处理。 - 元数据管理:为每个块添加唯一ID、来源和时间戳,便于检索和溯源。
document_processor模块为EduRAG系统提供了以下核心支持:
- 文档处理:实现多格式文档的高效加载和分块。
- 图片或表格:采用paddleOCR实现高效识别。
发布于 2025-12-31