亚马逊AWS官方博客

使用 LlamaIndex 和 Llama 2-Chat 构建基于知识库的会话式应用程序

从海量文本中解锁准确以及富有洞察力的内容是大语言模型所赋予的一种令人兴奋的能力。在构建大语言模型的相关应用时,通常需要连接和查询外部数据源来为模型提供相关的上下文。一种流行的方法是使用检索增强生成(RAG)来创建能够理解复杂信息并对查询提供自然响应的问答系统。RAG 允许模型利用庞大的知识库,为诸如聊天机器人和企业搜索助手等应用提供仿人类的对话。

在这篇文章中,我们探讨了如何借助 LlamaIndexLlama 2-70B-ChatLangChain 的力量来构建强大的问答应用。通过这些最先进的技术,您可以摄取文本语料库、为关键的知识创建索引,以及生成能够准确、清晰地回答用户问题的文本。

Llama 2-70B-Chat

Llama 2-70B-Chat 是一个强大的大语言模型,可与众多领先的模型相媲美。它是在两万亿个文本标记上进行了预训练,并且 Meta 公司主要目的是将其用于为用户提供聊天辅助。预训练数据来源于公开可用的数据,截止于 2022 年 9 月;而微调数据则截止于 2023 年 7 月。有关模型的训练过程、安全考虑、经验教训和预期用途的更多详细信息,请参阅论文 Llama 2: Open Foundation and Fine-Tuned Chat Models。Llama 2 模型可在 Amazon SageMaker JumpStart 上获得,以实现快速和直观的部署。

LlamaIndex

LlamaIndex 是一个数据框架,可用于构建大语言模型应用程序。它提供了数据连接工具,可以从各种来源和格式(PDF、docs、API、SQL 等)的文件摄取您的数据。无论您的数据存储在数据库中还是 PDF 文件中,LlamaIndex 都可以轻松地将这些数据用于大语言模型。正如本文所演示的例子,LlamaIndex API 使数据访问变得轻而易举,同时使您能够创建强大的自定义大语言模型应用程序和工作流程。

如果您正在尝试和构建大语言模型相关的应用,那么您可能熟悉 LangChain。它提供了一个健壮的框架,简化了大语言模型驱动的应用程序的开发和部署。与 LangChain 类似,LlamaIndex 也提供了多种工具,包括数据连接器、数据索引、引擎和数据代理,以及应用程序集成。LlamaIndex 专注于消除数据与大语言模型之间的隔阂,通过对用户友好的功能简化数据任务。LlamaIndex 是专门为构建搜索和检索应用程序而设计以及优化的,例如基于 RAG 的应用程序,因为它提供了一个简单的接口来查询大语言模型并检索相关文档。

解决方案概述

在本文中,我们演示了如何使用 LlamaIndex 和大语言模型创建基于 RAG 的应用程序。下图为该解决方案的分步架构,每一步将在接下来的章节中叙述。

RAG 结合了信息检索和自然语言生成,以产生更有洞察力的回答。当接收到提示时,RAG 首先搜索文本语料库,检索与输入最相关的示例。在生成响应期间,模型会考虑这些示例来增强其功能。通过结合相关的检索段落,RAG 响应往往比基础生成模型更加实事求是、连贯一致且与上下文相关。这种检索 – 生成框架利用了检索和生成的优势,有助于解决单纯的自回归对话模型可能出现的重复和缺乏上下文等问题。RAG 为构建具有上下文、高质量响应的对话代理和 AI 助手引入了一种有效的方法。

实施该解决方案包括以下步骤:

  1. 设置 Amazon SageMaker Studio 作为开发环境,并安装所需的依赖项。
  2. 从 Amazon SageMaker JumpStart 中心部署 embedding 模型。
  3. 下载新闻稿作为我们的外部知识库。
  4. 基于新闻稿构建索引,以便查询并将其作为上下文添加到提示中。
  5. 查询知识库。
  6. 使用 LlamaIndex 和 LangChain 代理构建问答应用程序。

本文中的所有代码都可在 GitHub 仓库中找到。

先决条件

对于实施本示例,您需要一个 AWS 账号,同时该账号应包含一个 SageMaker domain 和对应的 IAM 权限。有关 AWS 账号创建的说明,请参阅创建 AWS 账户。如果您还没有 SageMaker domain,请参阅 Amazon SageMaker domain 进行创建。在本文中,出于实验目的,我们将使用 AmazonSageMakerFullAccess 角色,但是不建议您在生产环境中使用该角色。相反,您应该创建和使用具有最小权限的角色。您还可以探索如何使用 Amazon SageMaker Role Manager 直接通过 SageMaker 控制台为常见的机器学习需求构建和管理 IAM 角色。

