Press "Enter" to skip to content

学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理

大型语言模型(LLM)代理是通过以下两种方式扩展独立LLM的能力的程序:1)访问外部工具(API、函数、Webhooks、插件等),和2)以自主方式计划和执行任务。通常情况下,LLM需要与其他软件、数据库或API进行交互以完成复杂的任务。例如,一个安排会议的管理聊天机器人需要访问员工的日历和电子邮件。通过访问工具,LLM代理可以变得更强大,但也会增加复杂性。

在本文中,我们介绍了LLM代理,并演示了如何使用Amazon SageMaker JumpStart和AWS Lambda构建和部署一个电子商务LLM代理。该代理将使用工具提供新功能,例如回答关于退货的问题(“我的退货rtn001处理完毕了吗?”)以及提供关于订单的更新(“能告诉我订单123456是否已发货?”)。这些新功能需要LLM从多个数据源(orders、returns)获取数据并执行检索增强生成(RAG)。

为了驱动LLM代理,我们使用部署为SageMaker端点的Flan-UL2模型,并使用使用AWS Lambda构建的数据检索工具。该代理随后可以与Amazon Lex集成,并作为网站或AWS Connect中的聊天机器人使用。我们在本文中还总结了在部署LLM代理到生产环境之前需要考虑的事项。对于构建LLM代理的全面管理体验,AWS还提供了用于Amazon Bedrock的代理功能(预览版)。

LLM代理架构简介

LLM代理是使用LLM来决定何时以及如何使用工具以完成复杂任务的程序。有了工具和任务规划能力,LLM代理可以与外部系统交互,并克服LLM的传统限制,例如知识截断、幻觉和不精确计算。工具可以采取多种形式,例如API调用、Python函数或基于Webhooks的插件。例如,LLM可以使用“检索插件”来获取相关上下文并执行RAG。

那么,LLM选择工具和规划任务意味着什么?有许多方法(如ReAct、MRKL、Toolformer、HuggingGPT和Transformer Agents)可以使用LLM与工具,而且进展迅速。但是,一种简单的方法是使用工具列表提示LLM,并要求其确定1)是否需要使用工具来满足用户查询,以及如果需要,2)选择适当的工具。这样的提示通常如下所示,并且可以包括少量示例以提高LLM在选择正确工具方面的可靠性。

‘’’
您的任务是选择一个工具来回答用户的问题。您可以使用以下工具。

搜索:在常见问题解答中搜索答案
订单:订购物品
无操作:不需要使用工具

{少量示例}

问题:{输入}
工具:
‘’’

更复杂的方法涉及使用专门的LLM,可以直接解码“API调用”或“工具使用”,例如GorillaLLM。这些微调的LLM是通过在API规范数据集上进行训练来识别和预测基于指令的API调用。通常,这些LLM需要有关可用工具的一些元数据(描述、yaml或JSON模式用于其输入参数),以便输出工具调用。Amazon Bedrock和OpenAI函数调用的代理采用了这种方法。请注意,通常需要LLM足够大且复杂才能显示工具选择能力。

学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第1张

假设已选择了任务规划和工具选择机制,则典型的LLM代理程序按照以下顺序工作:

  1. 用户请求 – 程序接收来自某个客户端应用程序的用户输入,例如“我的订单123456在哪里?”。
  2. 计划下一步操作并选择要使用的工具 – 接下来,程序使用提示让LLM生成下一步操作,例如“使用OrdersAPI查找订单表。” LLM被提示从预定义的可用工具及其描述中选择一个工具名称,例如OrdersAPI。或者,可以指示LLM直接生成具有输入参数(例如OrdersAPI(12345))的API调用。
    1. 请注意,下一步操作可能涉及使用工具或API,也可能不涉及。如果不需要使用工具,则LLM将在不考虑工具提供的额外上下文的情况下回应用户输入,或者简单地返回固定的回答,例如“我无法回答这个问题。”
  3. 解析工具请求 – 接下来,我们需要解析并验证LLM建议的工具/操作预测。验证是必要的,以确保工具名称、API和请求参数不是虚构的,并且工具根据规范被正确调用。此解析可能需要单独的LLM调用。
  4. 调用工具 – 确保有效的工具名称和参数后,我们调用工具。这可以是一个HTTP请求、函数调用等。
  5. 解析输出 – 来自工具的响应可能需要进行附加处理。例如,API调用可能会产生一个包含大量JSON响应的结果,其中只有一部分字段对LLM很重要。以清晰、标准化的格式提取信息可以帮助LLM更可靠地解释结果。
  6. 解释输出 – 给定来自工具的输出,LLM再次被提示来理解它,并决定是否可以生成最终答案回复给用户,或者是否需要进一步的操作。
  7. 终止或继续到步骤2 – 返回最终答案,或在出现错误或超时的情况下返回默认答案。

