使用 Triton 推理服务器进行函数调用#

本教程重点介绍函数调用,这是一种将大型语言模型 (LLM) 轻松连接到外部工具的常用方法。此方法使 AI 代理能够有效地使用工具并与外部 API 无缝交互,从而显著扩展其功能和实际应用。

目录#

什么是函数调用?#

函数调用是指 LLM 的以下能力:

  • 识别何时需要使用特定函数或工具来回答查询或执行任务。

  • 生成包含调用该函数所需参数的结构化输出。

  • 将函数调用的结果集成到其响应中。

函数调用是一种强大的机制,使 LLM 能够执行更复杂的任务(例如,多代理系统中的代理编排),这些任务需要超出其固有知识的特定计算或数据检索。通过识别何时需要特定函数,LLM 可以动态扩展其功能,使其在实际应用中更加通用和有用。

教程概述#

本教程演示了如何使用 Hermes-2-Pro-Llama-3-8B 模型进行函数调用,该模型已针对此功能进行了预微调。我们将创建一个基本的股票报告代理,该代理提供最新的股票信息并总结最近的公司新闻。

先决条件:Hermes-2-Pro-Llama-3-8B#

在继续之前,请确保您已按照这些步骤使用 Triton 推理服务器和 TensorRT-LLM 后端成功部署了 Hermes-2-Pro-Llama-3-8B 模型。

[!IMPORTANT] 确保在启动 docker 容器时,tutorials 文件夹已挂载到 /tutorials

函数定义#

我们将为我们的股票报告代理定义三个函数:

  1. get_current_stock_price:检索给定股票代码的当前股价。

  2. get_company_news:检索给定股票代码的公司新闻和新闻稿。

  3. final_answer:用作空操作并指示最终响应。

每个函数都包括其名称、描述和输入参数架构:

TOOLS = [
   {
       "type": "function",
       "function": {
           "name": "get_current_stock_price",
           "description": "Get the current stock price for a given symbol.\n\nArgs:\n  symbol (str): The stock symbol.\n\nReturns:\n  float: The current stock price, or None if an error occurs.",
           "parameters": {
               "type": "object",
               "properties": {"symbol": {"type": "string"}},
               "required": ["symbol"],
           },
       },
   },
   {
       "type": "function",
       "function": {
           "name": "get_company_news",
           "description": "Get company news and press releases for a given stock symbol.\n\nArgs:\nsymbol (str): The stock symbol.\n\nReturns:\npd.DataFrame: DataFrame containing company news and press releases.",
           "parameters": {
               "type": "object",
               "properties": {"symbol": {"type": "string"}},
               "required": ["symbol"],
           },
       },
   },
   {
       "type": "function",
       "function": {
           "name": "final_answer",
           "description": "Return final generated answer",
           "parameters": {
               "type": "object",
               "properties": {"final_response": {"type": "string"}},
               "required": ["final_response"],
           },
       },
   },
]

这些函数定义将通过提示传递给我们的模型,使其能够在对话期间识别并适当地使用它们。

有关实际实现,请参阅 client_utils.py

提示工程#

提示工程是函数调用的一个关键方面,因为它指导 LLM 识别何时以及如何使用特定函数。通过仔细设计提示,您可以有效地定义 LLM 的角色、目标以及它可以访问的工具,从而确保准确高效的任务执行。

对于我们的任务,我们组织了一个示例提示结构,在随附的 system_prompt_schema.yml 文件中提供。该文件详细概述了:

  • 角色:定义 LLM 预期执行的特定角色。

  • 目标:明确说明交互的目标或期望结果。

  • 工具:列出 LLM 可以用来实现其目标的可用函数或工具。

  • 架构:指定调用每个工具或函数所需的结构和格式。

  • 说明:提供一套清晰的指南,以确保 LLM 遵循预期的路径并适当地使用工具。

通过利用提示工程,您可以增强 LLM 执行复杂任务的能力,并将函数调用无缝集成到其响应中,从而最大限度地提高其在各种应用中的实用性。

将所有内容结合在一起#

首先,让我们启动 Triton SDK 容器:

# Using the SDK container as an example
docker run --rm -it --net host --shm-size=2g \
    --ulimit memlock=-1 --ulimit stack=67108864 --gpus all \
    -v /path/to/tutorials/:/tutorials \
    -v /path/to/tutorials/repo:/tutorials \
    nvcr.io/nvidia/tritonserver:<xx.yy>-py3-sdk

提供的客户端脚本使用 pydanticyfinance 库,这些库我们未随 sdk 容器一起提供。请确保在继续之前安装它们:

