LangChain Playbook#
在 LLM 和检索增强生成 (RAG) 工作流程中,嵌入将文本转换为捕捉语义含义的向量。这使得能够根据用户的查询高效搜索上下文相关的文档。然后将这些文档作为额外的上下文提供给 LLM,从而增强其生成准确响应的能力。
本 playbook 介绍了如何将 NeMo Retriever 文本嵌入 NIM (文本嵌入 NIM) 与 LangChain 结合使用,以使用 NVIDIAEmbeddings
类进行 RAG 工作流程。首先,它展示了如何从用户查询生成嵌入。然后,它使用此方法嵌入文档,将嵌入存储在向量数据库中,最后在 LangChain 表达式语言 (LCEL) 链中使用嵌入,以帮助 LLM 回答有关 NVIDIA H200 的问题。
Notebook 要求#
访问受支持的 GPU
Docker 版本 26.1.4 或更高版本
用于 LLM 的 NVIDIA NIM 或 NVIDIA API Catalog
Python 版本 3.10.12 或更高版本
Jupyter Notebook (可选)
设置#
使用以下 bash
脚本安装此 playbook 所需的软件包和依赖项。
cat > requirements.txt << "EOF"
faiss_cpu==1.8.0
fastapi==0.115.6
langchain==0.3.13
langchain-community==0.3.12
langchain-core==0.3.27
langchain-nvidia-ai-endpoints==0.3.7
numpy==1.26.4
sentence-transformers==3.3.1
unstructured==0.16.11
EOF
pip install -r requirements.txt
使用 NVIDIA API Catalog 或用于 LLM 的 NVIDIA NIM#
接下来,初始化此 playbook 的 LLM。此 playbook 提供了两个示例。一个使用 NVIDIA API Catalog,另一个使用用于 LLM 的 NVIDIA NIM。您可以使用 langchain-nvidia-ai-endpoints
包中的 ChatNVIDIA
类访问这两种方法的聊天模型,该软件包包含 LangChain 集成,用于构建使用 NVIDIA NIM 上大型语言模型 (LLM) 的应用程序。有关更多信息,请参阅 ChatNVIDIA 文档。
选项 1:NVIDIA API Catalog#
要使用 NVIDIA API Catalog,请将 NVIDIA_API_KEY
设置为环境变量。有关生成和使用 API 密钥的信息,请参阅 NGC 身份验证。
import os
from langchain_nvidia_ai_endpoints import ChatNVIDIA
os.environ["NVIDIA_API_KEY"] = "nvapi-***"
llm = ChatNVIDIA(model="meta/llama-3.1-8b-instruct")
选项 2:用于 LLM 的 NVIDIA NIM#
要使用用于 LLM 的 NVIDIA NIM,请按照 入门指南 中的说明进行操作。在您的基础设施上部署 NIM 后,您可以使用 ChatNVIDIA
类访问它,如以下示例所示。
from langchain_nvidia_ai_endpoints import ChatNVIDIA
# connect to a LLM NIM running at localhost:8000, specifying a specific model
llm = ChatNVIDIA(base_url="http://127.0.0.1:8000/v1", model="meta/llama-3.1-8b-instruct")
在 LLM 准备就绪后,您可以将其与 LangChain 的 ChatPromptTemplate
一起使用,这是一个用于构建多轮对话和格式化语言模型输入的类。
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", (
"You are a helpful and friendly AI!"
"Your responses should be concise and no longer than two sentences."
"Say you don't know if you don't have this information."
)),
("user", "{question}")
])
chain = prompt | llm | StrOutputParser()
要在 LangChain 表达式语言 (LCEL) 链中与 LLM 交互,请使用 invoke
方法,如以下示例所示。
print(chain.invoke({"question": "What's the difference between a GPU and a CPU?"}))
此查询应生成类似于以下的输出
GPU 或图形处理单元是一种专门类型的处理器,旨在快速渲染和处理图形和图像。CPU 或中央处理单元是计算机的主要处理组件,它执行计算机内部的大部分处理。虽然 CPU 对于通用处理任务仍然很重要,但 GPU 更适合并行处理,并且通常用于涉及图形、游戏和机器学习的任务。
使用以下示例尝试另一个关于 NVIDIA A100 GPU 的问题
print(chain.invoke({"question": "What does the A in the NVIDIA A100 stand for?"}))
很高兴为您服务!NVIDIA A100 中的 “A” 代表 “Ampere”,它是此 GPU 模型的架构代号。
接下来,询问有关 NVIDIA H200 GPU 的问题。由于许多 LLM 的知识截止日期为 2022 年底或 2023 年初,因此该模型可能无法访问该时间范围之后的任何信息。
print(chain.invoke({"question": "How much memory does the NVIDIA H200 have?"}))
很抱歉,我没有该特定信息。NVIDIA H200 似乎不是 NVIDIA GPU 产品线中公认的产品。
使用文本嵌入 NIM 生成嵌入#
要回答上一个问题,请构建一个简单的检索增强生成 (RAG) 管道。
以下示例使用 LangChain 通过使用与上一个示例相同的 langchain-nvidia-ai-endpoints
包中的 NVIDIAEmbeddings
Python 类与文本嵌入 NIM 交互。在使用此示例之前,请验证 文本嵌入 NIM 是否正在运行。此示例使用 nvidia/llama-3.2-nv-embedqa-1b-v2
。如果您使用不同的文本嵌入 NIM,请更新 model
。
使用以下命令从用户查询生成嵌入
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
# Initialize and connect to a NeMo Retriever Text Embedding NIM (nvidia/llama-3.2-nv-embedqa-1b-v2) running at localhost:8001
embedding_model = NVIDIAEmbeddings(model="nvidia/llama-3.2-nv-embedqa-1b-v2",
base_url="http://127.0.0.1:8001/v1")
# Create vector embeddings of the query
embedding_model.embed_query("How much memory does the NVIDIA H200 have?")[:10]
接下来,加载 NVIDIA H200 数据手册的 PDF。此文档成为 LLM 用来检索相关信息以回答问题的知识库。
LangChain 提供了各种文档加载器,这些加载器从许多不同的来源和位置(私有 s3 存储桶、公共网站)加载各种类型的文档(HTML、PDF、代码)。此示例使用 LangChain PyPDFLoader
加载有关 NVIDIA H200 Tensor Core GPU 的数据手册。
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("https://nvdam.widen.net/content/udc6mzrk7a/original/hpc-datasheet-sc23-h200-datasheet-3002446.pdf")
document = loader.load()
document[0]
加载文档后,通常会对其进行转换。一种转换方法称为分块,它将大段文本(例如长文档中的文本)分解为较小的片段。此技术很有价值,因为它有助于优化从向量数据库返回的内容的相关性。
LangChain 提供了各种文档转换器,例如文本拆分器。在此示例中,我们使用 RecursiveCharacterTextSplitter
。RecursiveCharacterTextSplitter
根据指定的块大小将大段文本划分为较小的块。它采用递归作为其拆分文本的核心机制,利用预定义的字符集(例如 “\n\n”、“\n”、“ ” 和 “”)来确定应在何处进行拆分。该过程首先尝试使用集合中的第一个字符拆分文本。如果生成的块仍然大于所需的块大小,则它继续使用集合中的下一个字符并尝试再次拆分。此过程一直持续到所有块都符合指定的最大块大小。
文本拆分存在一些细微的复杂性,因为从理论上讲,语义相关的文本应该放在一起。
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=100,
separators=["\n\n", "\n", ".", ";", ",", " ", ""],
)
document_chunks = text_splitter.split_documents(document)
print("Number of chunks from the document:", len(document_chunks))
以下代码片段演示了如何为单个文档创建向量嵌入。此步骤对于 RAG 管道不是必需的,但此处包含用于演示目的。该示例使用嵌入模型将文本块转换为向量。它仅显示来自第一个文档块的此向量的前 10 个元素,以了解这些嵌入的外观。
# Extract text (page content) from the document chunks
page_contents = [doc.page_content for doc in document_chunks]
# Create vector embeddings from the document
embedding_model.embed_documents(page_contents)[0][:10]
将文档嵌入存储在向量数据库中#
生成文档嵌入后,它们将存储在向量数据库中。当收到用户查询时,您可以
- 嵌入查询
- 在向量数据库中执行相似性搜索,以检索最相关的文档嵌入
- 使用检索到的文档生成对用户查询的响应
向量数据库负责存储嵌入的数据并执行向量搜索。LangChain 提供对各种向量数据库的支持,在此示例中我们将使用 FAISS。
from langchain_community.vectorstores import FAISS
vector_store = FAISS.from_documents(document_chunks, embedding=embedding_model)
将文本嵌入 NIM 与 LCEL 结合使用#
下一个示例将向量数据库与 LLM 集成。 LangChain 表达式语言 (LCEL) 将这些组件组合在一起。然后,它制定提示占位符(上下文和问题),并将它们管道传输到我们的 LLM 连接器,以使用来自 NVIDIA H200 datasheet
文档的嵌入来回答第一个示例中的原始问题 (NVIDIA H200 有多少内存?
)。
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system",
"You are a helpful and friendly AI!"
"Your responses should be concise and no longer than two sentences."
"Do not hallucinate. Say you don't know if you don't have this information."
# "Answer the question using only the context"
"\n\nQuestion: {question}\n\nContext: {context}"
),
("user", "{question}")
])
chain = (
{
"context": vector_store.as_retriever(),
"question": RunnablePassthrough()
}
| prompt
| llm
| StrOutputParser()
)
print(chain.invoke("How much memory does the NVIDIA H200 have?"))
此查询应生成类似于以下的输出
NVIDIA H200 具有 141 千兆字节 (GB) 的 HBM3e 内存。