LangChain多数据源生成
引言
随着大语言模型(LLM)应用场景的不断扩展,从多种数据源获取和处理信息的能力变得越来越重要。LangChain框架提供了丰富的数据加载器,可以从各种来源加载数据,包括CSV、HTML、Markdown、PDF等多种格式。本教程将详细介绍如何使用LangChain加载和处理不同类型的数据源,以便在下游任务中使用。
1. LangChain数据源概述
LangChain与各种数据源有数百个集成,可以从中加载数据:Slack、Notion、Google Drive等。每个文档加载器都有自己特定的参数,但它们可以通过相同的方式使用.load()
方法调用。这种统一的接口使得从不同来源加载数据变得简单而一致。

2. 环境准备
首先,我们需要安装必要的依赖库:
bash
# 安装主要依赖
pip install langchain langchain-community langchain-openai unstructured beautifulsoup4 pypdf markdown rapidocr-onnxruntime python-dotenv
# 对于特定数据源,可能需要额外的依赖
pip install pandas html2text
3. 加载CSV文件
3.1 CSV文件简介
逗号分隔值(CSV)文件是一种使用逗号分隔值的定界文本文件。文件的每一行是一个数据记录。每个记录由一个或多个字段组成,字段之间用逗号分隔。
3.2 基本用法
LangChain实现了一个CSVLoader,可以将CSV文件加载为一系列Document对象。CSV文件的每一行都会被翻译为一个文档。
python
from langchain_community.document_loaders import CSVLoader
# 创建一个CSV加载器
loader = CSVLoader(file_path="./data/sample_data.csv")
# 加载数据
documents = loader.load()
# 查看加载的文档
print(f"加载了 {len(documents)} 个文档")
print(f"第一个文档的内容: {documents[0].page_content}")
print(f"第一个文档的元数据: {documents[0].metadata}")
3.3 自定义CSV解析和加载
CSVLoader接受一个csv_args
关键字参数,用于自定义传递给Python的csv.DictReader
的参数。有关支持的csv参数的更多信息,请参阅csv模块文档。
python
from langchain_community.document_loaders import CSVLoader
# 创建一个带有自定义参数的CSV加载器
loader = CSVLoader(
file_path="./data/sample_data.csv",
csv_args={
'delimiter': ';', # 使用分号作为分隔符
'quotechar': '"', # 使用双引号作为引用字符
'fieldnames': ['名称', '年龄', '城市'] # 自定义字段名
}
)
# 加载数据
documents = loader.load()
# 查看加载的文档
print(f"加载了 {len(documents)} 个文档")
print(f"第一个文档的内容: {documents[0].page_content}")
4. 加载HTML文件
4.1 HTML简介
超文本标记语言(HTML)是用于在Web浏览器中显示的文档的标准标记语言。
4.2 使用Unstructured加载HTML
Unstructured是一个强大的库,可以解析各种文档格式,包括HTML。
python
from langchain_community.document_loaders import UnstructuredHTMLLoader
# 创建一个HTML加载器
loader = UnstructuredHTMLLoader("./data/example.html")
# 加载数据
data = loader.load()
# 查看加载的文档
print(f"加载了 {len(data)} 个文档")
print(f"文档内容: {data[0].page_content[:100]}...")
4.3 使用BeautifulSoup4加载HTML
我们还可以使用BeautifulSoup4通过BSHTMLLoader加载HTML文档。这将把HTML中的文本提取到page_content中,并将页面标题提取到metadata的title中。
python
from langchain_community.document_loaders import BSHTMLLoader
# 创建一个BeautifulSoup HTML加载器
loader = BSHTMLLoader("./data/example.html")
# 加载数据
data = loader.load()
# 查看加载的文档
print(f"加载了 {len(data)} 个文档")
print(f"文档内容: {data[0].page_content[:100]}...")
print(f"文档标题: {data[0].metadata.get('title', 'No title')}")
5. 加载Markdown文件
5.1 Markdown简介
Markdown是一种轻量级标记语言,可用于使用纯文本编辑器创建格式化文本。
5.2 基本用法
LangChain实现了一个UnstructuredMarkdownLoader对象,它需要使用Unstructured包。
python
from langchain_community.document_loaders import UnstructuredMarkdownLoader
# 创建一个Markdown加载器
loader = UnstructuredMarkdownLoader("./data/example.md")
# 加载数据
data = loader.load()
# 查看加载的文档
print(f"加载了 {len(data)} 个文档")
print(f"文档内容: {data[0].page_content[:100]}...")
5.3 保留元素
在幕后,Unstructured为不同的文本块创建了不同的"元素"。默认情况下,我们将它们组合在一起,但是您可以通过指定mode="elements"
轻松保留这种分离。
python
from langchain_community.document_loaders import UnstructuredMarkdownLoader
# 创建一个保留元素的Markdown加载器
loader = UnstructuredMarkdownLoader(
"./data/example.md",
mode="elements"
)
# 加载数据
data = loader.load()
# 查看加载的文档
print(f"加载了 {len(data)} 个文档")
# 查看不同元素类型
element_types = set()
for doc in data:
element_type = doc.metadata.get("category")
element_types.add(element_type)
print(f"文档包含的元素类型: {element_types}")
6. 加载PDF文件
6.1 PDF文件简介
便携式文档格式(PDF)是由Adobe于1992年开发的一种文件格式,标准化为ISO 32000。它以一种与应用软件、硬件和操作系统无关的方式呈现文档,包括文本格式和图像。
6.2 使用PyPDF
这里我们使用pypdf将PDF加载为文档数组,其中每个文档包含页面内容和带有page编号的元数据。
python
from langchain_community.document_loaders import PyPDFLoader
# 创建一个PDF加载器
loader = PyPDFLoader("./data/example.pdf")
# 加载数据
pages = loader.load()
# 查看加载的文档
print(f"加载了 {len(pages)} 页")
print(f"第一页内容: {pages[0].page_content[:100]}...")
print(f"第一页元数据: {pages[0].metadata}")
6.3 对PDF进行向量搜索
一旦我们将PDF加载到LangChain的Document对象中,我们可以像通常一样对它们进行索引(例如,RAG应用程序)。
python
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载PDF
loader = PyPDFLoader("./data/example.pdf")
pages = loader.load()
# 分割文本
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = text_splitter.split_documents(pages)
# 创建向量存储
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(chunks, embeddings)
# 执行相似性搜索
query = "这个PDF的主要内容是什么?"
docs = vectorstore.similarity_search(query, k=3)
# 查看搜索结果
for i, doc in enumerate(docs):
print(f"结果 {i+1}:")
print(f"内容: {doc.page_content[:100]}...")
print(f"元数据: {doc.metadata}")
print("-" * 50)
6.4 从图像中提取文本
一些PDF包含文本图像,例如扫描文档或图表。使用rapidocr-onnxruntime
软件包,我们也可以将图像提取为文本:
python
from langchain_community.document_loaders import PyPDFLoader
import os
# 确保已安装rapidocr-onnxruntime
os.environ["PYPDF_USE_RAPIDOCR"] = "True"
# 创建一个PDF加载器
loader = PyPDFLoader("./data/scanned_document.pdf")
# 加载数据
pages = loader.load()
# 查看加载的文档
print(f"加载了 {len(pages)} 页")
print(f"第一页内容: {pages[0].page_content[:100]}...")
6.5 使用Unstructured
Unstructured支持一个通用接口,用于处理非结构化或半结构化文件格式,例如Markdown或PDF。LangChain的UnstructuredPDFLoader与Unstructured集成,将PDF文档解析为LangChain Document对象。
python
from langchain_community.document_loaders import UnstructuredPDFLoader
# 创建一个Unstructured PDF加载器
loader = UnstructuredPDFLoader("./data/example.pdf")
# 加载数据
data = loader.load()
# 查看加载的文档
print(f"加载了 {len(data)} 个文档")
print(f"文档内容: {data[0].page_content[:100]}...")
6.6 保留元素
在幕后,Unstructured为不同的文本块创建不同的"元素"。默认情况下,我们将它们合并在一起,但您可以通过指定mode="elements"
轻松保持分离。
python
from langchain_community.document_loaders import UnstructuredPDFLoader
# 创建一个保留元素的PDF加载器
loader = UnstructuredPDFLoader(
"./data/example.pdf",
mode="elements"
)
# 加载数据
data = loader.load()
# 查看加载的文档
print(f"加载了 {len(data)} 个元素")
# 查看不同元素类型
element_types = set()
for doc in data:
element_type = doc.metadata.get("category")
element_types.add(element_type)
print(f"文档包含的元素类型: {element_types}")
6.7 使用Unstructured加载远程PDF
这涵盖了如何将在线PDF加载到我们可以在下游使用的文档格式中。这可用于各种在线PDF站点,如open.umn.edu/opentextboo...和arxiv.org/archive/
python
from langchain_community.document_loaders import OnlinePDFLoader
# 创建一个在线PDF加载器
loader = OnlinePDFLoader("https://arxiv.org/pdf/2307.09288.pdf")
# 加载数据
data = loader.load()
# 查看加载的文档
print(f"加载了 {len(data)} 个文档")
print(f"文档内容: {data[0].page_content[:100]}...")
7. 综合应用:多源数据加载与处理
在实际应用中,我们可能需要从多个不同的数据源加载数据,并将它们组合在一起进行处理。以下是一个综合示例,展示如何加载不同类型的数据源并将它们组合起来:
python
import os
from langchain_community.document_loaders import (
CSVLoader,
BSHTMLLoader,
UnstructuredMarkdownLoader,
PyPDFLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.chains import RetrievalQA
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
def load_multiple_sources():
"""从多个数据源加载文档"""
documents = []
# 加载CSV数据
if os.path.exists("./data/sample_data.csv"):
csv_loader = CSVLoader("./data/sample_data.csv")
documents.extend(csv_loader.load())
print(f"加载了CSV文档 {len(csv_loader.load())} 个")
# 加载HTML数据
if os.path.exists("./data/example.html"):
html_loader = BSHTMLLoader("./data/example.html")
documents.extend(html_loader.load())
print(f"加载了HTML文档 {len(html_loader.load())} 个")
# 加载Markdown数据
if os.path.exists("./data/example.md"):
md_loader = UnstructuredMarkdownLoader("./data/example.md")
documents.extend(md_loader.load())
print(f"加载了Markdown文档 {len(md_loader.load())} 个")
# 加载PDF数据
if os.path.exists("./data/example.pdf"):
pdf_loader = PyPDFLoader("./data/example.pdf")
documents.extend(pdf_loader.load())
print(f"加载了PDF文档 {len(pdf_loader.load())} 页")
return documents
def create_qa_system(documents):
"""创建问答系统"""
# 分割文档
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = text_splitter.split_documents(documents)
# 创建向量存储
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(chunks, embeddings)
# 创建检索器
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# 创建问答链
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
return qa_chain
def main():
"""主函数"""
# 加载多源数据
documents = load_multiple_sources()
if not documents:
print("未找到任何文档,请确保数据文件存在。")
return
print(f"总共加载了 {len(documents)} 个文档")
# 创建问答系统
qa_chain = create_qa_system(documents)
# 进行问答
while True:
query = input("\n请输入您的问题(输入'退出'结束): ")
if query.lower() in ["退出", "exit", "quit"]:
break
result = qa_chain({"query": query})
print("\n回答:")
print(result["result"])
print("\n来源文档:")
for i, doc in enumerate(result["source_documents"]):
print(f"文档 {i+1}:")
print(f"内容: {doc.page_content[:100]}...")
print(f"元数据: {doc.metadata}")
print("-" * 50)
if __name__ == "__main__":
main()
8. 实际应用场景
8.1 知识库构建
通过从多个数据源加载数据,可以构建一个综合的知识库,用于回答问题、生成内容或进行数据分析。
python
from langchain_community.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
# 从目录中加载所有支持的文档
loader = DirectoryLoader(
"./data",
glob="**/*.*", # 加载所有文件
loader_cls=None, # 自动选择合适的加载器
show_progress=True
)
# 加载文档
documents = loader.load()
print(f"加载了 {len(documents)} 个文档")
# 分割文档
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = text_splitter.split_documents(documents)
# 创建持久化向量存储
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
vectorstore.persist()
print(f"成功创建知识库,包含 {len(chunks)} 个文本块")
8.2 数据分析与报告生成
结合多源数据和LLM,可以进行数据分析并自动生成报告。
python
import pandas as pd
from langchain_community.document_loaders import CSVLoader
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
# 加载CSV数据
loader = CSVLoader("./data/sales_data.csv")
documents = loader.load()
# 将文档转换为DataFrame
data = []
for doc in documents:
data.append(eval(doc.page_content))
df = pd.DataFrame(data)
# 进行简单的数据分析
total_sales = df["sales"].sum()
avg_sales = df["sales"].mean()
top_product = df.loc[df["sales"].idxmax()]
# 使用LLM生成分析报告
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.2)
template = """
基于以下销售数据分析,生成一份简洁的业务报告:
总销售额: {total_sales}
平均销售额: {avg_sales}
销售最好的产品: {top_product}
报告应包括:
1. 销售概况
2. 关键发现
3. 建议的下一步行动
报告:
"""
prompt = PromptTemplate(
input_variables=["total_sales", "avg_sales", "top_product"],
template=template
)
chain = LLMChain(llm=llm, prompt=prompt)
report = chain.run(
total_sales=total_sales,
avg_sales=avg_sales,
top_product=str(top_product)
)
print(report)
8.3 多模态内容处理
结合文本和图像数据,可以进行多模态内容处理。
python
from langchain_community.document_loaders import PyPDFLoader, ImageCaptionLoader
from langchain.schema import Document
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
# 加载PDF文本
pdf_loader = PyPDFLoader("./data/report.pdf")
pdf_docs = pdf_loader.load()
# 加载图像描述
image_loader = ImageCaptionLoader(
path_images=["./data/chart1.png", "./data/chart2.png"]
)
image_docs = image_loader.load()
# 合并文档
all_docs = pdf_docs + image_docs
# 提取文本内容
text_content = "\n\n".join([doc.page_content for doc in pdf_docs])
image_descriptions = "\n\n".join([doc.page_content for doc in image_docs])
# 使用LLM生成综合分析
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.2)
template = """
基于以下报告文本和图表描述,提供一个综合分析:
报告文本:
{text_content}
图表描述:
{image_descriptions}
请提供:
1. 报告的主要发现
2. 图表展示的关键趋势
3. 文本和图表数据的关联分析
分析:
"""
prompt = PromptTemplate(
input_variables=["text_content", "image_descriptions"],
template=template
)
chain = LLMChain(llm=llm, prompt=prompt)
analysis = chain.run(
text_content=text_content,
image_descriptions=image_descriptions
)
print(analysis)
9. 最佳实践与注意事项
9.1 数据加载优化
- 批量处理:对于大型文档集合,考虑批量加载和处理
- 并行处理:使用多线程或异步处理加快加载速度
- 增量加载:只加载新的或更改的文档,而不是每次都重新加载所有内容
python
import concurrent.futures
from langchain_community.document_loaders import PyPDFLoader
def load_pdf(file_path):
"""加载单个PDF文件"""
loader = PyPDFLoader(file_path)
return loader.load()
# 并行加载多个PDF
pdf_files = ["./data/doc1.pdf", "./data/doc2.pdf", "./data/doc3.pdf"]
with concurrent.futures.ThreadPoolExecutor() as executor:
results = list(executor.map(load_pdf, pdf_files))
# 合并结果
all_pages = []
for pages in results:
all_pages.extend(pages)
print(f"总共加载了 {len(all_pages)} 页")
9.2 错误处理
在处理多种数据源时,错误处理尤为重要:
python
from langchain_community.document_loaders import PyPDFLoader
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def safe_load_document(loader_func, *args, **kwargs):
"""安全地加载文档,处理可能的错误"""
try:
return loader_func(*args, **kwargs)
except Exception as e:
logger.error(f"加载文档时出错: {str(e)}")
return []
# 使用安全加载函数
def load_pdf_safely(file_path):
return safe_load_document(
lambda: PyPDFLoader(file_path).load()
)
# 测试
documents = load_pdf_safely("./data/possibly_corrupted.pdf")
print(f"成功加载了 {len(documents)} 个文档")
9.3 数据预处理
在将数据输入到LLM之前,通常需要进行一些预处理:
python
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_transformers import (
Html2TextTransformer,
BeautifulSoupTransformer
)
# 加载HTML文档
from langchain_community.document_loaders import BSHTMLLoader
loader = BSHTMLLoader("./data/example.html")
documents = loader.load()
# 使用HTML转换器
html2text = Html2TextTransformer()
cleaned_docs = html2text.transform_documents(documents)
# 或者使用BeautifulSoup转换器
bs_transformer = BeautifulSoupTransformer()
cleaned_docs_alt = bs_transformer.transform_documents(documents)
# 文本分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = text_splitter.split_documents(cleaned_docs)
print(f"预处理后的文档块数: {len(chunks)}")
10. 总结
LangChain提供了强大而灵活的工具,用于从各种数据源加载和处理数据。通过本教程,我们学习了如何加载CSV、HTML、Markdown和PDF等不同类型的数据,以及如何将它们组合起来用于下游任务。
这些技术可以应用于各种场景,如构建知识库、生成报告、进行数据分析等。通过合理利用这些工具,我们可以充分发挥LLM的潜力,创建更强大、更智能的应用程序。
随着LangChain生态系统的不断发展,我们可以期待更多的数据源集成和更强大的数据处理能力。持续关注LangChain的更新,将帮助我们保持在AI应用开发的前沿。