上下文工程-RAG:原理、技术栈、实现解析、实现效果展示(解析篇) 数据解析定义rag的pipeline解析→分块→嵌入→存储 检索→生成。解析在索引线路的第一步。大多数的实际文档对LLM而言很难解读多栏布局、换页的表格、合并的单元格、嵌入的图表、手写字、扫描页等。 文档解析的核心是将格式各异、排版复杂的非结构化数据如 PDF、Word、图片、扫描件、音频等包含的文本、版面、图片、表格等内容转换为计算机可理解的、带有逻辑层级的结构化数据如 Markdown、HTML 或 JSON的过程。为什么需要数据解析RAG性能高度依赖于输入数据的质量文档解析是数据处理的第一步如果解析丢失了结构如表格错乱、多栏排版文字混排、图片后续无论检索算法多高级、大模型多聪明召回的内容对模型来说都是无意义的噪声最终会导致生成错误的答案。技术选型深入、全面了解下pdf、doc、图片、ppt、html等文件包含的元素、并以docling的itemblock为例搜集了对应关系以下是一部分比较流行的解析器针对每种文档的元素、特性以及业务场景初步针对性的选择合适的解析器文件类型使用解析器理由ImagePaddleOCR 多模态模型qwen3.7-plus文字密集型表格、截图→ PaddleOCR 提取文字信息密集型流程图、架构图→PaddleOCR 提取文字 多模态模型qwen3.7-plus生成描述文本调用多模态会产生额外成本请按实际需求判断选择PDF、DOCX、PPTX、XLSX、CSV、HTMLdocling Image处理1、docling解析文档布局、内容、提取图片2、然后对每个图片调用Image思路生成描述将描述替换回文本流中并在文本流中添加![image](path)图片占位符。markdown、DOC、PPT、代码文件、txtllamaindex的SimpleDirectoryReader默认解析JSONllamaindex的JSONReader实现解析1、路由层根据文件类型路由到对应的解析器def parse_file(path: Path, knowledge_base_id: str) - list[Document]: 解析单个文件返回 LlamaIndex Document 列表。 if not path.exists(): raise ParserException(f文件不存在: {path}) ext path.suffix.lower() if ext not in SUPPORTED_EXTENSIONS: raise ParserException( f不支持的文件类型: {ext}, details{file_path: str(path)}, ) metadata _build_metadata(path) # 图片 → vision LLM 描述 if ext in IMAGE_EXTENSIONS: logger.info(IMAGE_EXTENSIONS use _describe_image) description _describe_image(path) return [Document(textdescription, metadatametadata.to_node_metadata())] # json使用json reader if ext in {.json, .jsonl}: logger.info(json use _parse_with_json) try: docs _parse_with_json(path, ext) if docs: return [_merge_metadata(doc, metadata) for doc in docs] except Exception: logger.warning( fJSONReader 解析失败使用默认 reader 重试: {path} ) # 旧版 Office → textract if ext in LEGACY_OFFICE_EXTENSIONS: logger.info(LEGACY_OFFICE_EXTENSIONS use _load_legacy_office) text _load_legacy_office(path) return [Document(texttext, metadatametadata.to_node_metadata())] # DOCLING支持的类型先尝试 Docling失败则 fallback 到默认 reader if ext in DOCLING_EXTENSIONS: logger.info(DOCLING_EXTENSIONS use _parse_with_docling) try: docs _parse_with_docling(path, ext) if docs: return [_merge_metadata(doc, metadata) for doc in docs] except Exception: logger.warning( fDocling 解析失败使用默认 reader 重试: {path} ) # 兜底其余的文件类型直接用 LlamaIndex 默认 SimpleDirectoryReader logger.info(OTHERS use _parse_with_default) try: docs _parse_with_default(path) if docs: return [_merge_metadata(doc, metadata) for doc in docs] except Exception as e: raise ParserException( f无法解析文件: {path}, details{file_path: str(path), file_type: ext}, ) from e2、解析器3.1、Image解析器def describe_image(path: Path) - str: 先 OCR 提取文字再根据规则决定是否调用 vision LLM 生成描述。 ocr_text _extract_image_text_with_ocr(path) image_size _get_image_size(path) if not if_need_vlm(ocr_text, path, image_size): return (f【图片文字识别】\n{ocr_text}\n\n ) logger.info(f图片命中图表/流程图规则触发多模态描述: {path}) vision_description _call_vision_llm(path, ocr_text) return ( f【图片文字识别】\n{ocr_text}\n\n f【图片内容描述】\n{vision_description} ) def _extract_image_text_with_ocr(path: Path) - str: 使用 PaddleOCR 提取图片中的文字失败时返回空字符串。 try: ocr _get_paddle_ocr() result ocr.ocr(str(path)) except ImportError: logger.warning(fPaddleOCR 未安装跳过图片 OCR: {path}) return except Exception as e: logger.warning(f图片 OCR 识别失败: {path}, 错误: {e}) return if not result or result[0] is None: return texts: list[str] [] for line in result[0]: if line and len(line) 2: texts.append(str(line[1][0])) return \n.join(texts) def _call_vision_llm(path: Path, ocr_text: str) - str: 使用 vision LLM 描述图像内容OCR 文字作为上下文传入。 from openai import OpenAI as SyncOpenAI try: client SyncOpenAI( api_keysettings.vision_llm_api_key, base_urlsettings.vision_llm_api_base, ) image_bytes path.read_bytes() b64_image base64.b64encode(image_bytes).decode(utf-8) prompt IMAGE_DESCRIPTION_USER_PROMPT_TEMPLATE.format(ocr_textocr_text) response client.chat.completions.create( modelsettings.vision_llm_model, messages[ { role: user, content: [ { type: text, text: prompt, }, { type: image_url, image_url: { url: fdata:image/{_image_mime(path)};base64,{b64_image} }, }, ], }, ], temperaturesettings.vision_llm_temperature, max_tokenssettings.vision_llm_max_tokens, ) return response.choices[0].message.content or except Exception as e: raise ExternalServiceException( f图像描述失败: {path}, servicesettings.vision_llm_provider, original_errore, ) from e3.2、docling解析器llamaindex有默认集成了docling可直接使用from llama_index.core.schema import Document from docling.datamodel.document import ConversionResult from docling_core.types.doc.base import ImageRefMode def _parse_with_docling(path: Path, ext: str) - list[Document]: 用 Docling 解析文件提取图片并生成描述注入文本流。 converter _get_docling_converter() result: ConversionResult converter.convert(path) if result.status ConversionStatus.FAILURE: raise ParserException( fDocling 解析失败: {path}, details{file_path: str(path), file_type: ext}, ) dl_doc result.document if not dl_doc.pictures: text dl_doc.export_to_markdown( image_placeholder, image_modeImageRefMode.PLACEHOLDER, ) return [Document(texttext)] artifacts_dir _docling_artifacts_dir(path) image_paths _extract_docling_pictures(dl_doc, artifacts_dir) # 调用 vision LLM 生成图片描述 descriptions [_describe_docling_image(p) for p in image_paths] markdown dl_doc.export_to_markdown( image_placeholder_IMAGE_PLACEHOLDER, image_modeImageRefMode.PLACEHOLDER, ) text _inject_image_descriptions(markdown, image_paths, descriptions) return [Document(texttext)] def _get_docling_converter() - DocumentConverter: 获取或初始化 Docling DocumentConverter 实例懒加载。 global _docling_converter if _docling_converter is None: # 配置 PDF 图片提取 pipeline_options PdfPipelineOptions( do_table_structureTrue, # 表格结构 generate_page_imagesTrue, # 生成页面图片 generate_picture_imagesTrue, # ✅ 关键提取文档中的图片 images_scale2.0, # 图片缩放比例 ) _docling_converter DocumentConverter( format_options{ InputFormat.PDF: PdfFormatOption(pipeline_optionspipeline_options), # DOCX 使用默认配置图片自动提取 # InputFormat.DOCX: 不需要特殊配置 # PPTX 使用默认配置图片自动提取 # InputFormat.PPTX: 不需要特殊配置 # 其他格式HTML、MD、TXT 等无需图片提取配置 }) return _docling_converter3.3、SimpleDirectoryReader解析器def _parse_with_default(path: Path) - list[Document]: 用 LlamaIndex 默认 reader 解析文件fallback。 reader SimpleDirectoryReader( input_files[str(path)], filename_as_idTrue, ) return list(reader.load_data())3.4、JSONReader解析器llamaindex官为json单独定制了jsonreader特别针对json文件高质的提取信息。def _parse_with_json(path: Path, ext: str) - list[Document]: 用 JSON reader 解析文件。 reader JSONReader() return list(reader.load_data(input_filestr(path)))数据解析效果展示1、pdf测试重点复杂表格与跨页表格、多栏布局与复杂排版、图文表混排与上下文关联1.1、 复杂表格与跨页表格使用“600107_20260430_XFBG.pdf”第5页的跨页表格 “3.1 近 3 年的主要会计数据和财务指标”测试此表格包含复杂表头、合并单元格、以及跨越两页的长表格解析结果结果分析能将表格精准转换为 Markdown 而不是将其拆成散装的纯文本 。1.2、 多栏布局与复杂排版使用“财务趋势2026”第3页的“引言”测试此部分为双栏的复杂布局。解析结果结果分析解析器能否正确识别多栏布局避免将左栏第一行和右栏第一行强行拼接在一起导致语义完全错乱。1.3、 图文表混排与上下文关联使用“财务趋势2026”第7页的图文测试此部分为图文混排。解析结果结果分析能否精准定位图表等非文本区域并保留其与周围文本的上下文关联如页码、相对位置2、image测试重点基础的paddleocr文字提取能力、多模态LLM识别图表流程、复杂表格2.1、基础的paddleocr文字提取能力验证这是图片这是提取结果可以看到提取的内容完整、准确2.2、多模态LLM识别流程图效果可以看到图片内容和流程逻辑都很准确 、清晰的识别出来了 。2.3、复杂表格图效果可以看到识别出了内容能精准还原表格结构而不是将其解析为混乱的纯文本 。3、docx测试点跨页表格、嵌套列表、图文混排、隐藏文本测试文档 详见此文章页面顶部的“word文档解析测试.docx”3.1、跨页表格解析效果跨页表格能自动补全表头3.2、嵌套列表解析效果列表样式没有丢失3.3、图文混排解析结果将图片设置为“四周型环绕”也能提取并识别图片内容3.4、隐藏文本解析结果这一段关键信息设置为白色字体或隐藏属性 可以正常提取