langchain txt 文档加载,分割

本文介绍了三种处理大规模文档的文本总结策略:Stuff(将文本合并)、MapReduce(对每个块独立总结再合并)和Refine(逐文档逐步完善)。作者通过实例展示了如何使用Tongyi语言模型在arXiv论文中应用这些策略。
摘要由CSDN通过智能技术生成

stuff 策略

加载 arXiv 论文,让模型总结前 2000 字

这里采用的是 stuff 策略,也就是将一大段文本。按字数分割成 N 个文本块,又合并成一个大的文本块。

对超大规模不友好,没有区分文档重要性,适合文档量较少场景

import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('key.env')  # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY')  # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"]  # 获得指定环境变量
model = Tongyi(temperature=1)


from langchain_core.prompts import PromptTemplate, format_document
from langchain_core.output_parsers import StrOutputParser
from langchain_community.document_loaders import ArxivLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 加载 arXiv 上的论文《ReAct: Synergizing Reasoning and Acting in Language Models》
loader = ArxivLoader(
    query="2210.03629",
    load_max_docs=1  # 加载第一个匹配的文档
)
docs = loader.load()
print(docs[0].metadata)

# 把文本分割成 500 字一组的切片
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=0  # 要求文本没有重叠
)
chunks = text_splitter.split_documents(docs)

# 构建 Stuff 形态(即文本直接拼合)的总结链
doc_prompt = PromptTemplate.from_template("{page_content}")
chain = (
    {
        "content": lambda docs: "\n\n".join(
            format_document(doc, doc_prompt) for doc in docs
        )
    }
    | PromptTemplate.from_template("用中文总结以下内容,不需要人物介绍,字数控制在 50 字以内:\n\n{content}")
    | model
    | StrOutputParser()
)
# 由于论文很长,我们只选取前 2000 字作为输入并调用总结链
res = chain.invoke(chunks[:4])
print(res)

MapReduce 策略

对每个文本块最一个回答,最后汇总到一起

但适合超大成千上万的文档量,因为是并行,map,reduce 需要不同提示词模版,多次调用问答模型,效率低

import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('key.env')  # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY')  # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"]  # 获得指定环境变量
model = Tongyi(temperature=1)


from functools import partial
from langchain_core.prompts import PromptTemplate, format_document
from langchain_core.output_parsers import StrOutputParser
from langchain_community.document_loaders import ArxivLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 加载 arXiv 上的论文《ReAct: Synergizing Reasoning and Acting in Language Models》
loader = ArxivLoader(query="2210.03629", load_max_docs=1)
docs = loader.load()

# 把文本分割成 500 字一组的切片
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 500,
    chunk_overlap = 50  # 允许文档重叠字数
)
chunks = text_splitter.split_documents(docs)

# 构建工具函数:将 Document 转换成字符串
document_prompt = PromptTemplate.from_template("{page_content}")
partial_format_document = partial(format_document, prompt=document_prompt)

# 构建 Map 链:对每个文档都先进行一轮总结
map_chain = (
    {"context": partial_format_document}
    | PromptTemplate.from_template("Summarize this content:\n\n{context}")
    | model
    | StrOutputParser()
)

# 构建 Reduce 链:合并之前的所有总结内容
reduce_chain = (
    {"context": lambda strs: "\n\n".join(strs)}
    | PromptTemplate.from_template("Combine these summaries:\n\n{context}")
    | model
    | StrOutputParser()
)

# 把两个链合并成 MapReduce 链
map_reduce = map_chain.map() | reduce_chain
res = map_reduce.invoke(chunks[:4], config={"max_concurrency": 5})
print(res)

Refine 策略

也是多轮,但每一轮输入都只包含一个文档,以及之前轮次的中间回答

优势:

每次只需要针对一个文档生成回答,避免过长 context;回答是逐步推理和完善的,而不是一次性塞入所有信息;可以自定义每轮的提示词模版,实现更精细的控制

劣势:

文档顺序对结果又很大影响,需要智能排序;计算时间高

import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('key.env')  # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY')  # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"]  # 获得指定环境变量
llm = Tongyi(temperature=1)

from functools import partial
from operator import itemgetter
from langchain_core.prompts import PromptTemplate, format_document
from langchain_core.output_parsers import StrOutputParser
from langchain_community.document_loaders import ArxivLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 加载 arXiv 上的论文《ReAct: Synergizing Reasoning and Acting in Language Models》
loader = ArxivLoader(query="2210.03629", load_max_docs=1)
docs = loader.load()

# 把文本分割成 500 字一组的切片
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50
)
chunks = text_splitter.split_documents(docs)

# 构建工具函数:将 Document 转换成字符串
document_prompt = PromptTemplate.from_template("{page_content}")
partial_format_document = partial(format_document, prompt=document_prompt)

# 构建 Context 链:总结第一个文档并作为后续总结的上下文
first_prompt = PromptTemplate.from_template("Summarize this content:\n\n{context}")
context_chain = {"context": partial_format_document} | first_prompt | llm | StrOutputParser()

# 构建 Refine 链:基于上下文(上一次的总结)和当前内容进一步总结
refine_prompt = PromptTemplate.from_template(
    "Here's your first summary: {prev_response}. "
    "Now add to it based on the following context: {context}"
)
refine_chain = (
        {
            "prev_response": itemgetter("prev_response"),
            "context": lambda x: partial_format_document(x["doc"]),
        }
        | refine_prompt
        | llm
        | StrOutputParser()
)


# 构建一个负责执行 Refine 循环的函数
def refine_loop(docs):
    summary = context_chain.invoke(docs[0])
    for i, doc in enumerate(docs[1:]):
        summary = refine_chain.invoke({"prev_response": summary, "doc": doc})
    return summary


res = refine_loop(chunks[:4])
print(res)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值