一、 初識賽題——從迷茫到清晰
剛看到賽題時,坦白說有些不知所措。“多模態”、“RAG”、“圖文混排PDF”,這些詞匯組合在一起,聽起來就像一個龐大而復雜的工程。但當我強迫自己靜下心來,從“終點”(提交格式和評審規則)倒推整個流程后,任務的核心瞬間清晰了。
我的頓悟:
這不是一個聊天機器人,而是一個“引經據典”的“學術助理”。 我們的最終目標不是生成一段華麗但空洞的回答,而是要為
test.json
里的每一個question
,精準地提供三樣東西:answer
(答案)、filename
(來源文件)、page
(來源頁碼)。“可溯源”是王道。 評審規則里,
filename
和page
的匹配度占了總分的50%!這意味著,一個內容完美的答案如果來源錯誤,得分會大打折扣。反之,一個內容尚可但來源精準的答案,也能拿到一半的基礎分。這個發現直接決定了我的優化方向:溯源的準確性與答案內容的質量同等重要。核心矛盾: 機器(特別是LLM)天生只懂文本,而我們的知識庫(PDF)卻是圖文并茂的。如何將視覺信息(圖表)轉化成機器能理解的格式,并與文本信息建立聯系,是這個賽題最核心的挑戰。
基于以上理解,我將賽題的四大難點,用我自己的話重新梳理了一遍:
難點一(信息轉化): 怎么讓AI“看懂”圖片和表格?尤其是財報里的各種業績圖、流程圖,信息密度極高。
難點二(精準定位): 怎么在成堆的PDF中,像偵探一樣快速找到包含答案線索的那一頁、那一段?大海撈針,還不能撈錯。
難點三(忠實回答): LLM很能“腦補”,怎么給它戴上“緊箍咒”,讓它只根據我們找到的資料說話,別自由發揮?
難點四(協同優化): 如何平衡“答得對”和“找得準”這兩個目標?我的整個系統設計必須同時為這兩個指標服務。
二、 解剖Baseline——一個完整的起點
通讀了Baseline方案后,我對其設計思路有了清晰的認識。它是一套經典的文本RAG流程,巧妙地繞過了最棘手的多模態問題。
Baseline流程的兩大階段:
離線預處理(構建知識庫):
解析(Parse): 使用
fitz_parse_pdf.py
腳本,將所有PDF的純文本內容按“頁”為單位提取出來,保存成一個大的JSON文件 (all_pdf_page_chunks.json
)。向量化與索引(Index): 在
rag_from_page_chunks.py
的setup()
函數中,加載上述JSON,調用Embedding模型(如BGE-M3)將每一頁的文本內容轉換成向量,然后存入一個簡單的內存向量庫(SimpleVectorStore
)。
在線推理(回答問題):
檢索(Retrieve): 接收一個問題,同樣將其向量化,然后在向量庫中搜索最相似的Top-K個頁面文本作為上下文。
生成(Generate): 將“問題”和“檢索到的上下文”打包成一個精心設計的Prompt,喂給大語言模型(LLM,如Qwen),并指令它以JSON格式輸出答案、來源文件和頁碼。
我對Baseline的評價:
優點: 邏輯清晰,端到端完整。它是一個“麻雀雖小,五臟俱全”的系統,讓我能夠快速跑通整個流程,建立對RAG的基本認知。模塊化設計也為后續的優化提供了便利。
核心不足:
信息丟失嚴重:
PyMuPDF
(fitz
) 只提取文本,完全丟棄了所有圖表、表格信息。這是它最大的短板,直接導致所有基于視覺信息的問題都無法回答。分塊策略粗糙: 按“頁”分塊,一頁可能包含多個不相關的主題,也可能一個主題被分頁符切斷,這會嚴重影響檢索的精度。
檢索過于簡單: 單純的向量相似度搜索,容易引入噪音,干擾LLM的判斷。
這個Baseline是一個絕佳的起點,它的不足之處,恰恰就是我們上分的突破口。
三、 我的上分之路——從Baseline到更高分的進階方案
針對Baseline的不足,我設計了一個三步走的優化路線圖,從易到難,逐步提升系統性能。
第一步:低成本快速優化 (Low-hanging Fruits)
這些改動不涉及復雜的模型訓練,但能快速見效。
精調Prompt (Prompt Engineering):
目標: 降低幻覺,提高溯源準確性。
方案: 在
rag_from_page_chunks.py
中修改PROMPT_TEMPLATE
。增加嚴格指令: 明確要求LLM:“你必須嚴格依據提供的‘上下文’作答。如果‘上下文’中沒有答案,請明確回答‘根據現有信息無法回答’。你的答案必須從‘上下文’中提供的某一個‘chunk’中總結得出,并使用該‘chunk’的
file_name
和page
作為來源。”引入Few-shot示例: 在Prompt中加入1-2個高質量的問答示例,向LLM展示理想的輸入輸出格式,特別是如何從多個上下文中選擇最相關的一個作為來源。
優化分塊策略 (Smarter Chunking):
目標: 提高檢索內容的信噪比和完整性。
方案: 放棄按“頁”分塊。改用更智能的分塊方法。
策略: 使用
langchain.text_splitter
中的RecursiveCharacterTextSplitter
,按段落、標題等遞歸地切分文本。實現: 設置一個合理的塊大小(
chunk_size
,例如512個字符)和重疊大小(chunk_overlap
,例如50個字符)。重疊可以保證語義的連續性,避免關鍵信息被切斷。這將生成更小、更聚焦的知識塊,大大提升檢索精度。
第二步:攻克核心難點 (Tackling the Core Challenges)
這是拉開分數差距的關鍵一步,直接面對多模態和數據質量問題。
更換PDF解析引擎:從
PyMuPDF
到MinerU
目標: 實現真正的圖文信息提取,這是整個方案從量變到質變的一步。
方案:
棄用
fitz_parse_pdf.py
,改用mineru_pipeline_all.py
(或自己基于MinerU編寫腳本)。效果: MinerU能進行版面分析,將PDF解析為包含標題、段落、表格(轉為Markdown格式)、圖片等多元素的結構化數據。現在,我的知識庫里不僅有文本,還有了結構化的表格和被識別出的圖片。
“圖片文本化”:引入VLM生成圖像描述 (Image-to-Text)
目標: 讓系統理解圖片的內容。
方案: 這是對
MinerU
提取出的圖片信息的二次處理。工具: 調用一個強大的多模態大模型(VLM),如通義千問Qwen-VL。
流程: 遍歷所有被
MinerU
提取出的圖片,用Qwen-VL為其生成詳細的文本描述。對于圖表,Prompt可以更具引導性:“請詳細描述這張圖表。它的標題是什么?橫軸和縱軸代表什么?數據的主要趨勢和關鍵節點是什么?”融合: 將這些生成的“圖片描述”作為新的文本塊,與原始文本塊一樣,附上它們的元數據(文件名、頁碼),一起存入向量數據庫。
成果: 經過這一步,我的RAG系統雖然底層仍然是文本檢索,但知識庫已經融合了圖像信息,實現了“偽多模態”,能夠回答基于圖表內容的問題了。
第三步:向高分榜沖刺 (Advanced Optimizations)
當基礎框架搭建完畢,這些高級技巧可以進一步挖掘系統潛力。
智能檢索:引入重排模型 (Re-ranking)
目標: 解決Top-K檢索結果中噪音過多的問題,優中選優。
方案:
兩階段檢索:
召回(Recall): 先用快速的向量搜索,召回一個較大的候選集,比如Top 20個知識塊。
重排(Re-rank): 然后使用一個更強大的Cross-Encoder模型(如BGE-ReRanker),對這20個知識塊與問題的相關性進行更精細的計算和排序,最終選出Top 3或Top 5個最相關的知識塊交給LLM。
效果: 極大地提升了喂給LLM的上下文質量,是提升答案準確率的利器。
真·多模態:端到端多模態模型方案
目標: 讓模型直接看圖說話,避免“圖片->文字”過程中的信息損失。
方案:
多路召回: 當問題輸入后,不僅在文本向量庫中搜索,如果問題可能與圖像相關,也同時在圖像向量庫(用CLIP等模型構建)中搜索。
多模態生成: 將檢索到的文本塊和原始圖片文件一起作為上下文,提交給一個強大的多模態生成模型(如Qwen-VL-Max)。Prompt會變成:“請根據以下文本和圖片,回答問題...”。
挑戰: 這個方案對模型的選擇和系統流程的設計要求更高,但它是解決此類問題的最前沿、最徹底的方案。
四、 總結與反思
這次比賽的學習過程,對我來說是一次非常寶貴的經歷。我最大的收獲是:
從終局思考: 永遠先理解任務目標和評價標準,這會指引你所有技術選型的方向。
數據質量是生命線: Garbage in, garbage out。在RAG系統中,PDF解析的質量直接決定了知識庫的天花板。在
MinerU
上的投入是性價比最高的。迭代式開發: 不要妄想一步到位構建一個完美的系統。從一個能跑通的Baseline開始,通過分步優化,不斷給它“打補丁”、“升級零件”,才是最穩健的路徑。
擁抱開源工具:
Xinference
,MinerU
,HuggingFace Transformers
... 無數強大的開源工具極大地降低了我們實現復雜系統的門檻。