大模型应用开发 langchain和 langgraph 之 RAG 入门

3/8/2025

在我们看完 Langchain 基础之后,RAG 就是第二个难点。本文探索 langchain 中的 RAG 以及部分 langgraph RAG。

image.png

一、什么是 RAG ?

Retrieval Augmented Generation 检索增强生成。

简单的说就是大模型,虽然比较全面,但是在一些细节上还是有很多的不同,这个时候我们就可以通过上下文的方式,给大模型一些额外的数据增强大模型的知识库,然后生成内容。RAG 从数据源到 LLM 输出,大致分为三个流程:

  • 索引
  • 检索
  • 生成

先看一张经典的图片:

image.png

二、索引

索引页分为三个部分:

  • 读取数据源 load
  • 分割文档 split
  • 向量存储 embed + vector

2.1) Load

因为数据源是多种多样的,langchain 中使用 Loader 对各种数据源进行支持。可以在 document_loaders 找到对象。一些常用的:

  • txt
  • json
  • csv
  • pdf
  • docx
  • markdown
  • web content
  • ....
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.document_loaders import TextLoader
from langchain_community.document_loaders import CSVLoader
from langchain_community.document_loaders import Docx2txtLoader
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_community.document_loaders import UnstructuredHTMLLoader
from langchain_community.document_loaders import JSONLoader
from langchain_community.document_loaders import WebBaseLoader

这里以 pdf 为例从 loader 到 docs 的过程:

poetry add pypdf2 # 需要pdf 相关库的支持
from langchain_community.document_loaders import PyPDFLoader

pdf_loader = PyPDFLoader(full_path)

docs = loader.load()

读取到的数据结构是 Document 对象

from langchain_core.documents import Document

以下是 Document 的数据结构:

image.png

image.png

Document 对象继承自 BaseMedia, 包含了 idmetadata 属性:

image.png

2.2) Split

首先我们要明白为什么要 Split ?

其实也很简单,大模型处理的内容是有限的,大量数据文件,分割处理更加有利于大模型对数据内容理解。

当然 langchain 也提供了单独的一个库 langchain_text_splitters 处理 Split:

poetry add langchain_text_splitters
pip install langchain_text_splitters

image.png

# 字符文本分割
from langchain_text_splitters import CharacterTextSplitter
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_text_splitters import TokenTextSplitter
from langchain_text_splitters import SpacyTextSplitter
from langchain_text_splitters import SentenceTransformersTokenTextSplitter
from langchain_text_splitters import NLTKTextSplitter
from langchain_text_splitters import KonlpyTextSplitter

以上包含七种同的 TextSplitter,这里以 RecursiveCharacterTextSplitter 使用

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)

# tiktoken_encoder
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    encoding_name="cl100k_base", chunk_size=100, chunk_overlap=0
)

# document
splits = text_splitter.split_documents(docs)

# text
texts = text_splitter.split_text(document)
  • chunk_size 指定字符串和
  • chunk_overlap 重叠文本的长度

2.3)Embed + Store

Store 这里也可以分为两个部分:

  • 嵌入式模型
  • 向量存储

这个两个部分 langchain 也给我们封装好了,我们需要熟悉嵌入式模型的用法。

embeddings 模型

嵌入式模型种类也非常多,不同的模型能力也有不同。 目前 langchain 支持的 embeddings

image.png

  • openai
  • ollama
  • HuggingFace
  • Cohere
  • zhipu
  • ...

有本地部署的,有在线付费的,有开源免费的,总之根据自己的需求探索使用。这里使用 OpenAI 的 embeddings 和 ZhipuAI embeddings 为例进行讲解。

  • OpenAI:

OpenAI 目前有三个种模型如果你是自己测试 embeddings 模型:

image.png

from langchain_openai import OpenAIEmbeddings
embeddings_model = OpenAIEmbeddings()
  • 国内的我们以 zhipu AI 的 embeddings 为例:

image.png

简单的封装一下:

from langchain_community.embeddings import ZhipuAIEmbeddings

import dotenv

class ZhipuEmbeddings():
    api_key = dotenv.get_key(".env", "api_key")

    @classmethod
    def create_zhipu_embeddings(self, model_name):
        embeddings = ZhipuAIEmbeddings(
            model=model_name,
            api_key=self.api_key
        )
        return embeddings

当然你也可以在 text_embedding 查看 langchain 支持 embeddings 模型。

VectorStore

image.png

langchain 支持了众多的 vector store 这里我们简单的进行分类,这些是 langchain 中给到和一些常用的向量存储数据库。

  • 内存:InMemoryVectorStore
  • 本地存储:Chroma/FAISS
  • 其他类型:
    • Pinecone
    • Milvus
    • Qdrant
  • 传统数据库对
    • PostgreSQL + pgvector
    • Supabase
    • Redis
    • ...