此外,您的账号需要具备以下实例类型的配额:

  • ml.g5.2xlarge 用于部署 Hugging Face GPT-J 嵌入模型
  • ml.g5.48xlarge 用于部署 Llama 2-Chat 模型

若要增加配额,请参阅该文档

利用 SageMaker JumpStart 部署 GPT-J 嵌入模型

本节为您提供了两种部署 SageMaker JumpStart 模型的选项。您可以使用我们提供的代码进行基于代码的部署,或者使用 SageMaker JumpStart 用户界面 (UI)进行部署。

使用 SageMaker Python SDK 部署

您可以使用 SageMaker Python SDK 来部署大语言模型,如该代码所示。
步骤:

  1. 设置将用于部署嵌入模型的实例大小,使用 instance_type = "ml.g5.2xlarge"
  2. 找到需要的嵌入模型的 ID。在 SageMaker JumpStart 中,它被标识为 model_id = "huggingface-textembedding-gpt-j-6b-fp16"
  3. 获取预训练模型的容器并将其部署用于推理

当嵌入模型部署成功后,SageMaker 将返回模型端点的名称。

使用 SageMaker Studio 部署

在 SageMaker Studio 中使用 SageMaker JumpStart 部署模型,可根据以下步骤:

  1. 在 SageMaker Studio 控制台中,从导航栏选择 JumpStart。如下图所示。
  2. 搜索并选择 GPT-J 6B Embedding FP16 模型。
  3. 选择部署并自定义配置。如下图所示。
  4. 对于本示例,我们直接选择默认的实例大小 ml.g5.2xlarge 即可。
  5. 点击部署(此次部署需要大概 5 – 10 分钟)。

在部署了嵌入模型之后,为了使用 LangChain 与 SageMaker API 的集成,您需要创建一个函数来处理原始文本的输入并使用该模型将它们转换为嵌入。您可以通过创建一个名为 ContentHandler 的类来实现这一点,它接收 JSON 类型的输入数据,并返回 JSON 格式的文本嵌入: class ContentHandler(EmbeddingsContentHandler)

将模型端点的名称传给函数 ContentHandler 用以转换文本和返回嵌入:

embeddings = SagemakerEndpointEmbeddings(endpoint_name='huggingface-textembedding-gpt-j-6b-fp16', region_name= aws_region, content_handler=emb_content_handler).

您可以在代码执行的结果中或 SageMaker JumpStart 的部署界面中找到该端点名称。

您可以通过输入一些原始文本并运行 embeddings.embed_query(text) 函数来测试 ContentHandler 函数和端点是否按照预期工作。您可以使用提供的示例 text = "Hi! It's time for the beach"或尝试您自己的文本。

使用 SageMaker JumpStart 部署和测试 Llama 2-Chat 模型

现在您可以部署能够与用户进行互动对话的模型。在本示例中,我们选择了 Llama 2-chat 模型系列中的一个,如下代码所示。

my_model = JumpStartModel(model_id = "meta-textgeneration-llama-2-70b-f")

该模型需要使用 predictor = my_model.deploy() 部署到实时端点。SageMaker 将返回模型的端点名称,您可以使用该端点名称作为 endpoint_name 变量的值以备后用。

这里定义了一个 print_dialogue 函数来向聊天模型发送输入并接收其输出响应。发送的数据除了模型的超参数外,还包括以下内容:

  • max_new_tokens – 指模型在输出中可以生成的最大词元数。
  • top_p – 指模型在生成输出时可以保留的词元的累积概率。
  • temperature – 指模型生成输出的随机性。该值大于 0 或等于 1 会增加随机性水平,而等于 0 将生成最可能的词元。

您应该根据您的用例选择超参数,并对其进行适当测试。像 Llama 这个系列的模型需要您在调用时传递一个额外的参数,表示您已阅读并接受最终用户许可协议(EULA)。如下所示。

response = predictor.predict(payload, custom_attributes='accept_eula=true')

要测试所部署的模型,需要替换请求体中 content 部分的内容:"content": "what is the recipe of mayonnaise?"。您也可以使用自己的文本并更新超参数以更好地理解它们。

与部署嵌入模型类似,您也可以使用 SageMaker JumpStart UI 部署 Llama-70B-Chat:

  1. 在 SageMaker Studio 控制台上,选择导航窗格中的 JumpStart
  2. 搜索并选择 Llama-2-70b-Chat model
  3. 接受 EULA 并选择 Deploy,直接使用默认实例即可

