NVIDIA NIMs
langchain-nvidia-ai-endpoints
包含使用 NVIDIA NIM 推理微服务上的模型构建应用程序的 LangChain 集成。NIM 支持来自社区以及 NVIDIA 的聊天、嵌入和重新排序模型等各个领域的模型。这些模型经过 NVIDIA 优化,可在 NVIDIA 加速基础设施上提供最佳性能,并作为 NIM 部署,NIM 是易于使用、预构建的容器,可在 NVIDIA 加速基础设施上使用单个命令部署在任何地方。
可以在 NVIDIA API 目录 上测试 NVIDIA 托管的 NIM 部署。测试后,可以使用 NVIDIA AI Enterprise 许可证从 NVIDIA 的 API 目录导出 NIM,并在本地或云中运行,使企业能够拥有并完全控制其 IP 和 AI 应用程序。
NIM 作为容器映像按模型打包,并作为 NGC 容器映像通过 NVIDIA NGC 目录分发。从本质上讲,NIM 为在 AI 模型上运行推理提供了简单、一致且熟悉的 API。
此示例介绍了如何使用 LangChain 与支持的 NVIDIA Retrieval QA 嵌入模型 进行交互,以实现 检索增强生成,方法是使用 NVIDIAEmbeddings
类。
有关通过此 API 访问聊天模型的更多信息,请查看 ChatNVIDIA 文档。
安装
%pip install --upgrade --quiet langchain-nvidia-ai-endpoints
设置
开始使用
-
在托管 NVIDIA AI 基础模型的 NVIDIA 上创建一个免费帐户。
-
选择“检索”选项卡,然后选择您选择的模型。
-
在“输入”下选择“Python”选项卡,然后单击“获取 API 密钥”。然后单击“生成密钥”。
-
复制并保存生成的密钥作为
NVIDIA_API_KEY
。从那里,您应该可以访问端点。
import getpass
import os
# del os.environ['NVIDIA_API_KEY'] ## delete key and reset
if os.environ.get("NVIDIA_API_KEY", "").startswith("nvapi-"):
print("Valid NVIDIA_API_KEY already in environment. Delete to reset")
else:
nvapi_key = getpass.getpass("NVAPI Key (starts with nvapi-): ")
assert nvapi_key.startswith("nvapi-"), f"{nvapi_key[:5]}... is not a valid key"
os.environ["NVIDIA_API_KEY"] = nvapi_key
我们应该能够在该列表中看到一个嵌入模型,该模型可以与 LLM 结合使用以实现有效的 RAG 解决方案。我们可以通过 NVIDIAEmbeddings
类与该模型以及 NIM 支持的其他嵌入模型进行交互。
在 NVIDIA API 目录上使用 NIM
在初始化嵌入模型时,您可以通过传递它(例如,下面的 NV-Embed-QA
)来选择模型,或者通过不传递任何参数来使用默认模型。
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
embedder = NVIDIAEmbeddings(model="NV-Embed-QA")
此模型是经过微调的 E5-large 模型,它支持预期的 Embeddings
方法,包括
-
embed_query
:为查询样本生成查询嵌入。 -
embed_documents
:为要搜索的一系列文档生成段落嵌入。 -
aembed_query
/aembed_documents
:上述方法的异步版本。
使用自托管 NVIDIA NIM
准备好部署后,您可以使用 NVIDIA AI Enterprise 软件许可证中包含的 NVIDIA NIM 自托管模型,并在任何地方运行它们,让您拥有定制的权限,并完全控制您的知识产权 (IP) 和 AI 应用程序。
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
# connect to an embedding NIM running at localhost:8080
embedder = NVIDIAEmbeddings(base_url="http://localhost:8080/v1")
相似度
以下是这些数据点的相似度快速测试
查询
-
堪察加的天气怎么样?
-
意大利以哪些食物闻名?
-
我叫什么名字?我敢打赌你记不住……
-
人生的意义到底是什么?
-
人生的意义在于享受乐趣:D
文档
-
堪察加的天气寒冷,冬季漫长而严酷。
-
意大利以意面、披萨、冰淇淋和浓缩咖啡闻名。
-
我记不住个人姓名,只能提供信息。
-
人生的意义各不相同,通常被视为个人成就感。
-
享受生活中的点滴的确是一种很棒的方法。
嵌入运行时
print("\nSequential Embedding: ")
q_embeddings = [
embedder.embed_query("What's the weather like in Komchatka?"),
embedder.embed_query("What kinds of food is Italy known for?"),
embedder.embed_query("What's my name? I bet you don't remember..."),
embedder.embed_query("What's the point of life anyways?"),
embedder.embed_query("The point of life is to have fun :D"),
]
print("Shape:", (len(q_embeddings), len(q_embeddings[0])))
文档嵌入
print("\nBatch Document Embedding: ")
d_embeddings = embedder.embed_documents(
[
"Komchatka's weather is cold, with long, severe winters.",
"Italy is famous for pasta, pizza, gelato, and espresso.",
"I can't recall personal names, only provide information.",
"Life's purpose varies, often seen as personal fulfillment.",
"Enjoying life's moments is indeed a wonderful approach.",
]
)
print("Shape:", (len(q_embeddings), len(q_embeddings[0])))
现在我们已经生成了嵌入,我们可以对结果进行简单的相似度检查,以查看哪些文档会在检索任务中触发为合理的答案
%pip install --upgrade --quiet matplotlib scikit-learn
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# Compute the similarity matrix between q_embeddings and d_embeddings
cross_similarity_matrix = cosine_similarity(
np.array(q_embeddings),
np.array(d_embeddings),
)
# Plotting the cross-similarity matrix
plt.figure(figsize=(8, 6))
plt.imshow(cross_similarity_matrix, cmap="Greens", interpolation="nearest")
plt.colorbar()
plt.title("Cross-Similarity Matrix")
plt.xlabel("Query Embeddings")
plt.ylabel("Document Embeddings")
plt.grid(True)
plt.show()
提醒一下,发送到我们系统中的查询和文档是
查询
-
堪察加的天气怎么样?
-
意大利以哪些食物闻名?
-
我叫什么名字?我敢打赌你记不住……
-
人生的意义到底是什么?
-
人生的意义在于享受乐趣:D
文档
-
堪察加的天气寒冷,冬季漫长而严酷。
-
意大利以意面、披萨、冰淇淋和浓缩咖啡闻名。
-
我记不住个人姓名,只能提供信息。
-
人生的意义各不相同,通常被视为个人成就感。
-
享受生活中的点滴的确是一种很棒的方法。
截断
嵌入模型通常具有固定的上下文窗口,该窗口确定可以嵌入的最大输入标记数。此限制可以是硬限制,等于模型的最大输入标记长度,也可以是有效限制,超过此限制,嵌入的准确性会降低。
由于模型处理标记,而应用程序通常使用文本,因此应用程序确保其输入保持在模型的标记限制范围内可能具有挑战性。默认情况下,如果输入过大,则会抛出异常。
为了帮助解决此问题,NVIDIA 的 NIM(API 目录或本地)提供了一个 truncate
参数,如果输入过大,则会在服务器端截断输入。
truncate
参数有三个选项
- "NONE":默认选项。如果输入过大,则会抛出异常。
- "START":服务器从开头(左侧)截断输入,根据需要丢弃标记。
- "END":服务器从结尾(右侧)截断输入,根据需要丢弃标记。
long_text = "AI is amazing, amazing is " * 100
strict_embedder = NVIDIAEmbeddings()
try:
strict_embedder.embed_query(long_text)
except Exception as e:
print("Error:", e)
truncating_embedder = NVIDIAEmbeddings(truncate="END")
truncating_embedder.embed_query(long_text)[:5]
RAG 检索:
以下是 LangChain 表达式语言检索食谱条目 的初始示例的重新利用,但使用 AI 基础模型的 Mixtral 8x7B 指令 和 NVIDIA Retrieval QA 嵌入 模型在其游乐场环境中可用。食谱中的后续示例也按预期运行,我们鼓励您探索这些选项。
提示:我们建议使用 Mixtral 进行内部推理(即用于数据提取、工具选择等的指令遵循)和 Llama-Chat 进行单个最终“通过根据历史记录和上下文制作适合此用户的简单响应来进行总结”响应。
%pip install --upgrade --quiet langchain faiss-cpu tiktoken langchain_community
from operator import itemgetter
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_nvidia_ai_endpoints import ChatNVIDIA
vectorstore = FAISS.from_texts(
["harrison worked at kensho"],
embedding=NVIDIAEmbeddings(model="NV-Embed-QA"),
)
retriever = vectorstore.as_retriever()
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Answer solely based on the following context:\n<Documents>\n{context}\n</Documents>",
),
("user", "{question}"),
]
)
model = ChatNVIDIA(model="ai-mixtral-8x7b-instruct")
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
chain.invoke("where did harrison work?")
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Answer using information solely based on the following context:\n<Documents>\n{context}\n</Documents>"
"\nSpeak only in the following language: {language}",
),
("user", "{question}"),
]
)
chain = (
{
"context": itemgetter("question") | retriever,
"question": itemgetter("question"),
"language": itemgetter("language"),
}
| prompt
| model
| StrOutputParser()
)
chain.invoke({"question": "where did harrison work", "language": "italian"})