链载Ai

标题: 企业AI知识库的文件解析痛点-Word格式解析优化(准确率95%)-100%开源 [打印本页]

作者: 链载Ai    时间: 2 小时前
标题: 企业AI知识库的文件解析痛点-Word格式解析优化(准确率95%)-100%开源

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;display: table;padding: 0.3em 1em;color: rgb(255, 255, 255);background: rgb(59, 53, 53);border-radius: 8px;box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px;">一、前言

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-indent: 2em;letter-spacing: 0.1em;color: rgb(63, 63, 63);">在大模型和RAG(检索增强生成)技术飞速发展的今天,ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: inherit;color: rgb(59, 53, 53);">企业AI知识库建设已成为AI落地的核心战场。而文件解析是所有参与做企业AI知识库开发者所避免不了的难题。

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-indent: 2em;letter-spacing: 0.1em;color: rgb(63, 63, 63);">本文将结合我在开发TorchV AISingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: inherit;color: rgb(59, 53, 53);">企业级AI知识库产品中碰到的解析Word的问题,将Word文档(ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-feature-settings: normal;font-variation-settings: normal;font-size: 14.4px;text-align: left;line-height: 1.75;color: rgb(221, 17, 68);background: rgba(27, 31, 35, 0.05);padding: 3px 5px;border-radius: 4px;">.docingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-feature-settings: normal;font-variation-settings: normal;font-size: 14.4px;text-align: left;line-height: 1.75;color: rgb(221, 17, 68);background: rgba(27, 31, 35, 0.05);padding: 3px 5px;border-radius: 4px;">.docx)高效、准确地转换为Web友好格式(Markdown/HTML)优化目标的方案进行分享。结合目前的实际解决方案(依靠Apache Tika/POI),详细阐述一种创新的、具备行业领先水平的解决方案。通过实现一套ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: inherit;color: rgb(59, 53, 53);">完全基于表格结构、无需硬编码的通用算法,完美解决了ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-feature-settings: normal;font-variation-settings: normal;font-size: 14.4px;text-align: left;line-height: 1.75;color: rgb(221, 17, 68);background: rgba(27, 31, 35, 0.05);padding: 3px 5px;border-radius: 4px;">.docingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-feature-settings: normal;font-variation-settings: normal;font-size: 14.4px;text-align: left;line-height: 1.75;color: rgb(221, 17, 68);background: rgba(27, 31, 35, 0.05);padding: 3px 5px;border-radius: 4px;">.docx格式中的复杂表格解析问题,确保转换后的HTML与源文件在视觉和结构上高度一致。

二、Word文档解析:企业AI知识库的隐形瓶颈?

据我们的调研和实践经验:

1、两种文件格式,两种世界

2、传统工具的局限

3、企业现状

三、企业AI知识库的诉求

对于做企业AI知识库的服务商而言,Word的解析是一个全面综合去考虑分析的一个问题。对于我们而言,我们考虑的主要方向包括:

1、解析格式需要支持doc\docx两种格式

我知道现有的很多开源工具、商业产品在处理Word格式上,对于解析doc格式都倍感恼火。感觉这么一个上世纪微软留下的产物,让开发者恨的牙痒痒,不亚于10年前开发者去解决IE6的各种兼容性问题。然而在我们的客户里面,特别是金融、政府领域的客户,依然存在着大量的老的doc的格式文件,这些文件依然是企业的数字资产,在AI时代,都需要坐上这趟高速列车,把数据装载进入企业AI知识库里面,发挥知识的价值。

2、Word文档能够解析提取成Markdown格式,提取的元素包括:(文档Header、表格、图片)

企业AI知识库最关心的是“知识的结构化”。解析文档的目标不只是转换文本,更是为了“识别并提取有价值的知识结构”。因此:

3、表格的内容需要用Html呈现,解决合并(rowspan/colspan)单元格的问题

Markdown 表格天生不支持合并单元格(rowspan/colspan),而这正是企业文档中极为常见的复杂表格结构(如绩效考核表、组织结构、流程图表等):

TorchV AIS企业AI知识库,提供了强大的富文本编辑器,对于Word的格式内容,对于表格内容就需要解析成标准的Html格式进行展现,丰富企业的知识二次加工创作。


4、图片正确提取,并且图片的和文档内容上下位置正确。

Word 中的图文混排结构(例如图解流程、插图说明等)是许多专业知识文档的重要组成:

5、性能以及成本考虑

不同企业对解析能力的投入存在差异,因此服务端需提供灵活选择:

四、TorchV技术实现路径:"预处理+注入"策略

TorchV目前采用Java语言开发,在底层技术栈上,对于Word的解析,目前恰好有2个非常好的开源软件可以利用:

虽然在底层技术支持上,这2个组件已经可以做的非常好了,但是对于上面我们第二点(企业AI知识库)的诉求,我们还是需要手工干预一下,才能解析得到我们想要的结果。

