AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


提升RAG系统的回答质量:高质量企业文档解析(一)
发布日期:2024-09-02 05:28:22 浏览次数: 1810 来源:真聊技术



LLM(大型语言模型)在生成自然语言文本方面展现出强大的能力,但它们的训练数据通常来自于公开的、广泛的数据源,这意味着它们在面对特定企业的私有数据时,可能无法准确地生成相关的答案或信息。一种可行的思路是将这些专业数据用于微调大型语言模型(LLM),但这通常对技术和成本的要求都非常高。相比之下,RAG系统提供了一个更加经济和高效的解决方案。它通过在用户的原始问题后附加相关的私域数据,将其与通用LLM结合进行分析和总结。这样,RAG系统通过检索增强的方式为LLM提供更精准的信息,大大提升了最终的回答效果,如下图所示:

企业私有数据形成知识的重要性

企业的私有数据通常以文档形式存储,包括技术手册、政策文件、客户档案、研究报告等。这些文档承载了企业的核心知识,是企业运营的基础。通过将这些文档转换为可检索的结构化数据,RAG系统能够在需要时快速访问和利用这些数据,从而为LLM提供即时的参考。这一过程可以:

  • 提高回答的专业性:确保回答内容符合企业的专业要求和标准。

  • 增强数据安全性:通过私有数据的封闭检索,避免敏感信息泄露。

  • 支持动态更新:随着文档的更新,RAG系统能够不断刷新其知识库,保证回答内容的时效性。

企业私有数据存储介质的多样性

企业的私有数据通常以多种介质形式存储,这些介质承载了公司内部的关键知识和信息。常见的文档介质包括:

  1. 文本文档(如TXT、Markdown)

  2. 办公文档(如DOC、DOCX、PPT、XLSX)

  3. PDF文件

  4. 数据库(如SQL、NoSQL)

  5. 多媒体文件(如图像、音频、视频)

  6. 专有格式文件(如设计图纸、技术说明书等)

这些文档介质中的数据往往是结构化、半结构化或非结构化的,因此解析和读取这些数据对实现企业知识的自动化检索与生成至关重要。

基于DOC、DOCX文档的解析

DOCDOCX是微软Word的文件格式,广泛用于企业的各类文档编写和存储。解析这些文档类型的过程相对复杂,因为它们不仅包含文本,还可能嵌入各种复杂的元素,如表格、图像、超链接、页眉页脚、注释、修订历史等。这些元素的解析和读取涉及多个层次的技术挑战。

1. 文本内容的解析

  • 段落和标题:DOCX文件内部结构通常是通过XML来表示的,各个段落、标题有明确的标签。解析时需要识别这些标签,以提取不同层级的文本结构信息。

  • 字符样式:字体、大小、颜色等样式信息也可以通过解析XML获取,这对于保留原始文档的格式至关重要。
from docx import Document
def parse_docx_text(file_path):doc = Document(file_path)for para in doc.paragraphs:print(f"段落内容: {para.text}")

2. 表格内容的解析

  • 表格结构:表格在企业文档中非常常见,如财务报表、技术规范表等。解析表格时,需要精确识别每个单元格的数据,并保持其在表格中的位置。

  • 复杂表格:包括合并单元格、嵌套表格等,解析起来更为复杂,需要特殊的处理逻辑。
def parse_docx_tables(file_path):doc = Document(file_path)for table in doc.tables:for row in table.rows:print([cell.text for cell in row.cells])

3. 图像和其他嵌入对象的解析

  • 图像:DOCX文档中可能包含的图像可以通过解析文档内嵌的<w:drawing>标签来提取。

  • 其他对象:包括图表、公式、SmartArt等。由于这些对象可能以二进制格式存储,需要使用特定库或API来解析和处理。
def extract_images_from_docx(docx_path):doc = Document(docx_path)for rel in doc.part.rels.values():if "image" in rel.target_ref:print(f"提取图像: {rel.target_ref}")

4. 元数据的提取

  • 作者、创建日期、修订历史等元数据包含在文档的属性中,对于文档的版本控制和溯源管理非常重要。这些信息可以通过访问DOCX文件的docProps来提取。

5. 处理批量文档

  • 在企业中,批量处理大量DOCX文档是常见的需求。此时需要考虑并行处理技术,以提升解析效率,并可以考虑对解析后的内容进行语义切分,以优化后续的RAG应用。
