LangGraph构建Ai智能体-10-RAG示例

前言

首先展示下Embedding,本文使用阿里的模型,需要按照相关的依赖,参考 大模型服务平台百炼控制台

词嵌入

  • embed_query 处理单个文本
  • embed_documents 处理多个文本
python 复制代码
# embeddings
# from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv
import os

load_dotenv()
embeddings = DashScopeEmbeddings(
    dashscope_api_key=os.getenv("OPENAI_API_KEY"),
    model="text-embedding-v4",
)
text = 'This is a test document.'
query_result = embeddings.embed_query(text)
print(f"query_result[:2] {query_result[:2]} size={len(query_result)}")
# query_result[:2] [-0.038538604974746704, 0.03440577909350395] size=1024

text2 = '这是一个测试文本'
query_results = embeddings.embed_documents([text, text2])
print(f"len(query_results)={len(query_results)}")
# len(query_results)=2

嵌入返回的结果是一个1024的浮点数数组,这就是词嵌入,把一段话转成一个数组

json 复制代码
[-0.038538604974746704, 0.03440577909350395,...] 

索引存储与检索示例

嵌入把问题和文档都转换成向量(就是一串数字),然后把这些向量存储在一个向量数据库里(比如 LangChain 的 InMemoryVectorStore)。这样,我们就可以通过计算向量之间的相似度,快速准确地找到最相关的文档。

python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv
import os
from langchain_core.vectorstores import InMemoryVectorStore

load_dotenv()
embeddings = DashScopeEmbeddings(
    dashscope_api_key=os.getenv("OPENAI_API_KEY"),
    model="text-embedding-v4",
)

text = 'LangChain is the framework for building context-aware reasoning applications.'

# 1. 向量化并存储
vectorstore = InMemoryVectorStore.from_texts([text], embedding=embeddings, )

# 2. 检索器
retriever = vectorstore.as_retriever()

# 3. 检索文档
query = 'What is LangChain?'
retrieved_documents = retriever.invoke(query)
print(retrieved_documents[0].page_content)

嵌入把文本转换成高维向量,相似的文本在向量空间中会靠得很近。这样,我们可以通过测量向量之间的"距离"或"角度"来快速比较它们的意思。一般有余弦相似度欧几里得距离的测量方法

一个计算余弦相似度的示例,使用numpy库

python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv
import os
from langchain_core.vectorstores import InMemoryVectorStore
import numpy as np

load_dotenv()
embeddings = DashScopeEmbeddings(
    dashscope_api_key=os.getenv("OPENAI_API_KEY"),
    model="text-embedding-v4",
)

text1 = 'LangGraph is a library for building stateful, multi-actor applications with LLMs.'
text2 = 'LangChain is a framework for building context-aware reasoning applications.'
text3 = 'The quick brown fox jumps over the lazy dog.'

embedding1 = embeddings.embed_query(text1)
embedding2, embedding3 = embeddings.embed_documents([text2, text3])

print('Embedding for text1 (first 10 values):', embedding1[:10])
print('Embedding for text2 (first 10 values):', embedding2[:10])
print('Embedding for text3 (first 10 values):', embedding3[:10])


# Define a function to calculate cosine similarity

def cosine_similarity(vec1, vec2):
    "计算余弦相似度"
    dot_product = np.dot(vec1, vec2)
    norm_vec1 = np.linalg.norm(vec1)
    norm_vec2 = np.linalg.norm(vec2)
    return dot_product / (norm_vec1 * norm_vec2)


# Calculate similarity scores
similarity_1_2 = cosine_similarity(embedding1, embedding2)
similarity_1_3 = cosine_similarity(embedding1, embedding3)
similarity_2_3 = cosine_similarity(embedding2, embedding3)

# Display similarity scores
print('Cosine Similarity between text1 and text2:', similarity_1_2)  # 0.6875611669530656
print('Cosine Similarity between text1 and text3:', similarity_1_3)  # 0.18784711323151998
print('Cosine Similarity between text2 and text3:', similarity_2_3)  # 0.20749644904732326

由于similarity_1_2的值最大,就说明text1和text2的相似度更大

向量存储

