在这篇教程中,我们将学习如何使用 LangChain 构建一个基础的检索增强生成(RAG)系统。我们将逐步介绍从文档加载到问答的完整流程。
0.安装依赖 pip install langchain-chroma langchain-community langchain-text-splitters pypdf
1. 环境准备和依赖导入 首先,我们需要导入必要的依赖:
1 2 3 4 5 6 7 from langchain_core.documents import Documentfrom langchain_chroma import Chromafrom langchain_openai import OpenAIEmbeddings, ChatOpenAIfrom langchain_core.runnables import RunnableLambda, RunnablePassthroughfrom langchain_core.prompts import ChatPromptTemplate from langchain_community.document_loaders import PyPDFLoaderfrom langchain_text_splitters import RecursiveCharacterTextSplitter
这些导入包含了文档处理、向量存储、语言模型和提示模板等核心组件。
2. 文档加载 我们提供了两种文档加载方式:直接创建文本文档和加载PDF文件。
2.1 文本文档加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def get_text_docs (): docs = [ Document( page_content="我的名字叫张三" , metadata={"source" : "name-doc" }, ), Document( page_content="我的同学名字叫李四" , metadata={"source" : "name-doc" }, ), Document( page_content="我喜欢打乒乓球" , metadata={"source" : "hobby-doc" }, ), Document( page_content="李四喜欢打蓝球" , metadata={"source" : "hobby-doc" }, ), ] return docs
这个函数创建了包含个人信息和爱好的简单文档集合。每个文档都包含内容和元数据。
2.2 PDF文档加载 1 2 3 4 5 6 7 def get_pdf_docs (file_path = "test.pdf" ): loader = PyPDFLoader(file_path) docs = loader.load() print (len (docs)) print (f"{docs[0 ].page_content[:200 ]} \n" ) print (docs[0 ].metadata) return docs
这个函数可以加载PDF文件,并输出一些基本信息用于验证。
3. 文档处理和向量化 接下来,我们将文档分割成更小的片段,并将其转换为向量表示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 docs = get_text_docs() text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000 , chunk_overlap=200 , add_start_index=True ) all_splits = text_splitter.split_documents(docs) vectorstore = Chroma.from_documents( all_splits, embedding=OpenAIEmbeddings(model="text-embedding-3-large" ), )
这一步骤包括:
加载文档
使用递归字符分割器将文档分成小块
使用OpenAI的embedding模型将文本转换为向量
将向量存储在Chroma向量数据库中
4. 构建RAG管道 最后,我们构建完整的RAG查询管道:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 retriever = RunnableLambda(vectorstore.similarity_search).bind(k=1 ) llm = ChatOpenAI(model="gpt-4o-mini" ) message = """ Answer this question using the provided context only. {question} Context: {context} """ prompt = ChatPromptTemplate.from_messages([("human" , message)]) rag_chain = {"context" : retriever, "question" : RunnablePassthrough()} | prompt | llm
这个管道包含以下组件:
检索器:用于从向量存储中检索相关文档
语言模型:使用OpenAI的GPT模型
提示模板:定义如何构建查询
RAG链:将所有组件组合成一个完整的处理流程
在这个管道中,RunnablePassthrough()
的作用是:
直接传递输入:它会将输入值原封不动地传递给下一个组件,不做任何修改
在这里具体作用是:
当我们调用 rag_chain.invoke("我是谁?")
时
{"context": retriever}
会通过检索器获取相关文档
{"question": RunnablePassthrough()}
会将原始问题 “我是谁?” 直接传递下去
这样 prompt 模板就能同时获得检索到的上下文(context)和原始问题(question)
5. 使用示例 让我们测试这个RAG系统:
1 2 3 4 response = rag_chain.invoke("我是谁?" ) print (response.content)response = rag_chain.invoke("李四喜欢干什么?" ) print (response.content)
输出结果:
系统成功地从文档中检索到相关信息并给出了准确的回答。
6.总结 通过这个教程,我们学习了如何使用LangChain构建一个基础的RAG系统。这个系统可以:
加载和处理不同格式的文档
将文档转换为向量表示
存储和检索相关信息
使用大语言模型生成答案
这为构建更复杂的文档问答系统奠定了基础。你可以通过调整各个组件的参数,或添加更多功能来增强系统的性能。
7.完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 from langchain_core.documents import Documentfrom langchain_chroma import Chromafrom langchain_openai import OpenAIEmbeddings, ChatOpenAIfrom langchain_core.runnables import RunnableLambda, RunnablePassthroughfrom langchain_core.prompts import ChatPromptTemplate from langchain_community.document_loaders import PyPDFLoaderfrom langchain_text_splitters import RecursiveCharacterTextSplitterdef get_text_docs (): docs = [ Document( page_content="我的名字叫张三" , metadata={"source" : "name-doc" }, ), Document( page_content="我的同学名字叫李四" , metadata={"source" : "name-doc" }, ), Document( page_content="我喜欢打乒乓球" , metadata={"source" : "hobby-doc" }, ), Document( page_content="李四喜欢打蓝球" , metadata={"source" : "hobby-doc" }, ), ] return docs def get_pdf_docs (file_path = "test.pdf" ): loader = PyPDFLoader(file_path) docs = loader.load() print (len (docs)) print (f"{docs[0 ].page_content[:200 ]} \n" ) print (docs[0 ].metadata) return docs docs = get_text_docs() text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000 , chunk_overlap=200 , add_start_index=True ) all_splits = text_splitter.split_documents(docs) vectorstore = Chroma.from_documents( all_splits, embedding=OpenAIEmbeddings(model="text-embedding-3-large" ), ) retriever = RunnableLambda(vectorstore.similarity_search).bind(k=1 ) llm = ChatOpenAI(model="gpt-4o-mini" ) message = """ Answer this question using the provided context only. {question} Context: {context} """ prompt = ChatPromptTemplate.from_messages([("human" , message)]) rag_chain = {"context" : retriever, "question" : RunnablePassthrough()} | prompt | llm response = rag_chain.invoke("我是谁?" ) print (response.content)response = rag_chain.invoke("李四喜欢干什么?" ) print (response.content)