【解密源码】 RAGFlow 切分最佳实践- naive parser 语义切块(excel & csv & txt 篇)

引言

在前两期深度解析中,我们见证了 RAGFlow 如何驯服文档处理领域的"两大巨兽"------结构复杂的 PDF 和格式丰富的 DOCX。从布局分析到表格识别,从视觉增强到语义关联,RAGFlow 展现出了处理复杂文档的卓越能力。

然而,在真实的企业知识库中,除了这些"重量级"文档外,还存在着大量"轻量级"但同样重要的数据源:结构化的 Excel 报表、纯净的 CSV 数据、简洁的文本文件,以及各种源代码文档。这些格式看似简单,却在数据处理中扮演着不可或缺的角色。

本期我们将聚焦这些结构化与半结构化数据的处理方案。与 PDF/DOCX 的复杂解析不同,Excel/CSV/TXT 文档的处理更注重数据完整性、格式保真和高效提取。RAGFlow 通过精巧的设计,为每种格式提供了最优的语义切块策略,确保从表格数据到代码文件的全面覆盖。

省流版

RAGFlow 为结构化与半结构化文档提供了精准高效的轻量级解析方案,针对不同格式特性采用最优处理策略:

Excel & CSV - 结构化数据处理

针对不同场景双模式输出。

键值对文本

统一格式解析。使用 openpyxl 库解析 excel 文件,结构化解析 csv 文件后转换成 openpyxl.Workbook 格式。

键值对文本输出。通过对 openpyxl.Workbook 格式输出数据进行解析组装后输出。

python 复制代码
# 输出:语义化的键值对文本
"姓名:张三; 部门:销售部; 销售额:150万 ------销售报表"
HTML 表格

统一格式解析后,将表格按照指定大小切分,添加 html 标签输出。

python 复制代码
# 输出:完整的HTML表格,保持原始格式
<table>
  <tr><th>姓名</th><th>部门</th><th>销售额</th></tr>
  <tr><td>张三</td><td>销售部</td><td>150万</td></tr>
</table>

设计亮点

  • 统一格式处理:CSV→Excel格式转换,统一处理流水线;
  • 分块优化:大表格按行分块,避免token超限。

TXT & 代码文档 - 半结构化文本处理

识别输入文件编码格式,解码后按照规则进行切分

设计亮点

  • 多编码自适应:chardet智能编码检测,完美处理中文;
  • 语义边界识别:基于标点和逻辑结构的分块。

手撕版

1. Excel & CSV 文档

根据解析配置,将文档按照 html 方案和默认方案进行切块。

python 复制代码
elif re.search(r"\.(csv|xlsx?)$", filename, re.IGNORECASE):
    callback(0.1, "Start to parse.")
    excel_parser = ExcelParser()
    if parser_config.get("html4excel"):
        sections = [(_, "") for _ in excel_parser.html(binary, 12) if _]
    else:
        sections = [(_, "") for _ in excel_parser(binary) if _]
    parser_config["chunk_token_num"] = 12800

1.1 ExcelParser 类

_load_excel_to_workbook:判断输入文件类型 excel 或 csv,进行相应处理。

_clean_dataframe:清理掉 Excel 中非法控制字符(\x00 - \x1F)。

_dataframe_to_workbook:统一输出格式,将 csv 文件内容转换成 openpyxl.Workbook 格式,与 excel 文件处理后格式统一,便于后续处理。

html:将输入的 excel 或 csv 文件内容转换成 html 表格字符串,并按照指定的 chunk_row 对行进行分块输出。例如 chunk_row 指定 12,则输入的表格会被切分为多个 12 行的小表格输出。

1.1.1 _load_excel_to_workbook

判断输入文件是 excel 还是 csv

  • excel 文件使用 python 库 openpyxl 直接打开处理;
  • csv 文件使用 _dataframe_to_workbook 方法处理。
python 复制代码
if not (file_head.startswith(b"PK\x03\x04") or file_head.startswith(b"\xd0\xcf\x11\xe0")):
    logging.info("Not an Excel file, converting CSV to Excel Workbook")

    try:
        file_like_object.seek(0)
        df = pd.read_csv(file_like_object)
        return RAGFlowExcelParser._dataframe_to_workbook(df)
try:
    return load_workbook(file_like_object, data_only=True)

