大模型创新应用之RAG实战篇 文档的加载与分割——山东大学软件学院项目创新实训(三)

现在我们已经初步获取到了辩论相关的文档数据集,但是面对庞大的数据集和过多的文档内容,我们就需要先对其进行分块。

分割文档,主要是因为模型处理长文本的能力有限——直接塞入大段内容很有可能会截断关键信息,而分割成小块后,既能适配模型的上下文窗口,又能提高检索效率。小块文本信息更集中,检索时更精准,避免返回无关内容;同时,合理分割(比如按段落或章节)能保留局部语义连贯性,让生成的答案更准确。此外,分割后的小文本计算和存储成本更低,适合大规模应用。

由于本项目获取到的文档都是json格式,所以本文就以json文档为例探讨文档的加载与分割

上图中是本次需要进行分块的文档数据集,本文我们采用LangChain工具来实现。

首先简单介绍一下LangChain:

LangChain 是一个开源的 Python Al应用开发框架,它提供了构建基于大模型的 AI 应用所需的模块和工具。通过 LangChain,开发者可以轻松地与大语言模型 (LLM)集成,完成文本生成、问答、翻译、对话等任务。LangChain 降低了 AI 应用开发的门槛,让任何人都可以基于 LLM 来构建属于自己的创意应用。

现在我们就用 LangChain 工具来帮助我们进行文本分块。

安装 LangChain 

可以直接通过 pip 进行安装:

pip install langchain

注意如果使用了虚拟环境(如 venv/conda),一定要确保已激活环境后再安装:

# 激活虚拟环境示例(以 venv 为例)
source venv/bin/activate  # Linux/Mac
venv\Scripts\activate     # Windows

加载 Json 文件

with open("data.json", "r") as f:  # 注意把data.json换成自己的json文件路径
    data = json.load(f)  # json文档加载

尝试运行若出现以下 UnicodeDecodeError 报错:

说明 JSON 文件的编码格式与 Python 默认的 gbk 解码器不兼容,Windows 系统下 Python 默认用 gbk 编码打开文件,但 JSON 文件可能实际是 utf-8 编码(尤其是包含中文等非 ASCII 字符时)。我们可以在打开文件时显式地指定编码格式为 utf-8:

with open("data.json", "r", encoding="utf-8") as f:  # 添加 encoding 参数
    data = json.load(f)

这样可以成功解决上述报错!

提取并分割文档内容(携带元数据)

这里我们使用 LangChain 的递归字符文本分割器,将长文本按规则切割成小片段,常用于处理大模型输入限制。以下代码是文本分割器 splitter 的定义:

splitter = RecursiveCharacterTextSplitter(
    chunk_size = 300,
    chunk_overlap = 50,
    separators = ["\n", "。", "!", "?"]
)

解释一下上述代码中具体参数的作用:

  • chunk_size 表示每个文本片段的最大长度(如 300 字符或 token,具体单位取决于配置)
  • chunk_overlap 表示相邻片段之间的重叠字符数,用于保持上下文连贯性,防止关键信息被切断
  • separators 定义了文本分割的标志,比如上述代码就表示优先按换行符、句号、感叹号、问号分割文本,逐级尝试直到满足 chunk_size

具体的工作逻辑就是:先用 “\n” 分割文本,若分割后的文本片段仍大于 chunk_size,则换用 “。” 进行分割,依此类推,最终确保每个片段不超过 chunk_size,且相邻片段有 50 单位重叠。

接下来正式进行文本分割:

chunks = []  # 存储分割后的文本块
metadata = []  # 存储每个文本块的元数据信息

# 遍历每个发言记录
# for speech in data["debate"]: (错误写法)
for entry in data:
    for speech in entry["debate"]:
        # 提取基础元数据
        base_meta = {
            # "competition": data["competition"], (错误写法)
            "competition": entry["competition"],  # 赛事名称
            # "topic": data["topic"], (错误写法)
            "topic": entry["topic"],  # 辩论主题
            "stance": speech["stance"],  # 辩手立场
            "debater": speech["debater"]  # 辩手姓名
        }

        # 分割长文本,将辩手的发言内容 utterance 分割成多个小文本块
        split_texts = splitter.split_text(speech["utterance"])

        # 为每个分割块创建记录
        for i, text in enumerate(split_texts):
            chunks.append(text)  # 将文本块存入列表
            metadata.append({
                **base_meta,  # 继承基础元数据
                "chunk_id": f"{speech['debater']}_{i}",  # 生成唯一块标识(辩手名_序号)
                "word_count": len(text)  # 统计当前块的字符数
            })

❗尤其注意上述代码中的错误写法,这里涉及到“列表”和“字典”的区别

data = [
    {  # 第一个元素是字典
        "competition": "辩论赛A",
        "topic": "主题A",
        "debate": [发言记录1, 发言记录2...]
    },
    {  # 第二个元素是字典
        "competition": "辩论赛B", 
        "topic": "主题B",
        "debate": [发言记录1, 发言记录2...]
    }
]

那么很显然在上述结构中,data是一个“列表”,“列表”中包含着多个“字典”,每个“字典”代表一个辩论场次。代码中的错误写法 data["debate"] 就试图用字符串来访问列表元素,就会报错。因此我们需要先遍历data中的每一个字典entry,然后用 entry["debate"] 就能访问到辩论场次字典中的元素了。同理在提取基础元数据时也要用 entry["competition"] 而不是 data["competition"] 。speech也一样,它用来遍历 entry["debate"] 这个列表中的每一个字典speech,然后用 speech["stance"] 访问到辩手立场元素。

分割长文本直接调用前面定义的 splitter 分割器的 split_text() 方法,最后将每个分割块的文本内容和元数据信息存储起来即可。

查看结果示例

现在chunks里就已经存好了所有分割完毕的文本段落,我们以第一块为例进行查看:

print(f"总分割块数: {len(chunks)}")
print("\n示例块内容:")
print(chunks[0][:200] + "...")  # 显示第一块的前200个字符
print("\n对应元数据:")
print(metadata[0])

运行结果如下图所示:

至此我们就完成了文档的加载与分割!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值