与嵌入模型类似,您也可以通过为您的聊天模型的输入和输出创建内容模板来使用 LangChain 集成。在这种情况下,需要将输入定义为来自用户的输入,并指出它们受 system prompt 的约束。system prompt 的作用是告知模型它在特定场景下所需要扮演的特定角色。然后,在调用模型时传递这些提示词,并添加上述超参数和自定义属性(EULA 接受)。以下代码展示了调用方法。

llm = SagemakerEndpoint(
        endpoint_name=endpoint_name,
        region_name="us-east-1",
        model_kwargs={"max_new_tokens":500, "top_p": 0.1, "temperature": 0.4, "return_full_text": False},
        content_handler=content_handler,
        endpoint_kwargs = {"CustomAttributes": "accept_eula=true"}
    )

当端点可用时,我们就可以测试它是否按预期工作。您可以用自己的文本更新 llm("what is amazon sagemaker?")。同时还需要定义特定的 ContentHandler 来使用 LangChain 调用大语言模型,如该代码以及以下代码片段所示:

class ContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"
    def transform_input(self, prompt: str, model_kwargs: dict) -> bytes:
            payload = {
                "inputs": [
                    [
                        {
                            "role": "system",
                            "content": system_prompt,
                        },
                        {"role": "user", "content": prompt},
                    ],
                ],
                "parameters": model_kwargs,
            }
            input_str = json.dumps(
                payload,
            )
            return input_str.encode("utf-8")
   
    def transform_output(self, output: bytes) -> str:
            response_json = json.loads(output.read().decode("utf-8"))
            content = response_json[0]["generation"]["content"]
            return content
        
content_handler = ContentHandler()

使用 LlamaIndex 构建 RAG

接下来我们将使用 LlamaIndex 来构建 RAG 应用程序。您可以使用 pip 安装 LlamaIndex:pip install llama_index
首先,我们需要将数据(知识库)加载到 LlamaIndex 中进行索引。这涉及以下几个步骤:

1. 选择数据加载器

LlamaIndex 在 LlamaHub 上提供了许多用于常见数据类型 (如 JSON、CSV 和文本文件)以及其他数据源的数据连接器,允许我们摄取各种数据集。在本文中,我们使用 SimpleDirectoryReader 来摄取几个 PDF 文件。我们的数据样本是代码库中 press releases 文件夹中的两个 PDF 版本的 Amazon 新闻稿。加载 PDF 后,您可以看到它们已被转换为 11 个元素的列表。

除了直接加载文档,您还可以在为其创建索引之前,将 Document 对象转换为 Node 对象。是为整个 Document 对象创建索引,还是在索引之前先将 Document 转换为 Node 对象,这取决于您的特定用例和数据结构。对于长文档,检索文档的特定部分而不是整个文档,节点方法通常是一个不错的选择。更多相关信息,请参阅 Documents / Nodes

2. 实例化加载器并加载文档

此步骤初始化加载器类及任何所需的配置,例如是否忽略隐藏文件等。更多详细信息,请参阅 SimpleDirectoryReader

3. 调用加载器的 load_data 方法来解析源文件和数据,并将它们转换为 LlamaIndex 的 Document 对象,以便进行索引和查询。您可以使用以下代码完成数据摄取和准备,以使用 LlamaIndex 的索引和检索功能进行全文搜索:

docs = SimpleDirectoryReader(input_dir="pressrelease").load_data()

4. 构建索引

LlamaIndex 的一个关键特性是能够在数据上构建结构化的索引,表示为文档类型或节点类型。创建索引有助于高效查询数据。我们使用默认的内存向量存储和自定义的配置创建索引。LlamaIndex Settings 是一个配置对象,为 LlamaIndex 应用程序中的索引和查询操作提供常用资源和设置。它充当一个单例对象,因此允许您设置全局配置,同时还允许您通过直接传递该对象给相关接口(如大语言模型、嵌入模型)来覆盖特定组件的默认配置。当没有显式提供特定组件时,LlamaIndex 框架将会采用 Settings 对象中的配置作为全局默认设置。为了使用 LangChain 调用嵌入模型和大语言模型以及配置 Settings,我们需要安装 llama_index.embeddings.langchainllama_index.llms.langchain。我们可以按如下代码配置 Settings 对象:

Settings.embed_model = LangchainEmbedding(embeddings)
Settings.llm = LangChainLLM(llm)

默认情况下,VectorStoreIndex 会使用内存存储 SimpleVectorStore。但在真实场景中,通常需要使用外部向量存储,如 Amazon OpenSearch Service。有关更多详细信息,请参阅 Vector Engine for Amazon OpenSearch Serverless