不同的代理框架以不同的方式执行上述程序流程。例如,ReAct将工具选择和最终答案生成合并为一个提示,而不是使用单独的提示进行工具选择和答案生成。此外,这个逻辑可以在一个单独的步骤中运行,也可以在一个“代理循环”中运行(使用“代理循环”时会在生成最终答案、抛出异常或超时时终止)。不变的是,代理使用LLM作为核心来协调规划和工具调用,直到任务终止。接下来,我们将展示如何使用AWS服务实现一个简单的代理循环。

解决方案概述

在本博客文章中,我们实现了一个由工具驱动的电子商务支持LLM代理,提供两个功能:

  • 退货状态查询工具 – 回答关于退货状态的问题,例如:“我的退货rtn001正在发生什么?”
  • 订单状态查询工具 – 跟踪订单的状态,例如:“我的订单123456的状态是什么?”

该代理有效地将LLM用作查询路由器。给定一个查询(“订单123456的状态是什么?”),选择适当的查询工具来查询多个数据源(即退货和订单)。我们通过使LLM从多个查询工具中选择,这些工具负责与数据源交互和获取上下文,来实现查询路由。这扩展了简单的RAG模式,该模式假设只有一个数据源。

两个查询工具都是Lambda函数,以orderIdreturnId作为输入,从数据源获取JSON对象,并将JSON转换为适合LLM使用的人类可读字符串。在实际场景中,数据源可以是一个高度可扩展的NoSQL数据库,如DynamoDB,但本解决方案使用简单的Python Dict 来存储演示数据。

通过添加查询工具和相应地修改提示,可以为代理添加其他功能。该代理可以作为一个独立的服务进行测试,并通过HTTP与任何UI集成,这可以很容易地通过Amazon Lex实现。

学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第2张

以下是一些关键组件的附加细节:

  1. LLM推理端点 – 代理程序的核心是LLM。我们将使用SageMaker JumpStart基础模型库轻松部署Flan-UL2模型。SageMaker JumpStart使得轻松部署LLM推理端点到专用的SageMaker实例变得容易。
  2. 代理协调器 – 代理协调器协调LLM、工具和客户端应用程序之间的交互。对于我们的解决方案,我们使用AWS Lambda函数来驱动这个流程,并使用以下作为辅助函数。
    • 任务(工具)规划器 – 任务规划器使用LLM来建议以下中的一个:1)退货查询,2)订单查询,或者3)没有工具。我们仅使用提示工程和原样使用Flan-UL2模型,而不进行微调。
    • 工具解析器 – 工具解析器确保任务规划器提供的工具建议是有效的。特别地,我们确保可以解析一个单独的orderIdreturnId。否则,我们会回复一个默认的消息。
    • 工具分发器 – 工具分发器使用有效参数调用工具(Lambda函数)。
    • 输出解析器 – 输出解析器将JSON清理并从中提取相关项,转换为人类可读的字符串。这个任务既由每个查询工具完成,也由协调器内部完成。
    • 输出解释器 – 输出解释器的责任是1)解释工具调用的输出和2)确定用户请求是否可以被满足或是否需要额外的步骤。如果是后者,将单独生成并返回最终响应。

现在,让我们深入了解一下关键组件:代理协调器、任务规划器和工具分发器。

Agent orchestrator

以下是代理编排器 Lambda 函数内部代理循环的缩写版本。循环使用了task_plannertool_parser等辅助函数,将任务模块化。这里的循环设计最多运行两次,以防止 LLM 在循环中被卡住过长时间。

#.. 导入 ..
MAX_LOOP_COUNT = 2 #在最多两次迭代后停止代理循环
#... 辅助函数定义 ...
def agent_handler(event):
    user_input = event["query"]
    print(f"user input: {user_input}") 
    
    final_generation = ""
    is_task_complete = False
    loop_count = 0 

    #开始代理循环
    while not is_task_complete and loop_count < MAX_LOOP_COUNT:
        tool_prediction = task_planner(user_input)
        print(f"tool_prediction: {tool_prediction}")  
        
        tool_name, tool_input, tool_output, error_msg = None, None, "", ""

        try:
            tool_name, tool_input = tool_parser(tool_prediction, user_input)
            print(f"tool name: {tool_name}") 
            print(f"tool input: {tool_input}") 
        except Exception as e:
            error_msg = str(e)
            print(f"tool parse error: {error_msg}")  
    
        if tool_name is not None: #如果选择和解析出有效工具
            raw_tool_output = tool_dispatch(tool_name, tool_input)
            tool_status, tool_output = output_parser(raw_tool_output)
            print(f"tool status: {tool_status}")  

            if tool_status == 200:
                is_task_complete, final_generation = output_interpreter(user_input, tool_output) 
            else:
                final_generation = tool_output
        else: #如果没有选择和解析出有效工具,则返回默认消息或错误消息
            final_generation = DEFAULT_RESPONSES.NO_TOOL_FEEDBACK if error_msg == "" else error_msg
    
        loop_count += 1

    return {
        'statusCode': 200,
        'body': final_generation
    }

