讓AI讀懂PPT圖表!RAG系統從60分到95分的進化之路,LlamaParse+多模態實戰全解析 原創
背景信息
近期前線人員反饋,在使用問答系統處理PPT文件時回答效果不佳。經查發現,用戶上傳的PPT內容多為圖文混排形式,包含大量圖像和統計圖表。用戶提問主要聚焦于圖表數據及其相互關系。
我這里隨意在網上找了一個PPT做嘗試,這個PPT有一張圖如下所示:
提問:"2017年大數據開發人數比2016年增加了還是減少了,具體增加或減少了多少人?"
系統返回:
根據提供的資源數據,2017年大數據開發職位的招聘人數大幅增加。具體來說,2016年大數據開發的招聘人數為5,667人,而2017年則增加到41,831人。因此,2017年大數據開發職位比2016年增加了36,164人 (41,831 - 5,667 = 36,164)。
從圖片我們可以看出明顯存在回答錯誤的問題。
問題分析
目前使用的LangChain UnstructuredPowerPointLoader在解析PPT時存在以下不足:
- 對圖文混排內容處理能力弱
- 圖表數據提取不準確
- 語義信息丟失嚴重
PPT文檔特性
- 非結構化布局:沒有固定格式,圖文表混排
- 視覺化表達:大量使用圖表而非純文本傳遞信息
- 天然分塊:每頁幻燈片構成獨立的知識單元
傳統文本提取+RAG的處理方式會丟失視覺元素中的語義信息,這正是當前系統效果不佳的主因。
解決方案
隨著現在多模態大模型的效果越來越強,我們就可以使用LVM來解決這類問題。既然僅僅參考從圖片識別出的文本回答不是很準確,那么我們可不可以考慮使用文本+原始圖片的方式送給LVM來回答呢?
首先借助LVM對PPT進行解析,可以解析出每頁幻燈片對應的文本和圖片,我們把文本進行embedding作為召回,在檢索的時候把檢索到的文本和關聯的圖片一起送給大模型用于生成。
LlamaParse
為了方便演示,這里對PPT進行解析成文本和圖片我使用的工具是LlamaParse。免費用戶每天有1k頁的額度,夠我們日常測試使用。
首先注冊賬號并登錄https://cloud.llamaindex.ai,然后打開LVM功能
接著申請項目對應的API key 就可以用來測試了,我們可以通過對應的api 來獲取每頁幻燈片的文本內容并下載每頁圖片到本地。
LlamaParse是LlamaCloud的一部分,是一個GenAI原生文檔解析器,可以為任何下游LLM用例(RAG、代理)解析復雜的文檔數據。
os.environ["LLAMA_CLOUD_API_KEY"] = "xxx"
parser = LlamaParse(
result_type="json",
use_vendor_multimodal_model=True,
vendor_multimodal_model_name="gemini-2.0-flash-001",
language="ch_sim"
)
md_result = parser.get_json_result(file_path)
doc_id = md_result[0]["job_id"]
pages = md_result[0]["pages"]
# 下載圖片到本地
parser.get_images(md_result, download_path="data_images")
如何索引
- 原文檔的每一頁PPT轉為圖片,并借助多模態模型解析成每一頁的Markdown文本
- 將每一頁的Markdown文本塊作為一個Chunk,并根據頁碼與頁面圖片關聯起來(存儲base64編碼/云路徑/本地路徑)。這樣,在檢索時可以根據文本塊找到對應的圖片
- 嵌入這些文本Chunks,并將它們存儲在向量庫中
dataset = []
docs = []
base64_map = {}
for page in pages:
md = page["md"]
page_number = page["page"]
# 查找并上傳對應頁碼的圖片
local_image_path = find_image_by_page(
"data_images", doc_id, page_number)
with open(local_image_path, "rb") as image_file:
image_base64 = base64.b64encode(image_file.read()).decode('utf-8')
# 添加到dataset
dataset.append({
'content': md,
'image_base64': image_base64,
'page_number': page_number
})
docs.append(
Document(
page_cnotallow=md,
metadata={
"page_number": page_number,
})
)
base64_map[page_number] = image_base64
vectorstore = Milvus.from_documents(
documents=docs,
embedding=embeddings,
connection_args={"host": "127.0.0.1", "port": "19530"},
drop_old=True, # Drop the old Milvus collection if it exists
collection_name="collection_ppt",
)
檢索和生成
- 從向量庫檢索關聯的塊,也就是前面對應到PPT頁面的生成文本
- 根據這些塊中的元數據,找到對應的頁面截圖base64
- 將文本塊組裝成Prompt,與找到的圖片的base64一起輸入多模態模型,等待響應
dat = vectorstore.similarity_search(query=question, k=5)
image_base64_list = []
chunk = []
for doc in dat:
page_number = doc.metadata["page_number"]
print(page_number)
image_base64 = base64_map.get(page_number)
if image_base64:
image_base64_list.append(image_base64)
chunk.append(doc.page_content)
openai_api_key = os.environ["OPENAI_API_KEY"] # 替換為你的 OpenAI API Key
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {openai_api_key}"
}
# 構建 messages 內容
messages_content = [
{"type": "text", "text": """The following is the Markdown text and image information parsed in the slide. Markdown text has attempted to convert the relevant charts into tables. Give priority to using picture information to answer questions. Use Markdown text information only when the image cannot be understood.
Here is the context:
---------
{context}
---------
Question: {question}
""".format(cnotallow="\n".join(chunk), questinotallow=question)}
]
if image_base64_list:
# 添加所有檢索到的圖片
for img_base64 in image_base64_list:
messages_content.append({
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{img_base64}"
}
})
payload = {
"model": "gpt-4.1",
"messages": [
{
"role": "user",
"content": messages_content
}
],
"temperature": 0.1,
"max_tokens": 1000# 根據需要調整
}
# 發送請求到 OpenAI API
try:
response = requests.post(
"https://api.openai-proxy.com/v1/chat/completions",
headers=headers,
jsnotallow=payload
)
response.raise_for_status() # 檢查請求是否成功
# 解析并打印結果
result = response.json()
print("OpenAI 分析結果:")
print(result["choices"][0]["message"]["content"])
except requests.exceptions.RequestException as e:
print(f"請求 OpenAI API 失敗: {e}")
if hasattr(e, 'response') and e.response:
print(f"錯誤詳情: {e.response.text}")
當我們執行query:
??2017年大數據開發人數比2016年增加了還是減少了, 具體增加或者減少了多少人??
?
返回的結果就是正確的了:
總結和優化
在測試驗證過程中,我們發現當前方案仍存在一些可優化的空間,主要涉及準確性、性能和擴展性三個方面:
- 解析準確性問題
- 多維度數據圖表(如組合柱狀圖+折線圖)
- 顏色相近的信息元素
- 非標準圖表類型
- 視覺模型對復雜圖表的解析仍存在約5-10%的偏差率
- 特別在以下場景容易出錯:
- 性能與成本考量
- 多模態模型響應時間較純文本LLM增加40-60%
- Token消耗量約為普通文本處理的2-3倍
- 高并發場景下API調用成本顯著增加
- 檢索效率挑戰
- 檢索準確率下降15-20%
- 模糊查詢的召回率問題尤為突出
- 相似頁面間容易產生干擾
- 當文檔庫擴展到100+個PPT時:
基于這些問題,我們可以做如下嘗試:
- 存儲架構:
a.使用CDN托管解析后的圖片
b.采用對象存儲+臨時URL訪問機制
c.實現緩存策略(TTL 24h)
- 模型選型:
a.評估不同多模態模型效果
b.元數據增強:借助元數據粗略過濾一些無關數據
本文轉載自公眾號AI 博物院 作者:longyunfeigu
原文鏈接:??https://mp.weixin.qq.com/s/AYdBlzBmkxQBntVX8BN29w??