1、这2个工具在表格的处理、图片的处理提取上面,默认的策略都不太能满足我们的诉求,因此需要做更深层次的定制改动。

2、我们依赖上面这两个组件做优化,这2个组件在CPU环境下能够快速的解析处理,所依赖的服务资源非常少的

1. FileMagic:差异化处理策略

对于doc/docx,我们需要从底层做差异处理,两种格式是完全不一样的解析策略,针对不同格式采用不同的解析方案

// 检测文件格式
FileMagicfileMagic=FileMagicUtils.checkMagic(wordFile);
if(fileMagic == FileMagic.UNKNOWN) {
Stringsuffix=FileUtil.getSuffix(wordFile.getName());
if(StrUtil.equalsIgnoreCase(suffix,"docx")) {
fileMagic = FileMagic.OOXML;
}elseif(StrUtil.equalsIgnoreCase(suffix,"doc")) {
fileMagic = FileMagic.OLE2;
}
}

log.info("检测到文件格式: {}, 文件: {}", fileMagic, wordFile.getAbsolutePath());

// 根据文件格式选择不同的处理方式
switch(fileMagic) {
caseOOXML -> {
// DOCX 格式 - 使用自定义表格解析器
log.info("DOCX格式使用自定义表格解析器");
transfer = processDocxWithHtmlTable(wordFile, target);
}
caseOLE2 -> {
// DOC 格式 - 使用新的HTML表格支持
transfer = processDocWithHtmlTable(wordFile, target);
}
default-> {
log.warn("不支持的文件格式: {}, 使用默认处理方式", fileMagic);
// 回退到原始转换
target = toMarkdown(wordFile, pictures);
transfer =true;
}
}

2. DOCX格式处理策略

核心思路:利用DOCX格式的标准化特性,采用"预处理+替换"模式

利用WordTableParser类,单独精准解析docx格式中的所有表格。然后在Tika处理完的Content内容上,做replace替换。

privatestaticbooleanprocessDocxWithHtmlTable(File docxFile, File target){
// 1. 预处理阶段:使用WordTableParser精准解析表格
XWPFDocumentdocument=newXWPFDocument(inputStream);
WordTableParserwordTableParser=newWordTableParser();
List<String> customTablesHtmlList = wordTableParser.parseToHtmlList(document);

// 2. 常规解析:使用Tika进行流式解析
AutoDetectParserparser=newAutoDetectParser();
parser.parse(parseStream, textHandler, metadata, parseContext);

// 3. 智能替换:用精准的表格HTML替换Tika的粗糙输出
StringfinalContent=replaceTablesWithCustomHtmlList(tikaContent, customTablesHtmlList);
}

因为docx格式是相对标准的格式,POI在底层上给我们封装了非常强大的上层API供开发者调用,能够拿到docx中的所有table元素对象,这样让我们在处理表格合并时更方便。

publicclassWordTableParser{

privatefinalCellMergeAnalyzer cellMergeAnalyzer;
privatefinalHtmlTableBuilder htmlTableBuilder;

publicWordTableParser(){
this.cellMergeAnalyzer =newCellMergeAnalyzer();
this.htmlTableBuilder =newHtmlTableBuilder();
}

publicWordTableParser(CellMergeAnalyzer cellMergeAnalyzer, HtmlTableBuilder htmlTableBuilder){
this.cellMergeAnalyzer = cellMergeAnalyzer;
this.htmlTableBuilder = htmlTableBuilder;
}

/**
* 解析 Word 文档中的所有表格为 HTML
*/
publicStringparseToHtml(XWPFDocument document){
// 拿到所有table
List<XWPFTable> tables = document.getTables();
log.info("开始解析Word表格,共 {} 个表格", tables.size());

StringBuilderhtml=newStringBuilder();
for(inti=0; i < tables.size(); i++) {
XWPFTabletable=tables.get(i);
log.info("解析第 {} 个表格,包含 {} 行", i +1, table.getRows().size());

html.append(parseTableToHtml(table));

if(i < tables.size() -1) {
html.append("<br/><br/>\n");
}
}

returnhtml.toString();
}
// more....
}

我们在处理docx格式时,抽象一个CellMergeAnalyzer的来处理表格合并的情况。

3. DOC格式处理策略

核心思路:直接在解析过程中注入我们的表格处理逻辑

扩写Tika下的Handler模块,单独处理doc格式的文件。

privatestaticbooleanprocessDocWithHtmlTable(File docFile, File target){
// 使用专门的DOC内容处理器
DocMarkdownWithHtmlTableContentHandlerhandler=newDocMarkdownWithHtmlTableContentHandler();
handler.setCurrentDocument(document);

// 在解析过程中实时应用我们的表格算法
DocXMarkdownWithHtmlTableContentHandlertextHandler=
newDocXMarkdownWithHtmlTableContentHandler(handler, metadata);
}