向量数据库是一种专门用来存储和检索高维向量的数据库。在检索增强生成(RAG)系统中,向量数据库非常重要,因为它可以帮助我们快速找到和查询向量最相似的文档,从而获取上下文相关的信息。

常见的向量数据库

  • FAISS:Facebook AI 开发的高性能、开源库,适合本地部署。
  • Chroma:轻量级,作为本地库运行,与 Python 集成良好。
  • Lance:开源向量数据库,优化了存储和检索效率。
  • Milvus:开源,可以本地部署或在云端部署,支持动态索引。
  • Weaviate:可以在本地或云端运行,支持强大的查询语言和知识图谱。
  • Vespa:开源引擎,适合搜索和推荐,适合本地部署。
  • Pinecone:基于云的向量数据库,提供可扩展且托管的向量存储。
  • Google Vertex Matching Engine:Google 提供的托管服务,用于快速且可扩展的相似性搜索。
  • AWS Kendra:与 AWS 集成的完全托管搜索服务。
  • Redis Vector Search:Redis 模块,支持向量相似性搜索,优化了云环境。

Chroma使用示例

需要安装pip install langchain_chroma

pyhon 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv
import os
from langchain_chroma import Chroma
from langchain.schema import Document  # Import the Document class

# Chroma demo

load_dotenv()
embeddings = DashScopeEmbeddings(
    dashscope_api_key=os.getenv("OPENAI_API_KEY"),
    model="text-embedding-v4",
)

documents = [
    Document(page_content="LangChain is a framework for building context-aware reasoning applications."),
    Document(page_content="FAISS is a library for efficient similarity search and clustering of dense vectors."),
    Document(page_content="The quick brown fox jumps over the lazy dog.")
]

# 索引
db = Chroma.from_documents(documents, embeddings)
print("索引ok")

# 按照文本查询
query = "What is LangChain?"
docs = db.similarity_search(query)
print("Most similar document to the query:")
print(docs[0].page_content)  # LangChain is a framework for building context-aware reasoning applications.

# 按照向量查询
query_embedding = embeddings.embed_query(query)
docs_by_vector = db.similarity_search_by_vector(query_embedding)
print("Most similar document to the query (vector search):")
print(docs_by_vector[0].page_content)  # LangChain is a framework for building context-aware reasoning applications.

使用LLM使用RAG生成问题答案

用RAG和LLM来回答问题,就是先在向量存储里通过相似性搜索找到最相关的文件,然后把找到的文件内容当背景,让语言学习模型(LLM)来生成回答。这种办法在检索增强生成(RAG)场景里特别关键,LLM能把从向量存储里检索到的外部知识和自己的生成能力结合起来,搞定答案。

示意图

咱们来瞅瞅怎么用LLM,根据检索到的文件内容来生成回答。

用LLM生成回答的步骤

  • 检索相关文件:通过相似性搜索,找到跟用户问题最相关的文件。
  • 组合上下文和问题:把检索到的文件和用户的问题拼成一个提示词给LLM用。
  • 生成回答:让LLM根据拼好的提示词来生成回答。

这个例子的前提是我们已经在向量存储(比如Chroma)里存好了文件,并且根据用户的查询找到了最相似的文件。

一个简单的RAG示例

python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv
import os
from langchain_chroma import Chroma
from langchain.schema import Document  # Import the Document class
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# Chroma demo

load_dotenv()
embeddings = DashScopeEmbeddings(
    dashscope_api_key=os.getenv("OPENAI_API_KEY"),
    model="text-embedding-v4",
)
model = ChatOpenAI(model="qwen-plus",
                   base_url=os.getenv("BASE_URL"),
                   api_key=os.getenv("OPENAI_API_KEY"),
                   temperature=0,
                   streaming=True)

documents = [
    Document(page_content="LangChain is a framework for building context-aware reasoning applications."),
    Document(page_content="FAISS is a library for efficient similarity search and clustering of dense vectors."),
    Document(page_content="The quick brown fox jumps over the lazy dog.")
]

# 索引
db = Chroma.from_documents(documents, embeddings)
print("索引ok")

# 设置检索器 按照相似度最多检索5个
retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 5})

