智慧體課程文件

什麼是工具?

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
  • 一個工具應該包含:

    • 函式功能的文字描述
    • 一個可呼叫物件(用於執行操作)。
    • 帶有型別標註的引數
    • (可選)帶有型別標註的輸出。

工具如何工作?

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):`a` 和 `b` 的乘積。

所有這些細節都很重要。讓我們將它們組合成一個文字字串,用於描述我們的工具,以便 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` 類,以便在需要使用工具時進行復用。

免責宣告:此示例實現是虛構的,但與大多數庫中的真實實現非常相似。

from typing import Callable


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 arguments.
        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` 裝飾器所做的。

如果您感興趣,可以展開以下部分檢視裝飾器實現。

裝飾器程式碼
import inspect

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())

我們還可以使用 `Tool` 的 `to_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 的框架都可以利用協議中定義的工具,從而無需為每個框架重新實現相同的工具介面。

如果您想深入瞭解 MCP,可以檢視我們的免費 MCP 課程


工具在增強 AI Agent 能力方面發揮著至關重要的作用。

總而言之,我們學習了:

  • 什麼是工具:賦予 LLM 額外能力的函式,例如執行計算或訪問外部資料。

  • 如何定義工具:透過提供清晰的文字描述、輸入、輸出和可呼叫函式。

  • 為什麼工具必不可少:它們使 Agent 能夠克服靜態模型訓練的侷限性,處理即時任務,並執行專業操作。

現在,我們可以繼續學習Agent 工作流,在那裡您將看到 Agent 如何觀察、思考和行動。這將我們迄今為止所涵蓋的一切結合在一起,併為建立您自己的功能齊全的 AI Agent 奠定了基礎。

但首先,是時候進行另一個簡短的測驗了!

< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.