index = VectorStoreIndex.from_documents(docs, service_context=service_context)

现在,我们可以使用 LlamaIndex 的 query_engine 对文档运行问答。为此,请传递此前创建的索引用作查询以及开始提问。查询引擎是一个通用接口,用于查询数据。它将自然语言查询作为输入,并返回内容丰富的响应。查询引擎通常借助于检索器建立在一个或多个索引之上。

query_engine = index.as_query_engine() print(query_engine.query("Since migrating to AWS in May, how much in operational cost Yellow.ai has reduced?"))

可以看到,RAG 解决方案能够从所提供的文档中检索到正确的答案:

According to the provided information, Yellow.ai has reduced its operational costs by 20% since migrating to AWS in May

使用 LangChain tools 和 agents

Loader 类被设计用于将数据加载到 LlamaIndex 中,或者作为 LangChain agent 中的一个 tool。这让您在应用程序中使用它时拥有更多的可能性和灵活性。首先,从LangChain agent 类中定义 tool。其中的 func 就是去查询此前使用 LlamaIndex 创建的索引,如下代码所示。

tools = [
    Tool(
        name="Pressrelease",
        func=lambda q: str(index.as_query_engine().query(q)),
        description="useful pressreleases for answering relevnat questions",
        return_direct=True,
    ),
]

然后,选择合适的 agent 类型。在本例子中,可以使用 chat-zero-shot-react-descriptionagent。使用这个 agent,大语言模型将会利用可用的 tool(此处为知识库的 RAG)来生成响应。这时可以通过传递 tool、llm 和 agent 类型来初始化 agent:

agent= initialize_agent(tools, llm, agent="chat-zero-shot-react-description", verbose=True)

可以看到,agent 经历了 thoughtsactionsobservation,同时运用了 tool (此处为此前所生成的索引文档),返回了一个结果:

'According to the provided press release, Yellow.ai has reduced its operational costs by 20%, driven performance improvements by 15%, and cut infrastructure costs by 10% since migrating to AWS. However, the specific cost savings from the migration are not mentioned in the provided information. It only states that the company has been able to reinvest the savings into innovation and AI research and development.'

您可以在该 GitHub repo 中找到相关的实现代码。

清理资源

为避免不必要的成本,您可以通过 Boto3 SDK 或 Amazon JumpStart UI 来清理资源。

要使用Boto3 SDK,请使用以下代码删除嵌入模型以及大语言模型的端点,同时还有端点配置:

client = boto3.client('sagemaker', region_name=aws_region)
client.delete_endpoint(EndpointName=endpoint_name)
client.delete_endpoint_config(EndpointConfigName=endpoint_configuration)

要使用 SageMaker 控制台,请执行以下步骤:

  1. 在 SageMaker 控制台的导航窗格中,选择 Inference 下的 Endpoints。
  2. 搜索此前创建的 embedding 模型和大语言模型端点。
  3. 在端点详情页面,选择 Delete。
  4. 再次选择 Delete 以确认删除。

结论

对于以搜索和检索为重点的使用场景,LlamaIndex 提供了各种灵活的功能。它擅长为大语言模型对外部文档进行索引和检索,使其成为深入探索数据的强大工具。LlamaIndex 能够让您创建结构化的数据索引,调用不同的 LLM,通过提升数据检索能力来提高大语言模型的性能,并使用自然语言查询数据。

这篇文章展示了一些 LlamaIndex 关键的概念和功能。我们使用 GPT-J 进行嵌入,使用 Llama 2-Chat 作为大语言模型来构建 RAG 应用程序。与此同时,您也可以使用任何合适的模型,您可以探索 SageMaker JumpStart 上提供所有的模型。

我们还展示了 LlamaIndex 如何提供强大、灵活的工具,将数据与其他框架(如 LangChain)连接、索引、检索和集成。通过 LlamaIndex 与 LangChain 的集成,您可以构建更强大、更通用、更具洞察力的大语言模型应用程序。


Original URL:https://aws.amazon.com/blogs/machine-learning/build-knowledge-powered-conversational-applications-using-llamaindex-and-llama-2-chat/

本篇作者

Romina Sharifpour

亚马逊云科技高级解决方案架构师

Nicole Pinto

亚马逊云科技解决方案架构师

校译作者

梁宇

亚马逊云科技专业服务团队 DevOps 顾问,主要负责 DevOps 技术实施。尤为热衷云原生服务及其相关技术。在工作之余,他喜欢运动,以及和家人一起旅游。