微信扫码
添加专属顾问
我要投稿
深入探索RAGFlow分片引擎的切片实现,揭示其核心机制和策略注册。 核心内容: 1. 切片方法的选择与parser_id参数的作用 2. 代码实现中chunker.chunk方法的调用细节 3. 策略注册过程及各类处理器的应用场景
上一篇深度拆解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的隐式接口,不是自己写的代码,一不小心得来回翻几遍代码。等我过两天给它接口显式实现。
整块代码逻辑如下:
策略工厂FACTORYgeneral/naivepapertable...分片请求parser_id参数naive.pypaper.pytable.py...其他处理器统一chunk方法接口执行具体分片逻辑返回结构化分片数据
在上一篇中我们简单的画了下naive
的处理流程,也就是前端选择的general
。我把流程复制过来。
DOCXPDFExcelTXT/CodeMarkdownHTML/JSON是否输入文件格式判断DOCX解析器PDF解析器+布局识别表格解析器文本分割器MD表格提取结构化解析原始分片生成是否视觉增强?视觉模型处理图表基础分片处理分片合并Token化处理输出结构化分片
通用方法里,针对不同的文件类型,有对应的实现。
接下来,我们拆解几个定向的分片实现。
pdf
,后端代码支持pdf
和docx
。这块代码的整体处理逻辑如下PDFDOCX文件输入文件类型判断PDF解析器DOCX解析器OCR+布局分析表格识别段落结构解析表格HTML转换分块处理Token化输出
manual
中,并没有抽取图片,只抽取了表格,而且类似的代码写了两遍。manual
和naive
下pdf的处理代码。manual
中注重的是文档结构化,其他的并没有增强naive
模式下,通过视觉模型对图片进行了增强manual
只适合没有图片的,有表格的pdf合并逻辑如下:
是否是否是否排序文本块遍历相邻块是否跨页且无意义文本?删除当前块是否空文本块?计算合并特征满足禁止合并条件?跳过合并执行垂直合并
我们看了几个,特殊场景的处理,其实最后都是通过pdf的差异化处理实现的。
这个需要注意下,如果你源码部署,一定要注意这个,否则就趟坑了。
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就是根据不同的结构解析出来问答对。
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 []
这块的代码更简单,直接通过语音模型转成了文本,然后再进行处理。
图片的解析是使用OCR处理,所以识别到的是图片上的文本内容。使用的是deepdoc
之前测试,识别效果很一般。
图片识别有两种,一种是识别图片中的文本内容,一种是通过图片描述这个图片是什么。我们可以通过扩展,ocr+图片描述构建一个图片检索系统。
两种实现方案:
通过代码发现,专用处理有时候也蛮鸡肋的,如果我们在外面将文档都结构化了,很多通过一些分片策略,我们可以忽略一些专用类型。
底层的处理最后都是deepdoc
中的几个文件。后续会针对这个再做一些源码分析。
rag玩的是对文档的了解,怎么能拆解出合适的分片,这个是关键。
市面上应该有一些处理文档的专有模型,到时候找下看看。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2025-04-30
聊聊AI智能体框架MetaGPT下的RAG实践
2025-04-30
如何用大模型+RAG给宠物做一个AI健康助手(干货分享)?
2025-04-30
HiRAG:基于层级知识索引和检索的高精度RAG
2025-04-29
教程|通义Qwen 3 +Milvus,混合推理模型才是优化RAG成本的最佳范式
2025-04-29
RAG开发框架LangChain与LlamaIndex对比解析:谁更适合你的AI应用?
2025-04-29
RAG性能暴增20%!清华等推出“以笔记为中心”的深度检索增强生成框架,复杂问答效果飙升
2025-04-29
超神了,ChatWiki 支持GraphRAG,让 AI 具备垂直深度推理能力!
2025-04-29
AI 产品思维:我如何把一个 AI 应用从基础 RAG 升级到 multi-agent 架构
2024-10-27
2024-09-04
2024-07-18
2024-05-05
2024-06-20
2024-06-13
2024-07-09
2024-07-09
2024-05-19
2024-07-07
2025-04-30
2025-04-29
2025-04-29
2025-04-26
2025-04-25
2025-04-22
2025-04-22
2025-04-20