# RAG提示词
prompt = ChatPromptTemplate.from_template(
    """
    您是问答任务的助理。使用以下检索到的上下文来回答问题。如果你不知道答案,就说你不知道。最多使用三句话,保持答案简洁。
    Question: {question} 
    Context: {context} 
    Answer:
    """
)
# RAG链
rag_chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | prompt
        | model
        | StrOutputParser()
)

# Use the RAG pipeline to answer a user question
question = "狐狸做了什么?"
for chunk in rag_chain.stream(question):
    print(chunk, end="", flush=True)

# 狐狸跳过了懒狗。

从PDF中获取资料的RAG示例

加载一个PDF文档,将其拆分为更小的块,嵌入每个块,并将其存储在向量存储中。

具体步骤是:

  • 加载文件:用PyPDFLoader加载PDF文件。
  • 分割文件:用CharacterTextSplitter把文件切成小块。
  • 生成嵌入向量:给每个小块生成嵌入向量。
  • 存到Chroma:把小块索引到Chroma向量数据库里。
  • 计算token数量:用tiktoken算每个小块的token数量。
python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv
import os
from langchain_chroma import Chroma
from langchain.schema import Document  # Import the Document class
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_text_splitters import CharacterTextSplitter
import tiktoken
# pip install pypdf
from langchain_community.document_loaders import PyPDFLoader

load_dotenv()
embeddings = DashScopeEmbeddings(
    dashscope_api_key=os.getenv("OPENAI_API_KEY"),
    model="text-embedding-v4",
)
model = ChatOpenAI(model="qwen-plus",
                   base_url=os.getenv("BASE_URL"),
                   api_key=os.getenv("OPENAI_API_KEY"),
                   temperature=0,
                   streaming=True)
# 加载文档
file_path = os.path.join(os.getcwd(), "Faiss by FacebookAI.pdf")
raw_documents = PyPDFLoader(file_path).load()
print("size=", len(raw_documents))  # 12

# 拆分文档
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
print("documents size=", len(documents))  # documents size= 12

# Chroma
# 此处拆成10个一组是因为阿里云的词嵌入一次最多支持10个文档,所以我们分两批加载
db = Chroma.from_documents(documents=documents[:10], embedding=embeddings)
db.add_documents(documents[10:])

retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 3})  # Retrieve top 3 relevant chunks

# Rag提示词模板
prompt = ChatPromptTemplate.from_template(
    """
    您是问答任务的助理。使用以下检索到的上下文来回答问题。如果你不知道答案,就说你不知道。最多使用三句话,保持答案简洁。
    Question: {question} 
    Context: {context} 
    Answer:
    """
)

# RAG链
rag_chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | prompt
        | model
        | StrOutputParser()
)
# 测试
question = "你能解释一下FAISS的用途吗?"
for chunk in rag_chain.stream(question):
    print(chunk, end="", flush=True)

# FAISS(Facebook AI Similarity Search)是一个用于高效相似性搜索的库,主要用于快速查找大规模数据集中相似的多媒体文档。它通过高效的最近邻搜索实现,支持数十亿高维向量的快速处理,比之前的最先进方法快8.5倍。FAISS适用于需要处理高维向量的AI应用,如图像和文本检索。

从Web中获取资料的示例

langchain中提供了很多的Loader,比如WebLoader就可以从url中获取文件内容,下面是一个示例,展示了从pdf和url多个源中获取文档

python 复制代码
from typing import List

from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv
import os
from langchain_chroma import Chroma
from langchain.schema import Document  # Import the Document class
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_text_splitters import CharacterTextSplitter
# pip install pypdf
from langchain_community.document_loaders import PyPDFLoader
# pip install beautifulsoup4
from langchain_community.document_loaders import WebBaseLoader

load_dotenv()
embeddings = DashScopeEmbeddings(
    dashscope_api_key=os.getenv("OPENAI_API_KEY"),
    model="text-embedding-v4",
)
model = ChatOpenAI(model="qwen-plus",
                   base_url=os.getenv("BASE_URL"),
                   api_key=os.getenv("OPENAI_API_KEY"),
                   temperature=0,
                   streaming=True)


