Agents 课程文档

什么是工具?

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

什么是工具?

Unit 1 planning

AI Agent 的一个关键方面是它们采取行动的能力。正如我们所见,这通过使用工具来实现。

在本节中,我们将学习什么是工具,如何有效地设计工具,以及如何通过系统消息将它们集成到您的 Agent 中。

通过为您的 Agent 提供正确的工具——并清楚地描述这些工具的工作原理——您可以极大地提高您的 AI 可以完成的任务。让我们深入了解一下!

什么是 AI 工具?

工具是提供给 LLM 的函数。此函数应实现一个明确的目标

以下是 AI Agent 中一些常用的工具

工具 描述
网络搜索 允许 Agent 从互联网获取最新的信息。
图像生成 根据文本描述创建图像。
检索 从外部来源检索信息。
API 接口 与外部 API 交互(GitHub、YouTube、Spotify 等)。

这些只是一些示例,实际上您可以为任何用例创建工具!

一个好的工具应该是对 LLM 的能力进行补充的东西。

例如,如果您需要执行算术运算,为您的 LLM 提供一个计算器工具将比依赖模型的原生能力提供更好的结果。

此外,LLM 根据其训练数据预测提示的完成情况,这意味着它们的内部知识仅包括训练之前的事件。因此,如果您的 Agent 需要最新的数据,您必须通过一些工具来提供。

例如,如果您直接询问 LLM(没有搜索工具)今天的天气,LLM 可能会幻觉出随机的天气。

Weather
  • 一个工具应该包含

    • 函数作用的文本描述
    • 一个 Callable(用于执行操作的东西)。
    • 带有类型的参数
    • (可选)带有类型的输出。

工具如何工作?

正如我们所见,LLM 只能接收文本输入并生成文本输出。它们无法自行调用工具。当我们谈论为 Agent 提供工具时,我们指的是教会 LLM 这些工具的存在,并指示它在需要时生成基于文本的调用。

例如,如果我们提供一个工具来检查某个位置的互联网天气,然后询问 LLM 关于巴黎的天气,LLM 将识别出这是一个使用“天气”工具的机会。LLM 不会自己检索天气数据,而是会生成表示工具调用的文本,例如 call weather_tool(‘Paris’)。

然后 Agent 读取此响应,识别出需要工具调用,代表 LLM 执行该工具,并检索实际的天气数据。

工具调用步骤通常不会向用户显示:Agent 将它们作为新消息附加,然后再将更新后的对话传递给 LLM。然后,LLM 处理此附加上下文,并为用户生成自然的响应。从用户的角度来看,这看起来好像 LLM 直接与工具交互,但实际上,是 Agent 在后台处理了整个执行过程。

我们将在以后的课程中更多地讨论此过程。

我们如何给 LLM 提供工具?

完整的答案可能看起来令人难以承受,但我们基本上使用系统提示来向模型提供可用工具的文本描述

System prompt for tools

为了使其工作,我们必须非常精确和准确地说明

  1. 工具的作用
  2. 它期望的确切输入

这就是为什么工具描述通常使用表达性但精确的结构(例如计算机语言或 JSON)来提供的原因。不一定要这样做,任何精确且连贯的格式都可以。

如果这看起来太理论化,让我们通过一个具体的例子来理解它。

我们将实现一个简化的计算器工具,它将只乘以两个整数。这可能是我们的 Python 实现

