用自定義腳本,解鎖RAGFlow中Word復雜表格的終極圖文問答
一周前知識星球內有個星友,提了一個關于 Word 文檔中的復雜表格處理問題,根據貼出來的樣圖來看,其中有不少單元格合并的情況,以及有些單元格還嵌入了相關圖片。
這是個很有價值的問題,也算是在我前期介紹了很多期圖文混答的方案基礎上,本應該進一步延展介紹的話題。這篇就結合個工程機械的維保案例文檔,來具體介紹下自定義腳本的預處理方案,供各位參考。
這篇試圖說清楚:
RAGFlow 與 MinerU 在復雜表格處理下的局限性、如何使用 Python-docx 等庫實現把每一行表格數據都轉化為一個獨立且富含上下文的“事實”句子,以及如何可靠的提取單元格圖片和存儲實現。
以下,enjoy:
1、案例材料說明
這次演示所用的文檔,依然是歷史文章中經常使用的工程機械維保材料。鑒于原始文檔中并沒有標準的復雜表格結構,我手動做了下預處理,其中包含了合并單元格和單元格嵌入圖片的用例。
1.1結構特點
第一頁:
標準圖文對照表。這是一個相對規整的表格,但其關鍵在于將“相關圖片”作為了表格的一列。這代表了產品手冊、物料清單、故障圖例等場景,即圖像本身就是結構化數據的一部分。
第二頁:
多級合并單元格表。這個表格的復雜度高了很多,同時包含了橫向合并(如頂部的“案例基礎信息”橫跨多列)和縱向合并(如左側的“故障系統分類”縱跨多行)。這種多級表頭和行列合并的結構,在各類報告、技術規格書和復雜的流程記錄中非常普遍。
1.2解析難點
單行信息的完整性至關重要:
在進行向量化切分時,必須將同一行的所有信息作為一個完整的、富含上下文的知識塊(Chunk)來處理。如果簡單地按單元格或固定長度進行切分,就會徹底破壞這種內在邏輯,導致模型在檢索時只能找到零碎、殘缺的信息片段。
上下文補全是必要前提:
對于第二頁中被垂直合并的單元格,必須把上級標題(如“發動機故障”)自動填充到后續的邏輯行中,以確保每一行知識都具備完整的上下文,例如“(發動機故障下的)動臂側擺油缸自動伸出”。
2、三種非預處理的對比測試
在正式開始介紹自定義腳本的預處理方案前,先快速過下三種更簡單直接的方式,看下對比測試效果如何,或者說看下標準方案下的局限性如何。
2.1直接上傳 Word 到 RAGFlow
作為基線測試,我選擇直接把.docx 格式的維修案例源文件直接上傳至 RAGFlow 知識庫。測試之后發現,RAGFlow 對 Word 文檔中的表格結構有著相當不錯的解析能力。它能夠正確地保留原始的表格樣式,并且智能地處理了合并單元格,將主標題(如“故障系統分類”)自動填充到了被合并的單元格中,保證了每一行信息的上下文完整性。在純文本問答測試中,這種處理方式能夠返回準確的答案。
然而,有個明顯的短板在于其完全無法處理文檔中的圖片。上傳后,所有與故障案例相關的圖片信息都丟失了。當然根本原因在于,RAGFlow v0.19 的版本的圖片處理流程目前并不支持從.docx 文件中直接提取圖像,目前主要是針對 PDF、PPT 等格式。
2.2把 Word 另存為 PDF 后上傳 RAGFlow
為了觸發 RAGFlow 原生的圖片處理能力,我嘗試了一個看似直接的變通方法:把Word 文檔另存為 PDF 格式后再進行上傳。但是結果證明,這種做法是個很不明智的選擇。
雖然,RAGFlow 確實啟動了圖片處理模塊,但提取出的“圖片”內容完全不對。它把整個頁面,甚至是部分不相關的圖文組合錯誤地識別為了單一的圖片塊。與此同時,文本分塊也變得極度混亂,原有的表格結構被徹底打碎,失去了任何邏輯關聯。
造成這種現象的可能原因是,Word 在“另存為 PDF”的過程中,主要關注的是視覺保真度,而非邏輯結構的傳遞。生成的 PDF 很可能是一個“非結構化”或“無標簽”的 PDF,它雖然看起來和原文一樣,但已經丟失了關于哪些是文本、哪些是表格、哪些是圖片的底層元信息。RAGFlow 的 PDF 解析器在面對這種只有視覺布局、沒有邏輯結構的文檔時,無法準確地分割內容邊界,從而導致了錯誤的區塊識別和混亂的文本分塊。
2.3使用 MinerU 解析 Word 源文件
作為對比,我也測試了近期較受歡迎的解析工具 MinerU,對同一 Word 源文件的處理效果。結果同樣低于預期。MinerU 不僅與 RAGFlow 直傳 Word 一樣,沒法處理任何圖片信息,它在文本內容的提取上甚至也出現了明顯的缺失和遺漏。原文檔中部分表格的行內容未能被完整解析出來,這表明其內置的解析算法同樣難以適應這種包含多層合并單元格的復雜表格布局。
通過以上對比可以看出,無論是 RAGFlow 還是 MinerU,其內置的通用解析器在面對包含復雜表格、合并單元格及圖文混排的 Word 文檔時,都表現出明顯的局限性。這些通用工具為了兼容更廣泛的文檔類型,其解析策略往往是“最大公約數”式的,難以針對特定格式的復雜布局進行深度優化。
當然,這也恰恰凸顯了,面向特定場景的自定義預處理腳本的核心價值。通過使用 python-docx 等庫深入 docx 文檔的底層 XML 結構,從而實現精確地解析包括合并單元格在內的復雜表格,以及可靠地提取圖片二進制數據并實現自動保存到 MiniO 中,有目的地將非結構化的圖文信息轉化為對大型語言模型最友好的、富含上下文的“事實語句”。
3、解決方案框架
整個系統通過模塊化的設計,實現了從原始.docx 文件到 RAGFLow 聊天助手的端到端部署。
核心系統分為三大模塊:
3.1調度模塊 (process_docx_for_ragflow.py)
作為流程的入口和編排角色,它負責讀取.env 文件中的配置,接收用戶輸入的.docx 文件,并按順序調用其他功能模塊。
3.2文檔處理模塊 (docx_processor.py)
這是數據預處理的核心,其功能特色在于:
深度解析:能智能處理包含合并單元格的復雜表格。
MiniO 存儲:自動提取文檔中的圖片并上傳至 MinIO 對象存儲。
格式優化:將每一行表格數據轉化為對 RAG 模型友好的獨立句子,并將圖片 URL 封裝成可在 RAGFlow 中直接渲染的 HTML <img>標簽,這是確保可用性和可讀性的關鍵。
3.3RAGFlow 構建模塊 (ragflow_build.py)
負責與 RAGFlow 平臺的所有 API 交互,實現完全自動化部署,包括:
- 創建知識庫并上傳處理好的文本。
- 主動觸發并等待文檔解析完成。
- 創建聊天助手,并為其配置指定 LLM 和提示詞。
總結來說,原始文檔經過處理模塊的深度加工,生成包含 HTML 圖片標簽的結構化文本;隨后,這份優化后的文本被構建模塊無縫對接到 RAGFlow 平臺。
4、實現原理解析
要讓 RAG 系統能精準地理解表格內容,首要挑戰是必須先將 Word 中那些視覺上不規則、包含大量合并單元格的表格,轉化為程序可以理解的、規則的結構化數據。這個自定義腳本通過一個名為 get_table_as_grid 的函數,以一種精巧的算法很好地解決了這個問題。其核心原理可以概括為:在內存中重建一個與視覺布局完全一致的“虛擬網格”,并將原始單元格內容“投影”到這個網格的正確位置上。
注:下面內容偏技術向,不感興趣的可以跳過,不過還是但當涉獵下為好。
整個過程主要分為以下幾個關鍵步驟:
4.1第一步:構建標準化的“虛擬網格”
算法的第一步不是直接讀取內容,而是先創建一個空的二維列表(即矩陣),一般稱之為“虛擬網格”。這個網格的尺寸是嚴格按照表格的實際視覺行列數來定義的(例如,一個 5 行 4 列的表格)。這一步至關重要,因為它給后續不規則數據的“歸位”提供了一個規整的、標準化的“畫布”。(這點其實有些像工業物聯網中的數字孿生)
4.2第二步:維護一個“已處理坐標集”
在遍歷原始表格之前,腳本初始化了一個集合(Set)數據結構,用于實時記錄虛擬網格中已經被內容填充的坐標 (行號, 列號)。這個集合類似一個“遮罩層”,是整個算法能夠正確處理合并單元格的關鍵。它的作用是確保一旦某個單元格因合并而被填充,就不會再被后續的單元格錯誤地覆蓋。
4.3第三步:遍歷并“解碼”單元格的合并屬性
接下來,腳本會逐行、逐單元格地遍歷原始 Word 表格。對于每一個單元格,不只是簡單地讀取文本,而是深入其底層的 XML 屬性,重點解碼兩個核心屬性:
gridSpan (水平合并): 這個屬性直接告訴我們當前單元格在水平方向上占據了多少列。
vMerge (垂直合并): 這個屬性相對復雜。如果值是'restart',則表明這是垂直合并區域的起始單元格;如果屬性不存在或值為 None,則表明它是一個普通單元格,或者是被上方單元格所覆蓋的“后續單元格”。
4.4第四步:智能填充與坐標標記
在解碼了每個單元格的合并信息后,算法執行最核心的填充操作:
定位:對于當前遍歷到的單元格,算法首先在“虛擬網格”的對應行中,從左到右查找第一個未被“已處理坐標集”標記的位置。這個位置就是當前單元格內容應該被填充的左上角起點。
投影:根據上一步解碼出的 gridSpan(寬度)和 vMerge(高度,如果為 restart 則計算其跨度),腳本將當前單元格的內容(包括文本和提取出的圖片 URL)“投影”或“繪制”到虛擬網格中對應大小的矩形區域內。
標記:完成投影后,腳本立即將這個矩形區域內所有的坐標都添加到“已處理坐標集”中。
通過這個“定位 → 投影 → 標記”的循環,即使原始表格的結構再復雜,腳本也能確保每個單元格的內容都被不多不少、不重不漏地放置到虛擬網格的正確位置。最終,get_table_as_grid 函數返回的,就是一個與 Word 文檔視覺效果完全一致、數據完整的二維矩陣,為后續的“知識語句化”處理提供了可靠的數據基礎。
5、最終實現效果
從知識庫后臺的“數據分塊”視圖中可以清晰地看到,與之前所有方案的混亂分塊不同,經過自定義腳本處理后,知識庫中的每一個分塊(Chunk)都精準地對應了原始表格中的一個完整的邏輯行。
這種分塊的好處體現在兩個方面:
5.1信息的完整性
每一行數據,如“發動機冒藍煙”,其對應的“機型”、“故障原因”、“維修方案”以及“相關圖片”等所有信息,都完整地封裝在同一個知識塊中。這確保了在檢索的時候,可以一次性獲取關于該故障的全部上下文。
5.2圖文的原生綁定
最關鍵的一點是,腳本將圖片上傳至 MinIO 后,直接將返回的 URL 包裝成 HTML 的<img>標簽,并作為文本內容的一部分嵌入到知識塊中。當檢索系統命中這段文本時,圖片的 URL 也被無縫地繼承了過來。
需要特別說明的是,這里的圖片顯示方案依然是沿用了直接的 http url 的直接渲染方式,不是 RAGFlow V0.19 的這種內生方案。關于歷史文章中提到的圖片 URL 可能會被 LLM 在回答輸出時"自作聰明"的篡改問題,實測只要生成圖片名稱時保證命名的合理性,這種被修改的概率會降低很多。
5.3整體流程
用戶提問:用戶輸入問題“發動機冒藍煙的原因”。
精準檢索:RAGFlow 的檢索系統在向量數據庫中進行搜索,命中了之前構建的那個關于“發動機冒藍煙”的、包含了完整圖文信息的知識塊。
智能生成:這個知識塊被完整地提交給 LLM 作為上下文。根據預設的“工程機械專家”提示詞,LLM 從中提煉并總結出關鍵的故障原因:“1. 噴油器故障 2. 氣門間隙異常...”。
圖文并茂:由于預設的提示詞中明確要求“對于知識庫信息中包含 url 鏈接...請你務必也把鏈接信息不要做任何修改的顯示在回答中”,LLM 在生成文本答案的同時,也忠實地將知識塊中攜帶的那個<img>標簽一并放入了最終的回答里。
前端渲染:RAGFlow 的前端界面在收到包含<img>標簽的回答后,自動將其渲染為可見的圖片,從而實現了圖文并茂的最終效果。
6、One More Thing
RAGFlow 的 UI 原生并不支持點擊 Markdown(.md)文件鏈接進行預覽。為了打通這“最后一公里”的用戶體驗,我借助強大的瀏覽器擴展工具“油猴”(Tampermonkey),編寫了一個定制腳本,實現了在 RAGFlow 界面中點擊.md 文件鏈接,即可彈出窗口預覽其完整內容的功能。
動態元素監聽:聊天界面中的鏈接是動態生成的,腳本必須實時監控頁面的變化(通過 MutationObserver),才能在新鏈接出現時為其綁定事件。
事件攔截“賽跑”:RAGFlow 的前端框架自身擁有一套復雜的事件處理機制。腳本必須“搶”在框架之前,在事件捕獲階段就成功攔截用戶的點擊,并阻止其默認行為。
模擬 API 請求:為了獲取完整的 Markdown 內容,腳本需要從 localStorage 中讀取并使用當前用戶的認證令牌,然后模擬 RAGFlow 自身的 API 接口(/v1/chunk/list)向后端發起請求。
前端即時渲染:在成功獲取到 Markdown 文本后,腳本還需調用 marked.js 庫,將其動態渲染為格式優美的 HTML,并呈現在一個自定義的彈窗中。