Tips:判断 Excel 文件,因为 _load_excel_to_workbook 接收的参数是内存流(BytesIO)格式,无文件名,所以需要通过判断文件流的头 4 个字符
file_head.startswith(b"PK\x03\x04"):b"PK\x03\x04" 是 ZIP 文件头 的标志,.xlsx(Office Open XML)实际上是一个 ZIP 容器。
file_head.startswith(b"\xd0\xcf\x11\xe0")":b"\xd0\xcf\x11\xe0"(十六进制 D0 CF 11 E0)对应 OLE Compound File(Compound File Binary Format),这是传统的 .xls(BIFF)和早期 Office 二进制文件的标识。

1.1.2 ExcelParser实例化

使用解析后的表格数据,遍历每行内容拼接成文本作为文本块输出。

python 复制代码
wb = RAGFlowExcelParser._load_excel_to_workbook(file_like_object)
res = []
for sheetname in wb.sheetnames:
    ws = wb[sheetname]
    rows = list(ws.rows)
    if not rows:
        continue
    ti = list(rows[0])
    for r in list(rows[1:]):
        fields = []
        for i, c in enumerate(r):
            if not c.value:
                continue
            t = str(ti[i].value) if i < len(ti) else ""
            t += (":" if t else "") + str(c.value)
            fields.append(t)
        line = "; ".join(fields)
        if sheetname.lower().find("sheet") < 0:
            line += " ------" + sheetname
        res.append(line)
return res

2. Txt 以及其他代码文档

python 复制代码
elif re.search(r"\.(txt|py|js|java|c|cpp|h|php|go|ts|sh|cs|kt|sql)$", filename, re.IGNORECASE):
    callback(0.1, "Start to parse.")
    sections = TxtParser()(filename, binary,
                            parser_config.get("chunk_token_num", 128),
                            parser_config.get("delimiter", "\n!?;。;!?"))
    callback(0.8, "Finish parsing.")

2.1 TxtParser

parser_txt:按照传入的分隔符和 chunk size,切分文档输出。

这里简单介绍下 TxtParser() 实例化中获取文档内容部分 get_text 实现。

python 复制代码
def __call__(self, fnm, binary=None, chunk_token_num=128, delimiter="\n!?;。;!?"):
    txt = get_text(fnm, binary)
    return self.parser_txt(txt, chunk_token_num, delimiter)
2.1.1 get_text

如果传入的二进制内容,则使用从 rag.nlp 引入的方式自动推断出正确的编码,进行解码;否则直接从文件路径读取文本进行拼接返回。

python 复制代码
from rag.nlp import find_codec
def get_text(fnm: str, binary=None) -> str:
    txt = ""
    if binary:
        encoding = find_codec(binary)
        txt = binary.decode(encoding, errors="ignore")
    else:
        with open(fnm, "r") as f:
            while True:
                line = f.readline()
                if not line:
                    break
                txt += line
    return txt

find_codec 主要在不确定文件编码的情况下,使用 chardet 库自动推断出正确的编码方式,以便后续正确解码为 UTF-8 字符串。

经过切分得到 sections 后,excel,csv,txt及其它代码文档的 sections 后处理可参考《naive parser 语义切块(pdf 篇)》中 sections 后处理模块中的无图 sections 处理逻辑,经过后处理后得到最终输出的 res。

下期预告

在本期《【解密源码】RAGFlow 切分最佳实践- naive parser 语义切块(excel & csv & txt 篇)》中,我们深入剖析了 Excel 等文档在 RAGFlow 中的完整解析流水线,相比较 pdf 和 docx 文档,这类文档的处理较为简单直接。

在下一期中,我们将深入剖析 naive parser 下 .md 文件的语义切块方案。

相关推荐
York·Zhang3 小时前
AI 下的 Agent 技术全览
人工智能·大模型·agent
思绪漂移3 小时前
ReAct对“智能”做了一件什么事情
人工智能·agent
大模型教程3 小时前
RAG核心基础 Embedding 概念与技术详解
程序员·llm·agent
大模型教程3 小时前
一文搞懂大模型:何为深入理解Agent?
程序员·llm·agent
AI大模型3 小时前
一文搞懂RAG:阿里70K算法岗为什么都在用它?
程序员·llm·agent
AI大模型4 小时前
面试官狂问的 28 个 RAG 问题全解析:一次讲透,从基础到架构优化
程序员·llm·agent
Cxzzzzzzzzzz5 小时前
Kubernetes 架构
容器·架构·kubernetes
leafff1235 小时前
一文了解LLM应用架构:从Prompt到Multi-Agent
人工智能·架构·prompt
一见6 小时前
多头注意力论文的作用
大模型·llm