Transformers documentation

工具使用

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

工具使用

聊天模型通常经过训练以支持“函数调用”或“工具使用”。工具是由用户提供的函数,模型可以在其响应过程中选择调用这些工具。例如,模型可以访问一个计算器工具来执行算术运算,而无需在内部自行完成计算。

本指南将演示如何定义工具、如何将工具传递给聊天模型,以及当模型调用工具时如何处理其输出。

传递工具

当模型支持工具调用时,请将函数传入 apply_chat_template() 的 tools 参数。 工具以 JSON schema 或 Python 函数的形式传入。如果传入 Python 函数, 系统会解析参数、参数类型和函数 docstring,以自动生成 JSON schema。

虽然传入 Python 函数非常方便,但解析器只能处理 Google 风格的文档字符串。有关如何格式化可直接用于工具的函数,请参阅以下示例。

def get_current_temperature(location: str, unit: str):
    """
    Get the current temperature at a location.

    Args:
        location: The location to get the temperature for, in the format "City, Country"
        unit: The unit to return the temperature in. (choices: ["celsius", "fahrenheit"])
    """
    return 22.  # A real function should probably actually get the temperature!

def get_current_wind_speed(location: str):
    """
    Get the current wind speed in km/h at a given location.

    Args:
        location: The location to get the wind speed for, in the format "City, Country"
    """
    return 6.  # A real function should probably actually get the wind speed!

tools = [get_current_temperature, get_current_wind_speed]

您可以选择在文档字符串中添加 Returns: 块,并在函数头部添加返回类型,但大多数模型不会使用这些信息。解析器还会忽略函数内部的实际代码!

真正重要的是函数名、参数名、参数类型,以及描述函数用途及其参数用途的文档字符串。这些内容构成了模型在决定是否调用该工具时所使用的“签名”。

工具调用示例

加载一个支持工具调用的模型和分词器,例如 NousResearch/Hermes-2-Pro-Llama-3-8B,如果您的硬件支持,也可以考虑更大的模型,如 Command-R 和 Mixtral-8x22B。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

checkpoint = "NousResearch/Hermes-2-Pro-Llama-3-8B"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForCausalLM.from_pretrained(checkpoint, dtype="auto", device_map="auto")

创建聊天历史记录。

messages = [
  {"role": "system", "content": "You are a bot that responds to weather queries. You should reply with the unit used in the queried location."},
  {"role": "user", "content": "Hey, what's the temperature in Paris right now?"}
]

接下来,将 messages 和工具列表传入 apply_chat_template()。对聊天进行分词并生成回复。

inputs = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt")
outputs = model.generate(**inputs.to(model.device), max_new_tokens=128)
print(tokenizer.decode(outputs[0][len(inputs["input_ids"][0]):]))
<tool_call>
{"arguments": {"location": "Paris, France", "unit": "celsius"}, "name": "get_current_temperature"}
</tool_call><|im_end|>

该聊天模型调用了 get_current_temperature 工具,并使用了 docstring 中指定的正确参数。它根据 Paris 推断出位置为法国,并推断出温度单位应使用摄氏度。

模型实际上无法自行调用工具。它只是请求一次工具调用,而由你负责处理该调用,并将调用及其结果追加到聊天历史中。对于支持响应解析的模型,响应解析会自动完成,你只需使用parse_response()来提取工具调用即可。对于其他模型,你需要手动将输出字符串转换为工具调用字典。

无论采用哪种方法,工具调用都应放在 assistant 消息的 tool_calls 键中。这是推荐的 API,大多数支持工具调用的模型的聊天模板都应支持此方式。

尽管 tool_calls 与 OpenAI API 类似,但 OpenAI API 使用 JSON 字符串作为其 tool_calls 的格式。如果在 Transformers 中使用该格式,可能会引发错误或导致模型出现异常行为,因为 Transformers 期望接收一个 dict。

tool_call = {"name": "get_current_temperature", "arguments": {"location": "Paris, France", "unit": "celsius"}}
messages.append({"role": "assistant", "tool_calls": [{"type": "function", "function": tool_call}]})

将工具响应以 tool 角色追加到聊天历史中。

messages.append({"role": "tool", "content": "22"})  # Note that the returned content is always a string!

最后,让模型读取工具响应并回复用户。

inputs = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt")
out = model.generate(**inputs.to(model.device), max_new_tokens=128)
print(tokenizer.decode(out[0][len(inputs["input_ids"][0]):]))
The temperature in Paris, France right now is 22°C.<|im_end|>

尽管助手消息中的键名为 tool_calls,但在大多数情况下,模型一次只发出一个工具调用。一些较旧的模型会同时发出多个工具调用,但这是一个复杂得多的过程,因为你需要同时处理多个工具响应并加以区分,通常需要使用工具调用 ID。请参阅模型卡片以了解模型对工具调用的具体格式要求。

JSON 模式

定义工具的另一种方式是传入 JSON schema。

您也可以手动调用将 Python 函数转换为 JSON schema 的底层函数,然后检查或编辑生成的 schema。通常无需这样做,但了解其底层机制会很有帮助。对于需要访问 JSON schema 以渲染工具定义的聊天模板作者而言,这一点尤为重要。

apply_chat_template() 方法使用 get_json_schema 函数将 Python 可调用对象转换为 JSON schema。这包括方法:self 和 cls 被视为隐式接收者参数,会被忽略。

from transformers.utils import get_json_schema

def multiply(a: float, b: float):
    """
    A function that multiplies two numbers

    Args:
        a: The first number to multiply
        b: The second number to multiply
    """
    return a * b

schema = get_json_schema(multiply)
print(schema)
{
  "type": "function",
  "function": {
    "name": "multiply",
    "description": "A function that multiplies two numbers",
    "parameters": {
      "type": "object",
      "properties": {
        "a": {
          "type": "number",
          "description": "The first number to multiply"
        },
        "b": {
          "type": "number",
          "description": "The second number to multiply"
        }
      },
      "required": ["a", "b"]
    }
  }
}

我们在此不会深入介绍 JSON schema 本身的细节,因为它在别处已有非常详尽的文档。不过,我们会提到,您可以将 JSON schema 字典传递给 apply_chat_template() 的 tools 参数,而无需使用 Python 函数:

# A simple function that takes no arguments
current_time = {
  "type": "function",
  "function": {
    "name": "current_time",
    "description": "Get the current local time as a string.",
    "parameters": {
      'type': 'object',
      'properties': {}
    }
  }
}

# A more complete function that takes two numerical arguments
multiply = {
  'type': 'function',
  'function': {
    'name': 'multiply',
    'description': 'A function that multiplies two numbers',
    'parameters': {
      'type': 'object',
      'properties': {
        'a': {
          'type': 'number',
          'description': 'The first number to multiply'
        },
        'b': {
          'type': 'number', 'description': 'The second number to multiply'
        }
      },
      'required': ['a', 'b']
    }
  }
}

model_input = tokenizer.apply_chat_template(
    messages,
    tools = [current_time, multiply]
)
Update on GitHub