# 加载文档
def load_from_pdf(file_path: str) -> List[Document]:
    raw_documents = PyPDFLoader(file_path).load()
    return raw_documents


def load_from_url(url: str) -> List[Document]:
    return WebBaseLoader(url).load()


def split_docs(docs: List[Document]) -> List[Document]:
    text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
    return text_splitter.split_documents(docs)


def create_db(docs: List[Document]):
    if len(docs) <= 10:
        res = Chroma.from_documents(documents=docs, embedding=embeddings)
    else:
        first_docs = docs[:10]
        res = Chroma.from_documents(documents=first_docs, embedding=embeddings)
        left = 10
        while left < len(docs):
            right = min(left + 10, len(docs))
            seg_docs = docs[left:right]
            res.add_documents(seg_docs)
            left += 10
    return res


docs1 = load_from_pdf(os.path.join(os.getcwd(), "Faiss by FacebookAI.pdf"))
docs2 = load_from_url("https://faiss.ai/")
db = create_db(split_docs([*docs1, *docs2]))
retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 3})  # Retrieve top 3 relevant chunks

# 测试检索内容
docs_retrieved = retriever.invoke(input="你能解释一下FAISS的用途吗?")
for doc in docs_retrieved:
    print(doc.id, doc.metadata["source"], f"{doc.page_content[:20]}...")

# Rag提示词模板
prompt = ChatPromptTemplate.from_template(
    """
    您是问答任务的助理。使用以下检索到的上下文来回答问题。如果你不知道答案,就说你不知道。最多使用三句话,保持答案简洁。
    Question: {question} 
    Context: {context} 
    Answer:
    """
)

# RAG链
rag_chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | prompt
        | model
        | StrOutputParser()
)

# 测试
question = "你能解释一下FAISS的用途吗?"
for chunk in rag_chain.stream(question):
    print(chunk, end="", flush=True)

运行结果

csharp 复制代码
# 检索出的文档内容
1ce0d5a7-1690-40d6-8411-73335de65249 https://faiss.ai/ Welcome to
8c966424-78c1-4171-a249-6df55841d0b5 D:\code\support\LangGraphProjects\proj1\Faiss by FacebookAI.pdf AUG 23, 20
ce45fa21-5a3a-4106-a050-2d1c2bf994ff https://faiss.ai/ The faiss-

# llm的回答
FAISS 是一个用于高效相似性搜索和密集向量聚类的库,能够处理从内存到大规模数据集的各种向量集合。它支持相似性搜索操作,例如在给定向量时快速查找最接近的向量。FAISS 还提供 GPU 实现以加速计算,并主要用于 AI 研究领域。
相关推荐
mit6.82411 分钟前
[AI React Web]`意图识别`引擎 | `上下文选择算法` | `url内容抓取` | 截图捕获
前端·人工智能·react.js
正经教主12 分钟前
GitHub Browser-Use 的部署失败记录:失败了,失败了。。。。
人工智能·自动化·github
战场小包16 分钟前
AI潮汐日报0813期:Github CEO 辞职、ChatGPT 爆漏洞、下架GPT-4o引起失恋式抗议
人工智能·chatgpt·github·aigc·业界资讯·agi
在钱塘江41 分钟前
LangGraph构建Ai智能体-11-高级RAG之Self-RAG
人工智能·python
爱吃猪排1 小时前
基于 Paddle Inference 3.0 的高性能 OCR 服务实现
人工智能·命令行
小阿鑫1 小时前
MCP神器!MCP-USE 一键部署连接任何MCP服务器
服务器·人工智能·aigc·部署·ai落地·mcp·mcpserver·部署mcpserver·部署mcp
xw33734095641 小时前
《卷积神经网络(CNN):解锁视觉与多模态任务的深度学习核心》
人工智能·pytorch·深度学习·神经网络·cnn
极客BIM工作室1 小时前
机器学习阶段性总结:对深度学习本质的回顾 20250813
人工智能·深度学习·机器学习
程序员海军1 小时前
MCP神器!MCP-USE 一键部署连接任何MCP服务器
人工智能·aigc·mcp
NeoFii1 小时前
Day 39: 图像数据与显存
python·深度学习·机器学习