pip install pydantic yfinance

按如下方式运行提供的 client.py

python3 /tutorials/AI_Agents_Guide/Function_Calling/artifacts/client.py --prompt "Tell me about Rivian. Include current stock price in your final response." -o 200

您应该会看到类似于以下的响应:

+++++++++++++++++++++++++++++++++++++
RESPONSE: Rivian, with its current stock price of <CURRENT STOCK PRICE>, <NEWS SUMMARY>
+++++++++++++++++++++++++++++++++++++

要查看我们的 LLM “调用”了哪些工具,只需添加 verbose 标志,如下所示:

python3 /tutorials/AI_Agents_Guide/Function_Calling/artifacts/client.py --prompt "Tell me about Rivian. Include current stock price in your final response." -o 200 --verbose

这将显示函数调用的逐步过程,包括:

  • 正在调用的工具

  • 传递给每个工具的参数

  • 来自每个函数调用的响应

  • 最终总结的响应

[b'\n{\n  "step": "1",\n  "description": "Get the current stock price for Rivian",\n  "tool": "get_current_stock_price",\n  "arguments": {\n    "symbol": "RIVN"\n  }\n}']
=====================================
Executing function: get_current_stock_price({'symbol': 'RIVN'})
Function response: <CURRENT STOCK PRICE>
=====================================
[b'\n{\n  "step": "2",\n  "description": "Get company news and press releases for Rivian",\n  "tool": "get_company_news",\n  "arguments": {\n    "symbol": "RIVN"\n  }\n}']
=====================================
Executing function: get_company_news({'symbol': 'RIVN'})
Function response: [<LIST OF RECENT NEWS TITLES>]
=====================================
[b'\n{\n  "step": "3",\n  "description": "Summarize the company news and press releases for Rivian",\n  "tool": "final_answer",\n  "arguments": {\n    "final_response": "Rivian, with its current stock price of  <CURRENT STOCK PRICE>, <NEWS SUMMARY>"\n  }\n}']


+++++++++++++++++++++++++++++++++++++
RESPONSE: Rivian, with its current stock price of  <CURRENT STOCK PRICE>, <NEWS SUMMARY>
+++++++++++++++++++++++++++++++++++++

[!TIP] 在本教程中,所有功能(工具定义、实现和执行)都在客户端实现(请参阅 client.py)。对于生产场景,尤其是在预先知道函数的情况下,请考虑在服务器端实现此逻辑。服务器端实现的推荐方法是通过 Triton 集成BLS 部署您的工作流程。使用预处理模型将用户提示与系统提示和可用工具结合并格式化。使用后处理模型来管理对已部署 LLM 的多次调用,以根据需要获得最终答案。

进一步优化#

强制输出格式#

在本教程中,我们演示了如何使用提示工程来强制执行特定的输出格式。所需的结构如下:

  {
    "step" : <Step number>
    "description": <Description of what the step does and its output>
    "tool": <Tool to use>,
    "arguments": {
        <Parameters to pass to the tool as a valid dict>
    }
  }

但是,在某些情况下,输出可能会偏离此必需的架构。例如,考虑以下提示执行:

python3 /tutorials/AI_Agents_Guide/Function_Calling/artifacts/client.py --prompt "How Rivian is doing?" -o 500 --verbose

此执行可能会因无效的 JSON 格式错误而失败。详细输出将显示最终 LLM 响应包含纯文本,而不是预期的 JSON 格式:

{
  "step": "3",
  "description": <Description of what the step does and its output>
  "tool": "final_answer",
  "arguments": {
    "final_response": <Final Response>
  }
}

幸运的是,可以使用约束解码来控制此行为,约束解码是一种指导模型生成满足特定格式和内容要求的输出的技术。我们强烈建议您探索我们关于约束解码的专门教程,以更深入地了解并增强您有效管理模型输出的能力。

[!TIP] 为了获得最佳结果,请使用 client_utils.py 中定义的 FunctionCall 类作为 Logits 后处理器的 JSON 架构。此方法确保一致且格式正确的输出,与我们在本教程中建立的结构保持一致。

并行工具调用#

本教程重点介绍单轮强制调用,即提示 LLM 在单次交互中进行特定的函数调用。当需要立即执行精确操作时,此方法很有用,可确保函数作为当前对话的一部分执行。

有可能同时执行某些函数调用。此技术对于可以划分为独立操作的任务非常有利,从而可以提高效率并缩短响应时间。

我们鼓励读者接受实现并行工具调用的挑战,作为一项实践练习。

参考文献#

本教程的部分内容基于 Hermes-Function-Calling