# InMemoryVectorStore
from langchain_core.vectorstores import InMemoryVectorStore

vector_store = InMemoryVectorStore(embedding=SomeEmbeddingModel())

# Chroma
from langchain_chroma import Chroma

vectorstore = Chroma.from_documents(  
    documents,  
    embedding=OpenAIEmbeddings(),  
)

# FAISS
from langchain_community.vectorstores import FAISS
vectorstore = FAISS.from_documents(texts, embeddings)

# Milvus
from langchain_milvus import Milvus
URI = "./milvus_example.db"

vector_store = Milvus(
    embedding_function=embeddings,
    connection_args={"uri": URI},
)

三、检索和生成

先看一个经典的图

image.png

一个问题来,先经过向量的检索,然后生成新的提示词,然后给到大模型,最后得到答案。

四、VectorStore 的核心方法

  • add_texts(texts, metadatas):将文本及其元数据嵌入并存储到 VectorStore。
  • similarity_search(query, k):基于查询向量,返回最相似的 k 个结果。
  • similarity_search_with_score:使用距离运行相似性搜索。
  • delete(ids):根据指定的 ID 删除向量。
  • from_documents(documents):从文档列表构建向量存储。
  • save_local(path) 和 load_local(path):将向量存储保存到本地或从本地加载。

五、retriever 检索和生成

5.1)检索

将 VectorStore 转换为检索器: as_retriever

query = "my query"
# 搜索文档
docs = vectorstore.similarity_search(query)
# 转成检索器
retriever = vector_store.as_retriever()

# 检索
retrieved_docs = retriever.invoke("What are the approaches to Task Decomposition?")

5.2) 生成

准备好了这一切,从读取数据库,到分割成 Document, 到 embeddings model + 向量存储。下面就是使用 Langchian 的 chain 组合上传文进行生成内容了。

  • | chain 模型

在生成 retriever 之后,retriever 做来 context 的值传递给 chain 后面的内容,当然也可以使用 create_retrieval_chain api 来创建一个 rag_chain。

# retrieve 作为 chain 中的 context

# chain
chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
)

  • create_retrieval_chain
# create_retrieval_chain
from langchain.chains import create_retrieval_chain

rag_chain = create_retrieval_chain(retriever, question_answer_chain)

六、langgraph 中的 RAG

LangGraph是一个用于使用LLMs构建有状态、多参与者应用程序的库,用于创建代理和多代理工作流程。

langgraph 的 RAG 是 langgraph 架构 与 langchain 有所不同,这里我们简单的实现一个 Langgraph 的 RAG:

  • chatbot + pdf 的 RAG
poetry init
poetry add langchain langgraph python-dotenv langchain-openai langchain-text-splitters langchain-community pypdf zhipui

在国内我们的大模型选择zhipu的大模型。首先通过 质谱开放平台获取 zhipu 的 API KEY 和 url 地址。

image.png

下面我们基于 Jupyter Notebook + VSCode 插件做一个来完成一个简单的 PDF RAG 应用。

6.1)设置 env 环境文件

image.png

这里的 OPEN_AI_KEY 就是 zhipu ai 的key

6.2)读取 env 文件环变量

image.png

6.3)定义 zhipu ai 变量

image.png

6.4)定义 zhipu ai 的模型

image.png

6.5)定义质谱ai的 embeddings 模型

image.png

6.6)定义 langchain 读取 pdf 文件

image.png

6.7)对读取的 pdf 文件进行分割

image.png

6.8)使用内存型向量数据存储

image.png

6.9)使用 langchain hub 中的提示器

image.png

6.10)定义 langgraph 的 State

image.png

6.11)定义 langgraph 的两个节点

  • retrieve:根据问题进行向量相似搜素,返回检索的上下文
  • generate: 提供问题和上下文,调用大模型生成,然后返回

image.png

6.12)组合 langgraph 节点和、并编译

image.png

6.13)传递问题并回答

image.png

我们问题是: 用中文回答,本书主要讲的内容,100字以内, 我们看到回答也很简单,在 100 字之内,符合预期。

6.14) 绘制一个 langgraph 的图

image.png

七、小结

本文主要梳理 Langchain 的 RAG 的使用和流程。Langchain 中关于 RAG 基础工具已经帮助我们解决了,我们可以借助这些工具方便快捷的写一个简单的 RAG 应用。langchain 也提供了另外一种架构 langgraph 完成 RAG, RAG 的流程与 langchain 不一样的地方在于 RAG 检索在 langgraph 的节点上完成。