任务规划器(工具预测)

代理编排器使用task planner根据用户输入预测检索工具。对于我们的 LLM 代理,我们将简单地使用提示工程和少量示例提示来教授 LLM 在上下文中完成此任务。更复杂的代理可以使用经过微调的 LLM 进行工具预测,这超出了本文的范围。提示如下:

tool_selection_prompt_template = """
您的任务是选择适当的工具来满足用户输入。如果不需要工具,则选择“no_tool”

可用工具有:

returns_inquiry: 关于特定退货状态的信息数据库,无论是待处理、已处理等。
order_inquiry: 关于特定订单状态的信息,如送货状态、产品、金额等。
no_tool: 不需要工具来回答用户输入。

您可以建议多个工具,用逗号分隔。

示例:
用户:“您的营业时间是什么?”
工具:no_tool

用户:“订单 12345 已发货吗?”
工具:order_inquiry

用户:“已处理退货 ret812 吗?”
工具:returns_inquiry

用户:“我还剩几天可以退货?”
工具:returns_inquiry

用户:“订单 38745 的订单总额是多少?”
工具:order_inquiry

用户:“基于店铺政策,我可以退回订单 38756 吗?”
工具:order_inquiry

用户:“你好”
工具:no_tool

用户:“你是人工智能吗?”
工具:no_tool

用户:“天气如何?”
工具:no_tool

用户:“订单 12347 的退款状态是什么?”
工具:order_inquiry

用户:“退货 ret172 的退款状态是什么?”
工具:returns_inquiry

用户输入:{}
工具:
"""

工具调度器

工具调度机制通过if/else逻辑来调用适当的 Lambda 函数,具体取决于工具的名称。以下是tool_dispatch辅助函数的实现。它在代理循环内使用,并返回工具 Lambda 函数的原始响应,然后由output_parser函数进行清理。

def tool_dispatch(tool_name, tool_input):
    #...
     
    tool_response = None 

    if tool_name == "returns_inquiry":
        tool_response = lambda_client.invoke(
            FunctionName=RETURNS_DB_TOOL_LAMBDA,
            InvocationType="RequestResponse",
            Payload=json.dumps({
              "returnId": tool_input  
            })
        )
    elif tool_name == "order_inquiry":
        tool_response = lambda_client.invoke(
            FunctionName=ORDERS_DB_TOOL_LAMBDA,
            InvocationType="RequestResponse",
            Payload=json.dumps({
                "orderId": tool_input
            })
        )
    else:
        raise ValueError("无效的工具调用")
        
    return tool_response

部署解决方案

重要的先决条件 –要开始部署,您需要满足以下先决条件:

  • 通过能够启动AWS CloudFormation堆栈的用户访问AWS管理控制台
  • 熟悉导航AWS Lambda和Amazon Lex控制台
  • Flan-UL2需要一个ml.g5.12xlarge进行部署,这可能需要通过支持票增加资源限制。在我们的示例中,我们使用us-east-1作为区域,请确保增加us-east-1中的服务配额(如果需要)。

使用CloudFormation进行部署 –您可以通过点击下面的按钮将解决方案部署到us-east-1学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第3张

部署解决方案大约需要20分钟,并创建一个LLMAgentStack堆栈,其中包括:

  • 使用来自SageMaker JumpStart的Flan-UL2模型部署SageMaker端点;
  • 部署三个Lambda函数:LLMAgentOrchestratorLLMAgentReturnsToolLLMAgentOrdersTool;以及
  • 部署一个可以用来测试代理的AWS Lex机器人:Sagemaker-Jumpstart-Flan-LLM-Agent-Fallback-Bot

测试解决方案

