智能体:函数调用

LLM 函数调用是指大型语言模型 (LLM) 通过自然语言提示与用户定义的函数或 API 进行交互的能力。这使得模型能够执行特定任务、检索实时数据或执行其训练知识之外的计算。因此,通过集成外部资源或实时执行代码,模型可以提供更准确和动态的响应。

本文档演示了如何使用 Llama 3.1 70B 构建能够调用用户定义函数的 AI 智能体,该智能体由 LMDeploy 和 BentoML 提供支持。

定义的示例 Python 函数用于货币转换,并通过 API 暴露,允许用户提交如下查询

{
   "query": "I want to exchange 42 US dollars to Canadian dollars"
}

应用程序处理此请求,并使用虚拟汇率 1:3.14159 将 USD 转换为 CAD 并进行响应。

The converted amount of 42 US dollars to Canadian dollars is 131.95.

此示例已准备好轻松部署到 BentoCloud 并进行扩展。只需一个命令,您就可以部署一个生产级应用程序,该应用程序具有快速自动扩展、在您的云中安全部署以及全面的可观测性。

Animated GIF demonstrating the function calling capability in BentoCloud playground, showing currency conversion queries and responses

架构

此示例包含两个 BentoML 服务:货币兑换助手和一个 LLM。LLM 服务暴露了一个与 OpenAI 兼容的 API,因此兑换助手可以调用 OpenAI 客户端。以下是此示例的通用工作流程

Architecture diagram showing the workflow between Exchange Assistant and LLM services, illustrating how user queries are processed through function calling
  1. 用户向兑换助手的查询 API 提交查询,该 API 处理查询并将其转发给 LLM 以确定所需的函数并提取参数。

  2. 利用提取的参数,查询 API 调用已确定的兑换函数,该函数负责使用指定的参数执行兑换转换。

  3. 兑换函数计算出结果后,会将结果发送回 LLM。然后,LLM 使用这些数据生成自然语言响应,并通过兑换助手返回给用户。

代码解释

您可以在 GitHub 上找到源代码。以下是此项目中关键代码实现的详细说明。

service.py

service.py 文件概述了两个所需的 BentoML 服务的逻辑。

  1. 首先指定项目使用的 LLM。本示例使用 Llama 3.1 70B Instruct AWQ INT4,您可以根据需要选择其他模型。

    service.py
    MODEL_ID = "hugging-quants/Meta-Llama-3.1-70B-Instruct-AWQ-INT4"
    
  2. 创建一个 Python 类(示例中的 Llama)来初始化模型和分词器,并使用以下装饰器添加 BentoML 功能。

    • @bentoml.service: 将此类转换为 BentoML 服务。您可以选择设置 配置,例如超时和在 BentoCloud 上使用的 GPU 资源。我们建议您使用 80 GB 的 NVIDIA A100 GPU 以获得最佳性能。

    • @bentoml.asgi_app: 将 openai_endpoints.py 文件中定义的现有 ASGI 应用程序挂载到此类。它将基本路径设置为 /v1,使其可通过 HTTP 请求访问。挂载的 ASGI 应用程序提供与 OpenAI 兼容的 API,并且可以与 LLM 服务并行提供服务。更多信息,请参阅挂载 ASGI 应用程序

    service.py
    import bentoml
    from openai_endpoints import openai_api_app
    
    @bentoml.asgi_app(openai_api_app, path="/v1")
    @bentoml.service(
        traffic={
            "timeout": 300,
        },
        resources={
            "gpu": 1,
            "gpu_type": "nvidia-a100-80gb",
        },
    )
    class Llama:
       # Declare the model as a class variable
       hf_model = bentoml.models.HuggingFaceModel(MODEL_ID)
    
       def __init__(self) -> None:
       # Logic to initialize the model and tokenizer
       ...
    

    在该类中,从 Hugging Face 加载模型并将其定义为类变量。HuggingFaceModel 方法提供了一种高效的机制来加载 AI 模型,从而加速在 BentoCloud 上的模型部署,减少镜像构建时间和冷启动时间。

  3. @bentoml.service 装饰器还允许您定义 Bento(BentoML 中的统一分发格式)的运行时环境。一个 Bento 包含所有源代码、Python 依赖项、模型引用和环境设置,从而易于在不同环境中一致地部署。

    以下是一个示例

    service.py
    my_image = bentoml.images.Image(python_version='3.11') \
                  .requirements_file("requirements.txt")
    
    @bentoml.service(
        image=my_image, # Apply the specifications
        ...
    )
    class Llama:
        ...
    
  4. 接下来,使用 @bentoml.service 装饰器创建另一个名为 ExchangeAssistant 的 BentoML 服务。与 LLM 不同,函数调用不需要 GPU,并且可以使用单个 CPU 运行。将它们运行在单独的实例上还意味着您以后可以在 BentoCloud 上独立地对它们进行扩展。

    ExchangeAssistant 服务中的关键元素

    • bentoml.depends(): 此函数调用 Llama 服务作为依赖项,这使得 ExchangeAssistant 可以利用其所有功能。更多信息,请参阅运行分布式服务

    • 服务初始化:由于 Llama 服务提供与 OpenAI 兼容的端点,您可以使用其 HTTP 客户端和 client_url 构建一个 OpenAI 客户端与之交互。

    • 一个面向前端的 API /exchange: 使用 @bentoml.api 装饰器定义端点,用于处理货币兑换查询。

    service.py
    from openai import OpenAI
    
    @bentoml.service(
          image=my_image,
          resources={"cpu": "1"}
    )
    class ExchangeAssistant:
        # Declare dependency on the Llama class
        llm = bentoml.depends(Llama)
    
        def __init__(self):
            # Setup HTTP client to interact with the LLM
             self.client = OpenAI(
                  base_url=f"{self.llm.client_url}/v1",
                  http_client=self.llm.to_sync.client,
                  api_key="API_TOKEN_NOT_NEEDED"
            )
            ...
    
        @bentoml.api
        def exchange(self, query: str = "I want to exchange 42 US dollars to Canadian dollars") -> str:
          # Implementation logic
    
  5. exchange 方法使用 OpenAI 客户端将函数调用能力与指定的 LLM 集成。解析查询以确定所需函数并提取相关参数后,它会调用已确定的兑换函数生成结果。有关 OpenAI 函数调用客户端 API 的详细信息,请参阅OpenAI 文档

    service.py
    @bentoml.api
    def exchange(self, query: str = "I want to exchange 42 US dollars to Canadian dollars") -> str:
          tools = [
              {
                  "type": "function",
                  "function": {
                      "name": "convert_currency",
                      "description": "Convert from one currency to another. Result is returned in the 'converted_amount' key.",
                      "parameters": {
                          "type": "object",
                          "properties": {
                              "from_currency": {"type": "string", "description": "The source currency to convert from, e.g. USD",},
                              "to_currency": {"type": "string", "description": "The target currency to convert to, e.g. CAD",},
                              "amount": {"type": "number", "description": "The amount to be converted"},
                          },
                          "required": [],
                      },
                  },
              }
          ]
          messages = [
              {"role": "system", "content": SYSTEM_PROMPT},
              {"role": "user", "content": query},
          ]
          response_message = self.client.chat.completions.create(
              model=MODEL_ID,
              messages=messages,
              tools=tools,
          ).choices[0].message
          tool_calls = response_message.tool_calls
    
  6. 然后您可以调用该函数并根据需要添加其他函数。确保 JSON 中的函数定义与相应的 Python 函数签名匹配。

    service.py
          # Check if there are function calls from the LLM response
          if tool_calls:
    
              # Map the function name to the actual method
              available_functions = {
                  "convert_currency": self.convert_currency,
              }
    
              # Append the initial LLM response to messages for complete context
              messages.append(response_message)
              for tool_call in tool_calls:
                  function_name = tool_call.function.name
                  function_to_call = available_functions[function_name]
                  function_args = json.loads(tool_call.function.arguments)
    
                  # Call the mapped function with parsed arguments
                  function_response = function_to_call(
                      from_currency=function_args.get("from_currency"),
                      to_currency=function_args.get("to_currency"),
                      amount=function_args.get("amount"),
                  )
    
                  # Append function responses to the message chain
                  messages.append(
                      {
                          "role": "user",
                          "name": function_name,
                          "content": function_response,
                      }
                  )
    
              # Generate the final response from the LLM incorporating the function responses
              final_response = self.client.chat.completions.create(
                  model=MODEL_ID,
                  messages=messages,
              )
              return final_response.choices[0].message.content
          else:
              return "Unable to use the available tools."
    

