上一篇深度拆解RAGFlow分片引擎!3大階段+視覺增強,全網最硬核架構解析 講了切片的整體流程,今天我們來拆下切片的實現。
我們在設置的時候,可以選擇切片方法。這個參數是parser_id
在創建知識庫的時候,選擇對應的切片方法以后,我們可以看到右側的切片介紹。
async def build_chunks(task, progress_callback): # 根據配置獲取到切片實現(策略)chunker = FACTORY[task["parser_id"].lower()]
async with chunk_limiter: cks = await trio.to_thread.run_sync(lambda: chunker.chunk(task["name"], binary=binary, from_page=task["from_page"], to_page=task["to_page"], lang=task["language"], callback=progress_callback, kb_id=task["kb_id"], parser_config=task["parser_config"], tenant_id=task["tenant_id"]))
- 在上面的代碼里根據配置
parser_id
從FACTORY
中獲取到對應的實現文件 - 注意
chunker.chunk
調用對應實現文件中的chunk
方法
from rag.app import laws, paper, presentation, manual, qa, table, book, resume, picture, naive, one, audio, email, tag
# 策略注冊(隱式接口)
FACTORY = { "general": naive, # 基礎文本處理器ParserType.NAIVE.value: naive, ParserType.PAPER.value: paper, # 學術論文處理器ParserType.BOOK.value: book, ParserType.PRESENTATION.value: presentation, ParserType.MANUAL.value: manual, ParserType.LAWS.value: laws, ParserType.QA.value: qa, ParserType.TABLE.value: table, # 表格專用處理器ParserType.RESUME.value: resume, ParserType.PICTURE.value: picture, ParserType.ONE.value: one, ParserType.AUDIO.value: audio, ParserType.EMAIL.value: email, ParserType.KG.value: naive, ParserType.TAG.value: tag
}
FACTORY
對應的 實現,就是一個配置映射,根據前端的配置,然后映射到對應的方法- 我們可以看到對應的是從
rag.app
導入的
看下代碼結構,都是對應的類文件。引入的類文件每個都有一個相同的chunk
方法
這塊代碼就是一個典型的策略模式實現。
這里要吐槽下python的隱式接口,不是自己寫的代碼,一不小心得來回翻幾遍代碼。等我過兩天給它接口顯式實現。
整塊代碼邏輯如下:
在上一篇中我們簡單的畫了下naive
的處理流程,也就是前端選擇的general
。我把流程復制過來。
通用方法里,針對不同的文件類型,有對應的實現。
接下來,我們拆解幾個定向的分片實現。
Manual
前端顯示僅支持pdf
,后端代碼支持pdf
和docx
。這塊代碼的整體處理邏輯如下
在manual
中,并沒有抽取圖片,只抽取了表格,而且類似的代碼寫了兩遍。
我又對比了manual
和naive
下pdf的處理代碼。
manual
中注重的是文檔結構化,其他的并沒有增強- 反而在
naive
模式下,通過視覺模型對圖片進行了增強 - 所以
manual
只適合沒有圖片的,有表格的pdf
laws
- 法律文本的處理,在pdf上 處理上,唯一特殊的地方只有一個垂直合并
合并邏輯如下:
book
我們看了幾個,特殊場景的處理,其實最后都是通過pdf的差異化處理實現的。
resume 簡歷
- 首先通過內部服務,會進行簡歷的處理,通過上下文,可以看到是對簡歷進行了結構化處理。
這個需要注意下,如果你源碼部署,一定要注意這個,否則就趟坑了。
然后通過結構化的關鍵詞,構建一個分片的數據結構。
qa
def rmPrefix(txt): return re.sub( r"^(問題|答案|回答|user|assistant|Q|A|Question|Answer|問|答)[\t:: ]+", "", txt.strip(), flags=re.IGNORECASE) def beAdocPdf(d, q, a, eng, image, poss): qprefix = "Question: " if eng else "問題:" aprefix = "Answer: " if eng else "回答:" d["content_with_weight"] = "\t".join( [qprefix + rmPrefix(q), aprefix + rmPrefix(a)]) d["content_ltks"] = rag_tokenizer.tokenize(q) d["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(d["content_ltks"]) d["image"] = image add_positions(d, poss) return d def beAdocDocx(d, q, a, eng, image, row_num=-1): qprefix = "Question: " if eng else "問題:" aprefix = "Answer: " if eng else "回答:" d["content_with_weight"] = "\t".join( [qprefix + rmPrefix(q), aprefix + rmPrefix(a)]) d["content_ltks"] = rag_tokenizer.tokenize(q) d["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(d["content_ltks"]) d["image"] = image if row_num >= 0: d["top_int"] = [row_num] return d def beAdoc(d, q, a, eng, row_num=-1): qprefix = "Question: " if eng else "問題:" aprefix = "Answer: " if eng else "回答:" d["content_with_weight"] = "\t".join( [qprefix + rmPrefix(q), aprefix + rmPrefix(a)]) d["content_ltks"] = rag_tokenizer.tokenize(q) d["content_sm_ltks"] = rag_tokenizer.fine_grained_tokenize(d["content_ltks"]) if row_num >= 0: d["top_int"] = [row_num] return d
我們可以看到qa就是根據不同的結構解析出來問答對。
audio
def chunk(filename, binary, tenant_id, lang, callback=None, **kwargs): doc = { "docnm_kwd": filename, "title_tks": rag_tokenizer.tokenize(re.sub(r"\.[a-zA-Z]+$", "", filename)) } doc["title_sm_tks"] = rag_tokenizer.fine_grained_tokenize(doc["title_tks"]) # is it English eng = lang.lower() == "english" # is_english(sections) try: callback(0.1, "USE Sequence2Txt LLM to transcription the audio") seq2txt_mdl = LLMBundle(tenant_id, LLMType.SPEECH2TEXT, lang=lang) ans = seq2txt_mdl.transcription(binary) callback(0.8, "Sequence2Txt LLM respond: %s ..." % ans[:32]) tokenize(doc, ans, eng) return [doc] except Exception as e: callback(prog=-1, msg=str(e)) return []
這塊的代碼更簡單,直接通過語音模型轉成了文本,然后再進行處理。
picture
圖片的解析是使用OCR處理,所以識別到的是圖片上的文本內容。使用的是deepdoc
之前測試,識別效果很一般。
圖片識別有兩種,一種是識別圖片中的文本內容,一種是通過圖片描述這個圖片是什么。我們可以通過擴展,ocr+圖片描述構建一個圖片檢索系統。
兩種實現方案:
- 直接改這塊的源碼,添加圖片理解反推
- 在外面將圖片反推后,構建圖片描述,后續我基于這個寫個案例
后記
通過代碼發現,專用處理有時候也蠻雞肋的,如果我們在外面將文檔都結構化了,很多通過一些分片策略,我們可以忽略一些專用類型。
底層的處理最后都是deepdoc
中的幾個文件。后續會針對這個再做一些源碼分析。
rag玩的是對文檔的了解,怎么能拆解出合適的分片,這個是關鍵。
市面上應該有一些處理文檔的專有模型,到時候找下看看。
系列文章
uv配置環境
dify相關
DeepSeek+dify 本地知識庫:真的太香了
Deepseek+Dify本地知識庫相關問題匯總
dify的sandbox機制,安全隔離限制
DeepSeek+dify 本地知識庫:高級應用Agent+工作流
DeepSeek+dify知識庫,查詢數據庫的兩種方式(api+直連)
DeepSeek+dify 工作流應用,自然語言查詢數據庫信息并展示
聊聊dify權限驗證的三種方案及實現
dify1.0.0版本升級及新功能預覽
Dify 1.1.0史詩級更新!新增"靈魂功能"元數據,實測竟藏致命Bug?手把手教你避坑
【避坑血淚史】80次調試!我用Dify爬蟲搭建個人知識庫全記錄
手撕Dify1.x插件報錯!從配置到網絡到Pip鏡像,一條龍排雷實錄
dify1.2.0升級,全新循環節點優化,長文寫作案例
dify1.x無網環境安裝插件
dify項目結構說明與win11本地部署
Dify 深度拆解(二):后端架構設計與啟動流程全景圖
dify應用:另類的關鍵詞檢索
ragflow相關
DeepSeek+ragflow構建企業知識庫:突然覺的dify不香了(1)
DeepSeek+ragflow構建企業知識庫之工作流,突然覺的dify又香了
DeepSeek+ragflow構建企業知識庫:高級應用篇,越折騰越覺得ragflow好玩
RAGFlow爬蟲組件使用及ragflow vs dify 組件設計對比
從8550秒到608秒!RAGFlow最新版本讓知識圖譜生成效率狂飆,終于不用通宵等結果了
以為發現的ragflow的寶藏接口,其實是一個天坑、Chrome/Selenium版本地獄
NLTK三重降噪內幕!RAGFlow檢索強悍竟是靠這三板斧
從代碼逆向RAGFlow架構:藏在18張表里的AI知識庫設計哲學
解剖RAGFlow!全網最硬核源碼架構解析
深度拆解RAGFlow分片引擎!3大階段+視覺增強,全網最硬核架構解析
深度拆解RAGFlow分片引擎之切片實現
RAGFlow核心引擎DeepDoc之PDF解析大起底:黑客級PDF解析術與致命漏洞
RAGFlow 0.18.0 實戰解讀:從 MCP 支持到插件配置的全流程揭秘
ragflow 0.19.0 圖文混排功能支持