该堆栈部署了一个名为Sagemaker-Jumpstart-Flan-LLM-Agent-Fallback-Bot的Amazon Lex机器人。可以使用该机器人对代理进行端到端测试。以下是关于使用Lambda集成测试AWS Amazon Lex机器人以及集成工作的高级综合指南。简而言之,Amazon Lex机器人是一个提供与我们构建的Lambda函数内运行的LLM代理进行聊天的快速界面的资源(LLMAgentOrchestrator)。

要考虑的示例测试案例如下:

  • 有效的订单查询(例如,“为订单123456订购了哪个物品?”)
    • 订单“123456”是一个有效的订单,因此我们应该期望得到一个合理的答案(例如“Herbal Handsoap”)
  • 有效的退货查询(例如,“我的退货rtn003何时处理?”)
    • 我们应该期望得到有关退货状态的合理答案。
  • 与退货或订单都无关(例如,“苏格兰现在的天气如何?”)
    • 与退货或订单无关的问题,因此应返回一个默认答案(“对不起,我无法回答该问题。”)
  • 无效的订单查询(例如,“为订单383833订购了哪个物品?”)
    • 订单数据集中不存在id 383832,因此我们应该优雅地处理错误(例如,“未找到订单,请检查您的订单ID。”)
  • 无效的退货查询(例如,“我的退货rtn123何时处理?”)
    • 类似地,退货数据集中不存在id rtn123,因此应优雅地处理错误。
  • 与退货查询无关(例如,“退货rtn001对世界和平的影响是什么?”)
    • 这个问题虽然似乎与一个有效订单有关,但是与之无关。LLM用于过滤具有无关上下文的问题。

要自行运行这些测试,请按照以下说明进行操作。

  1. 在Amazon Lex控制台(AWS控制台> Amazon Lex)上,导航到名为Sagemaker-Jumpstart-Flan-LLM-Agent-Fallback-Bot的机器人。该机器人已经配置好,当触发FallbackIntent时,会调用LLMAgentOrchestrator Lambda函数。
  2. 在导航窗格中,选择意图学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第4张
  3. 选择右上角的构建学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第5张
  4. 等待构建过程完成。完成后,您将收到一个成功消息,如下面的截图所示。 学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第6张
  5. 通过输入测试用例来测试机器人。 学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第7张

清理

为了避免额外费用,请按照以下步骤删除我们解决方案创建的资源:

  • AWS CloudFormation控制台上,选择名为LLMAgentStack的堆栈(或您选择的自定义名称)。
  • 选择删除
  • 检查在CloudFormation控制台中堆栈是否已删除。

重要:请确保通过确保Flan-UL2推理端点已被删除来双重检查堆栈是否成功删除。

  • 要检查,请转到AWS控制台> Sagemaker> 端点> 推理页面。
  • 页面应列出所有活动的端点。
  • 确保sm-jumpstart-flan-bot-endpoint不存在,就像下面的截图一样。

学习如何使用AWS SageMaker JumpStart基础模型构建和部署使用工具的LLM代理 四海 第8张

生产考虑事项

将LLM代理部署到生产环境需要采取额外的步骤来确保可靠性、性能和可维护性。在部署代理到生产环境之前,需要考虑以下几点:

  • 选择用于驱动代理循环的LLM模型:对于本文讨论的解决方案,我们使用了一个未经微调的Flan-UL2模型来执行任务规划或工具选择。在实践中,使用经过微调的LLM直接输出工具或API请求可以提高可靠性和性能,并简化开发。我们可以对工具选择任务进行微调LLM,或使用直接解码工具令牌的模型,如Toolformer。
    • 使用经过微调的模型还可以简化向代理添加、移除和更新可用工具。对于仅基于提示的方法,更新工具需要修改代理协调器中的每个提示,例如任务规划、工具解析和工具调度的提示。这可能很麻烦,并且如果提供给LLM的上下文中提供了太多工具,性能可能会下降。
  • 可靠性和性能:LLM代理可能不可靠,特别是对于无法在几个循环内完成的复杂任务。添加输出验证、重试、将LLM的输出结构化为JSON或yaml,并强制执行超时以提供用于退出循环的逃生通道,可以增强可靠性。

结论

在本文中,我们探讨了如何从头开始构建一个可以利用多个工具的LLM代理,使用低级别提示工程、AWS Lambda函数和SageMaker JumpStart作为构建块。我们详细讨论了LLM代理的架构和代理循环。本博文介绍的概念和解决方案架构可能适用于使用预定义工具集的少数工具的代理。我们还讨论了在生产环境中使用代理的几种策略。Bedrock的代理,目前处于预览阶段,还提供了构建代理的托管体验,并原生支持代理工具的调用。

Leave a Reply

Your email address will not be published. Required fields are marked *