引言
在处理长文本文件(如PDF)时,我们经常会遇到文本超出语言模型(LLM)上下文窗口的问题。本文旨在探讨解决此问题的两种有效策略:通过强力分块和检索增强生成(RAG)方法来提取内容。我们将提供详细的指导和代码示例,帮助您在不同场景下选择合适的方法。
主要内容
1. 强力分块(Brute Force)
强力分块是将长文档拆分为多个块,每个块的大小适合LLM的上下文窗口,然后从每个块中提取内容。此方法简单直接,但需要注意信息跨块丢失的风险。
2. RAG方法
RAG方法先将文档分块并索引,只从相关性高的分块中提取内容。此方法可以减少无关信息的处理,从而提高效率,适合于内容密集的文档。
代码示例
设置环境与数据
首先,我们需要下载一个示例数据,如Wikipedia上的汽车文章,并加载为LangChain文档。
import re
import requests
from langchain_community.document_loaders import BSHTMLLoader
# 下载内容
response = requests.get("https://en.wikipedia.org/wiki/Car")
# 将内容写入文件
with open("car.html", "w", encoding="utf-8") as f:
f.write(response.text)
# 使用HTML解析器加载文档
loader = BSHTMLLoader("car.html")
document = loader.load()[0]
# 清理文本
document.page_content = re.sub("\n\n+", "\n", document.page_content)
print(len(document.page_content))
定义信息提取的Schema
使用Pydantic定义我们需要提取的信息结构,例如历史上的关键发展事件。
from typing import List
from langchain_core.pydantic_v1 import BaseModel, Field
class KeyDevelopment(BaseModel):
year: int = Field(..., description="发生重大历史发展的年份。")
description: str = Field(..., description="该年发生了什么事件?")
evidence: str = Field(..., description="从文章中提取信息的原句。")
class ExtractionData(BaseModel):
key_developments: List[KeyDevelopment]
强力分块方法实现
将文档分成适合LLM处理的块。
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(chunk_size=2000, chunk_overlap=20)
texts = text_splitter.split_text(document.page_content)
# 限制为前3个块以便快速运行
first_few = texts[:3]
extractions = extractor.batch([{"text": text} for text in first_few],
{"max_concurrency": 5}) # 并行处理
# 合并结果
key_developments = []
for extraction in extractions:
key_developments.extend(extraction.key_developments)
print(key_developments[:10])
RAG方法实现
使用向量存储来实现RAG方法。
from langchain_community.vectorstores import FAISS
from langchain_core.runnables import RunnableLambda
from langchain_openai import OpenAIEmbeddings
texts = text_splitter.split_text(document.page_content)
vectorstore = FAISS.from_texts(texts, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 1})
rag_extractor = {
"text": retriever | (lambda docs: docs[0].page_content)
} | extractor
results = rag_extractor.invoke("Key developments associated with cars")
for key_development in results.key_developments:
print(key_development)
常见问题和解决方案
- 块间信息丢失:在强力分块中,若信息分布在多个块中,可能导致提取不完整。可以通过增加块的重叠部分来缓解。
- 重复提取:大块重叠可能导致信息重复提取,需要进行去重处理。
- LLM生成错误数据:特别是在大文本中寻找单一事实时,可能会得到错误生成的数据,适当的上下文管理是关键。
总结和进一步学习资源
长文本处理是文本分析中的一项重要挑战。无论是选择强力分块还是RAG方法,都需要根据应用场景来权衡选择。推荐进一步学习LangChain和Pydantic的文档以获得更深刻的理解。
参考资料
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
—END—