import osfrom concurrent.futures import ProcessPoolExecutor
def process_docx_files(directory):docx_files = [f for f in os.listdir(directory) if f.endswith('.docx')]with ProcessPoolExecutor() as executor:for _ in executor.map(parse_docx_text, docx_files):pass
解析和处理企业的DOC、DOCX文档是RAG系统的重要步骤,通过精确提取文档中的结构化和非结构化信息,可以更好地实现语义切分和知识库的构建。这些处理步骤不仅确保了企业私有数据的有效利用,还极大提升了RAG系统的生成质量和检索精准度。
下面我们详细解析一段Python代码编写的DocxParser类的Docx解析方法。
解析方法代码:
def __call__(self, filename, binary=None, from_page=0, to_page=100000):self.doc = Document(filename) if not binary else Document(BytesIO(binary))pn = 0lines = []last_image = Nonefor p in self.doc.paragraphs:if pn > to_page:breakif from_page <= pn < to_page:if p.text.strip():if p.style and p.style.name == 'Caption':former_image = Noneif lines and lines[-1][1] and lines[-1][2] != 'Caption':former_image = lines[-1][1].pop()elif last_image:former_image = last_imagelast_image = Nonelines.append((self.__clean(p.text), [former_image], p.style.name))else:current_image = self.get_picture(self.doc, p)image_list = [current_image]if last_image:image_list.insert(0, last_image)last_image = Nonelines.append((self.__clean(p.text), image_list, p.style.name))else:if current_image := self.get_picture(self.doc, p):if lines:lines[-1][1].append(current_image)else:last_image = current_imagefor run in p.runs:if 'lastRenderedPageBreak' in run._element.xml:pn += 1continueif 'w:br' in run._element.xml and 'type="page"' in run._element.xml:pn += 1new_line = [(line[0], reduce(concat_img, line[1]) if line[1] else None) for line in lines]
tbls = []for tb in self.doc.tables:html= "<table>"for r in tb.rows:html += "<tr>"i = 0while i < len(r.cells):span = 1c = r.cells[i]for j in range(i+1, len(r.cells)):if c.text == r.cells[j].text:span += 1i = ji += 1html += f"<td>{c.text}</td>" if span == 1 else f"<td colspan='{span}'>{c.text}</td>"html += "</tr>"html += "</table>"tbls.append(((None, html), ""))return new_line, tbls
def __call__(self, filename, binary=None, from_page=0, to_page=100000):self.doc = Document(filename) if not binary else Document(BytesIO(binary))pn = 0lines = []last_image = None
  • 函数入口__call__是一个魔术方法,使得类实例可以像函数一样被调用。它接受文件名、二进制数据、起始页和结束页作为输入参数,并将文档加载到self.doc中。如果提供了二进制数据,则使用BytesIO进行处理。

  • 页数控制from_pageto_page参数允许我们控制从文档的哪个页开始解析,到哪个页结束。这对于处理大型文档时非常有用,可以避免一次性加载整个文档导致内存过载。
for p in self.doc.paragraphs:if pn > to_page:breakif from_page <= pn < to_page:if p.text.strip():if p.style and p.style.name == 'Caption':former_image = Noneif lines and lines[-1][1] and lines[-1][2] != 'Caption':former_image = lines[-1][1].pop()elif last_image:former_image = last_imagelast_image = Nonelines.append((self.__clean(p.text), [former_image], p.style.name))else:current_image = self.get_picture(self.doc, p)image_list = [current_image]if last_image:image_list.insert(0, last_image)last_image = Nonelines.append((self.__clean(p.text), image_list, p.style.name))else:if current_image := self.get_picture(self.doc, p):if lines:lines[-1][1].append(current_image)else:last_image = current_image
  • 段落遍历for p in self.doc.paragraphs遍历文档的所有段落。pn记录当前页码,控制页码范围内的解析。
  • 文本和图片处理

    • 段落文本:如果段落有文本内容且属于指定页码范围,则提取文本并进行清理(self.__clean)。

    • 图片处理:如果段落样式为“Caption”(说明文字),则判断是否与图片相关联,关联的图片会被提取出来并添加到lines列表中。对于普通段落,提取图片后也将其加入lines列表。
for run in p.runs:if 'lastRenderedPageBreak' in run._element.xml:pn += 1continueif 'w:br' in run._element.xml and 'type="page"' in run._element.xml:pn += 1
  • 分页符处理for run in p.runs遍历段落中的每一个运行块(run),检测是否存在分页符或手动分页符,并据此增加页码计数pn

    new_line = [(line[0], reduce(concat_img, line[1]) if line[1] else None) for line in lines]
  • 数据整合:将解析的行数据(文本+图片)进行合并处理。reduce(concat_img, line[1])会将同一行中的多张图片合并。

tbls = []for tb in self.doc.tables:html = "<table>"for r in tb.rows:html += "<tr>"i = 0while i < len(r.cells):span = 1c = r.cells[i]for j in range(i+1, len(r.cells)):if c.text == r.cells[j].text:span += 1i = ji += 1html += f"<td>{c.text}</td>" if span == 1 else f"<td colspan='{span}'>{c.text}</td>"html += "</tr>"html += "</table>"tbls.append(((None, html), ""))return new_line, tbls


  • 表格解析:遍历文档中的每一个表格,将表格内容提取并转换为HTML格式。对于单元格内容相同且连续的情况,使用colspan合并单元格。

  • 结果返回:最终函数返回提取的文本和图片组合(new_line),以及转换为HTML格式的表格(tbls)。


这段代码展示了如何精确地提取文档的核心信息,包括段落、图片和表格。通过控制解析的页码范围,可以高效处理大型文档,而对于图文并茂的文档,这段代码尤其有价值。

在RAG系统中,这样的解析步骤可以帮助企业将非结构化文档转化为结构化数据,并通过进一步的语义切分和向量化处理,为后续的检索和生成提供高质量的输入。这段代码不仅展示了文档内容提取的技术实现,还表明了在复杂文档环境下,精确的文本与图像关联处理对RAG系统的重要性。

说明:企业文档解析的内容较多,所以分成多篇来阐述,重点是Pdf的文档的解析,敬请期待



53AI,企业落地应用大模型首选服务商

产品:大模型应用平台+智能体定制开发+落地咨询服务

承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询