doc格式和docx天然差异,可以算是一个新文件类型也不为过,对于表格的解析合并内容,会有明显的不同。

主要体现在提取的Table元素,并不能很好的获取单元格合并信息,需要有一套通用的合并单元格识别算法

我们的在DocumentTableParser实现了完全基于表格结构特征的通用算法,不依赖任何具体内容:

1. 数据结构构建

// 构建表格数据矩阵
String[][] cellContents =newString[numRows][];
int[] rowCellCounts =newint[numRows];

2. 列合并检测算法

privatestaticintdetectColspanByStructure(introwIndex,intcellIndex,
String[][] cellContents,int[] rowCellCounts,intmaxCols){

intcurrentRowCells=rowCellCounts[rowIndex];

// 如果当前行只有1个单元格,占满整行
if(currentRowCells ==1) {
returnmaxCols;
}

// 智能分配列数
intaverageColspan=maxCols / currentRowCells;
intremainingCols=maxCols % currentRowCells;

// 最后一个单元格处理剩余空间
if(cellIndex == currentRowCells -1&& remainingCols >0) {
returnaverageColspan + remainingCols;
}

returnaverageColspan >0? averageColspan :1;
}

3. 行合并检测算法

privatestaticintdetectRowspanByStructure(introwIndex,intcellIndex,
String cellText, String[][] cellContents,int[] rowCellCounts){

// 检查向下相邻行的对应位置
introwspan=1;
for(intnextRow=rowIndex +1; nextRow < cellContents.length; nextRow++) {
StringnextCellText=cellContents[nextRow][cellIndex];

if(nextCellText.trim().isEmpty()) {
// 进一步验证:检查该行是否有其他内容
booleanhasContentInRow=false;
for(inti=0; i < rowCellCounts[nextRow]; i++) {
if(!cellContents[nextRow][i].trim().isEmpty()) {
hasContentInRow =true;
break;
}
}

if(hasContentInRow) {
rowspan++;
}else{
break;// 整行为空,不是rowspan
}
}else{
break;// 遇到非空单元格,rowspan结束
}
}

returnrowspan;
}

4. 智能跳过算法

privatestaticbooleanshouldSkipCellByStructure(introwIndex,intcellIndex,
String cellText, String[][] cellContents,int[] rowCellCounts){

// 向上查找可能的rowspan源
for(intprevRow=rowIndex -1; prevRow >=0; prevRow--) {
if(cellIndex < rowCellCounts[prevRow]) {
StringprevCellText=cellContents[prevRow][cellIndex];

if(!prevCellText.trim().isEmpty()) {
// 重新计算该单元格的rowspan
intcalculatedRowspan=calculateRowspanFromPosition(
prevRow, cellIndex, cellContents, rowCellCounts);

// 判断是否覆盖到当前行
if(prevRow + calculatedRowspan > rowIndex) {
returntrue;// 跳过被占用的单元格
}
}
}
}

returnfalse;
}

五、解析效果验证

我让大语言模型帮我创建了30种不同格式的复杂表格类型,包括上下单元格合并、跨页表格、左右单元格合并等多种复杂的情况,验证了doc/docx两种格式。主要表现如下:

在31个复杂表格情况下,对于表格的合并情况:

1、简单的上下合并情况


2、跨页上下单元格合并单元格的情况


3、上下左右单元格合并的情况


4、翻页单元格上下左右合并的情况



5、更复杂的纵向横向表格合并情况


六、100%开源

TorchV 的企业级 AI 知识库系统,是在开源生态的基础上构建而成的。从创业初期开始,我们便充分受益于业界开源的各种大语言模型、Embedding 模型、Reranker 模型、开源组件/中间件等,这些技术的开放极大地推动了行业的发展。我们深知开源的价值,也始终秉持开放、共享、共建的理念。

在构建开发产品的过程中,特别是在 Word 文档解析这一关键环节,我们团队进行了深入的研究和持续的优化,并在实践中取得了一些突破。尽管当前的解析组件仍存在一些待完善之处,或在测试覆盖面上尚有不足,通过开源的形式开放出来,希望借此与更多开发者携手,共同完善这一能力模块,推动行业工具链的进步。

我们诚挚欢迎社区开发者参与共建,一起打磨这项通用而关键的能力。

GitHub:https://github.com/torchv/torchv-unstructured

Gitee: https://gitee.com/torchv/torchv-unstructured

该仓库是一个Java项目,1.0.0版本已经推送Maven中央仓库,坐标地址:

<dependency>
<groupId>com.torchv.infra</groupId>
<artifactId>torchv-unstructured</artifactId>
<version>1.0.0</version>
</dependency>








欢迎光临 链载Ai (https://www.lianzai.com/) Powered by Discuz! X3.5