def calculator(a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b

因此,我们的工具名为 calculator,它将两个整数相乘,并且需要以下输入

  • a (int): 一个整数。
  • b (int): 一个整数。

工具的输出是另一个整数,我们可以这样描述它

  • (int): ab 的乘积。

所有这些细节都很重要。让我们将它们放在一起,用一个文本字符串来描述我们的工具,以便 LLM 理解。

Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int

提醒: 此文本描述是我们希望 LLM 了解的关于工具的信息

当我们将之前的字符串作为输入的一部分传递给 LLM 时,模型将识别它为工具,并将知道它需要传递什么作为输入以及期望从输出中获得什么。

如果我们想提供其他工具,我们必须保持一致并始终使用相同的格式。此过程可能很脆弱,我们可能会意外地忽略一些细节。

有没有更好的方法?

自动格式化工具部分

我们的工具是用 Python 编写的,并且实现已经提供了我们需要的一切

  • 对其作用的描述性名称:calculator
  • 更长的描述,由函数的文档字符串注释提供:Multiply two integers.(乘以两个整数。)
  • 输入及其类型:该函数清楚地期望两个 int
  • 输出的类型。

人们使用编程语言是有原因的:它们具有表达性、简洁性和精确性。

我们可以提供 Python 源代码作为 LLM 工具的规范,但工具的实现方式并不重要。重要的是它的名称、它的作用、它期望的输入和它提供的输出。

我们将利用 Python 的内省功能来利用源代码并自动为我们构建工具描述。我们所需要的只是工具实现使用类型提示、文档字符串和合理的函数名称。我们将编写一些代码来从源代码中提取相关部分。

完成之后,我们只需要使用 Python 装饰器来指示 calculator 函数是一个工具

@tool
def calculator(a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b

print(calculator.to_string())

请注意函数定义之前的 @tool 装饰器。

通过我们接下来将看到的实现,我们将能够通过装饰器提供的 to_string() 函数从源代码中自动检索以下文本

Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int

如您所见,它与我们之前手动编写的内容相同!

通用工具实现

我们创建一个通用的 Tool 类,以便在需要使用工具时可以重复使用。

免责声明: 此示例实现是虚构的,但与大多数库中的真实实现非常相似。

class Tool:
    """
    A class representing a reusable piece of code (Tool).

    Attributes:
        name (str): Name of the tool.
        description (str): A textual description of what the tool does.
        func (callable): The function this tool wraps.
        arguments (list): A list of argument.
        outputs (str or list): The return type(s) of the wrapped function.
    """
    def __init__(self,
                 name: str,
                 description: str,
                 func: callable,
                 arguments: list,
                 outputs: str):
        self.name = name
        self.description = description
        self.func = func
        self.arguments = arguments
        self.outputs = outputs

    def to_string(self) -> str:
        """
        Return a string representation of the tool,
        including its name, description, arguments, and outputs.
        """
        args_str = ", ".join([
            f"{arg_name}: {arg_type}" for arg_name, arg_type in self.arguments
        ])

        return (
            f"Tool Name: {self.name},"
            f" Description: {self.description},"
            f" Arguments: {args_str},"
            f" Outputs: {self.outputs}"
        )

    def __call__(self, *args, **kwargs):
        """
        Invoke the underlying function (callable) with provided arguments.
        """
        return self.func(*args, **kwargs)

它可能看起来很复杂,但如果我们慢慢地浏览它,我们可以看到它的作用。我们定义一个 Tool 类,其中包括

  • name (str): 工具的名称。
  • description (str): 工具作用的简要描述。
  • function (callable): 工具执行的函数。
  • arguments (list): 预期的输入参数。
  • outputs (strlist): 工具的预期输出。
  • __call__(): 在调用工具实例时调用该函数。
  • to_string(): 将工具的属性转换为文本表示形式。

我们可以使用如下代码使用此类创建工具

calculator_tool = Tool(
    "calculator",                   # name
    "Multiply two integers.",       # description
    calculator,                     # function to call
    [("a", "int"), ("b", "int")],   # inputs (names and types)
    "int",                          # output
)

但我们也可以使用 Python 的 inspect 模块来检索所有信息!这就是 @tool 装饰器的作用。

如果您有兴趣,可以公开以下部分以查看装饰器实现。

装饰器代码
def tool(func):
    """
    A decorator that creates a Tool instance from the given function.
    """
    # Get the function signature
    signature = inspect.signature(func)

    # Extract (param_name, param_annotation) pairs for inputs
    arguments = []
    for param in signature.parameters.values():
        annotation_name = (
            param.annotation.__name__
            if hasattr(param.annotation, '__name__')
            else str(param.annotation)
        )
        arguments.append((param.name, annotation_name))

    # Determine the return annotation
    return_annotation = signature.return_annotation
    if return_annotation is inspect._empty:
        outputs = "No return annotation"
    else:
        outputs = (
            return_annotation.__name__
            if hasattr(return_annotation, '__name__')
            else str(return_annotation)
        )

    # Use the function's docstring as the description (default if None)
    description = func.__doc__ or "No description provided."

    # The function name becomes the Tool name
    name = func.__name__

    # Return a new Tool instance
    return Tool(
        name=name,
        description=description,
        func=func,
        arguments=arguments,
        outputs=outputs
    )

再次重申,有了这个装饰器,我们可以像这样实现我们的工具

@tool
def calculator(a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b

print(calculator.to_string())

我们可以使用 Toolto_string 方法自动检索适合用作 LLM 工具描述的文本

Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int

该描述注入到系统提示中。以我们开始本节的示例为例,以下是替换 tools_description 后的外观

System prompt for tools

行动部分中,我们将了解有关 Agent 如何调用我们刚刚创建的工具的更多信息。

模型上下文协议 (MCP):统一的工具接口

模型上下文协议 (MCP) 是一种开放协议,它标准化了应用程序向 LLM 提供工具的方式。MCP 提供

  • 不断增长的预构建集成列表,您的 LLM 可以直接插入其中
  • 在 LLM 提供商和供应商之间切换的灵活性
  • 在您的基础设施中保护数据的最佳实践

这意味着任何实现 MCP 的框架都可以利用协议中定义的工具,从而无需为每个框架重新实现相同的工具接口。


工具在增强 AI Agent 的能力方面起着至关重要的作用。

总结一下,我们学习了

  • 什么是工具:为 LLM 提供额外功能的函数,例如执行计算或访问外部数据。

  • 如何定义工具:通过提供清晰的文本描述、输入、输出和可调用函数。

  • 为什么工具至关重要:它们使 Agent 能够克服静态模型训练的局限性,处理实时任务并执行专门的操作。

现在,我们可以继续学习Agent 工作流程,您将在其中看到 Agent 如何观察、思考和行动。这将我们到目前为止涵盖的所有内容结合在一起,并为创建您自己的全功能 AI Agent 奠定基础。

但首先,是时候进行另一个简短的测验了!

< > 在 GitHub 上更新