Skip to main content
Open In ColabOpen on GitHub

NVIDIA NIMs

langchain-nvidia-ai-endpoints 包包含 LangChain 集成,用于构建使用 NVIDIA NIM 推理微服务上模型的应用程序。NIM 支持来自社区以及 NVIDIA 的聊天、嵌入和重新排序模型等领域模型。这些模型由 NVIDIA 优化,可在 NVIDIA 加速基础设施上实现最佳性能,并作为 NIM 部署。NIM 是易于使用的预构建容器,通过 NVIDIA 加速基础设施上的单一命令即可在任何地方进行部署。

NVIDIA 托管的 NIM 部署可在 NVIDIA API catalog 上进行测试。测试后,可以使用 NVIDIA AI Enterprise 许可证从 NVIDIA 的 API 目录导出 NIM,并在本地或云中运行,从而使企业能够拥有并完全控制其 IP 和 AI 应用程序。

NIM 按模型打包为容器镜像,并通过 NVIDIA NGC Catalog 作为 NGC 容器镜像分发。本质上,NIM 提供简单、一致且熟悉的 API 来运行 AI 模型推理。

此示例介绍了如何使用 LangChain 通过 NVIDIAEmbeddings 类与支持的 NVIDIA Retrieval QA Embedding Model 进行交互,以实现检索增强生成

有关通过此 API 访问聊天模型的更多信息,请参阅 ChatNVIDIA 文档。

安装

%pip install --upgrade --quiet  langchain-nvidia-ai-endpoints

设置

开始之前:

  1. NVIDIA 创建一个免费账户,NVIDIA 在此托管 NVIDIA AI Foundation 模型。

  2. 选择 Retrieval 选项卡,然后选择您选择的模型。

  3. Input 下选择 Python 选项卡,然后点击 Get API Key。然后点击 Generate Key

  4. 复制并保存生成的密钥作为 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 Catalog 中使用 NIM

在初始化嵌入模型时,你可以通过传递模型名称来选择模型,例如下面示例中的 NV-Embed-QA,或者不传递任何参数来使用默认模型。

from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings

embedder = NVIDIAEmbeddings(model="NV-Embed-QA")
API Reference:NVIDIAEmbeddings

该模型是一个微调后的 E5-large 模型,支持预期的 Embeddings 方法,包括:

  • embed_query:为查询样本生成查询 embedding。

  • embed_documents:为要搜索的文档列表生成 passage embedding。

  • aembed_query/aembed_documents:上述方法的异步版本。

使用自托管 NVIDIA NIM

准备部署时,您可以使用 NVIDIA NIM 自托管模型——NVIDIA AI Enterprise 软件许可已包含此项,并可在任何地方运行它们,让您拥有自定义内容的版权,并完全控制您的知识产权 (IP) 和 AI 应用。

了解更多关于 NIM 的信息

from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings

# connect to an embedding NIM running at localhost:8080
embedder = NVIDIAEmbeddings(base_url="http://localhost:8080/v1")
API Reference:NVIDIAEmbeddings

相似度

以下是对这些数据点相似度的快速测试:

查询:

  • 堪察加的天气怎么样?

  • 意大利以哪些食物闻名?

  • 我叫什么名字?我敢打赌你不记得了……

  • 生命的意义到底是什么?

  • 生命的意义在于玩乐 :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 Catalog 或本地)提供了一个 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 Foundation Models 的 Mixtral 8x7B InstructNVIDIA Retrieval QA Embedding 模型,这些模型在其 Playground 环境中可用。食谱后续的示例也能如期运行,我们鼓励您使用这些选项进行探索。

提示: 我们建议将 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"})