试一试

您可以在 BentoCloud 上运行此示例项目,或在本地提供服务,将其容器化为符合 OCI 标准的镜像并在任何地方部署。

BentoCloud

BentoCloud 提供快速且可扩展的基础设施,用于在云端使用 BentoML 构建和扩展 AI 应用程序。

  1. 安装 BentoML 并通过 BentoML CLI 登录 BentoCloud。如果您没有 BentoCloud 账户,请在此处免费注册

    pip install bentoml
    bentoml cloud login
    
  2. 克隆仓库并将项目部署到 BentoCloud。

    git clone https://github.com/bentoml/BentoFunctionCalling.git
    cd BentoFunctionCalling
    bentoml deploy
    
  3. 在 BentoCloud 上运行后,您可以通过以下方式调用端点

    Animated GIF demonstrating the function calling capability in BentoCloud playground, showing currency conversion queries and responses
    import bentoml
    
    with bentoml.SyncHTTPClient("<your_deployment_endpoint_url>") as client:
       response_generator = client.exchange(
             query="I want to exchange 42 US dollars to Canadian dollars"
              )
       for response in response_generator:
            print(response, end='')
    
    curl -X 'POST' \
      '<your_deployment_endpoint_url>/exchange' \
      -H 'accept: text/plain' \
      -H 'Content-Type: application/json' \
      -d '{
        "query": "I want to exchange 42 US dollars to Canadian dollars"
    }'
    
  4. 为了确保部署在一定副本范围内自动扩展,请添加扩展标志

    bentoml deploy --scaling-min 0 --scaling-max 3 # Set your desired count
    

    如果已部署,请按如下方式更新其允许的副本数

    bentoml deployment update <deployment-name> --scaling-min 0 --scaling-max 3 # Set your desired count
    

    更多信息,请参阅如何配置并发和自动扩展

本地服务

BentoML 允许您在本地运行和测试代码,以便您可以使用本地计算资源快速验证代码。

重要提示

要在本地提供此项目服务,您需要一块具有足够显存的 Nvidia GPU 来运行 LLM。我们建议您使用 80 GB 的 NVIDIA A100 GPU 来运行所包含的 Llama 3.1 70B Instruct AWQ INT4,以获得最佳性能。

  1. 克隆项目仓库并安装依赖项。

    git clone https://github.com/bentoml/BentoFunctionCalling.git
    cd BentoFunctionCalling
    
    # Recommend Python 3.11
    pip install -r requirements.txt
    
  2. 在本地提供服务。

    bentoml serve
    
  3. 访问或向 https://:3000 发送 API 请求。

要在您自己的基础设施中进行自定义部署,请使用 BentoML 生成符合 OCI 标准的镜像