> For the complete documentation index, see [llms.txt](https://docs.neuron-ai.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.neuron-ai.dev/neuron-v3-zh/agent/tools.md).

# 工具与工具包

核心智能体循环包括调用模型，让它选择要执行的工具，然后在不再需要工具来提供响应时结束：

<figure><img src="/files/7a7affacc284a4ff0ecca4bce5e26e98faf715d7" alt=""><figcaption></figcaption></figure>

### 什么是工具

工具通过促进与您的应用服务或外部 API 交互，使智能体不止于生成文本。

可以把工具想象成智能体在需要执行特定任务时可以使用的特殊函数。它们让你能够通过赋予智能体访问代码中可调用的特定函数来扩展其能力。

{% embed url="<https://www.youtube.com/watch?v=lI8xE-uIek8>" %}

在 [YouTubeAgent](/neuron-v3-zh/agent/agent.md) 示例中，我们可以定义一个工具，让智能体能够检索 YouTube 视频转录内容，从而生成简短摘要：

```php
namespace App\Neuron;

use NeuronAI\Agent\Agent;
use NeuronAI\Agent\SystemPrompt;
use NeuronAI\Providers\AIProviderInterface;
use NeuronAI\Providers\Anthropic\Anthropic;
use NeuronAI\Tools\PropertyType;
use NeuronAI\Tools\Tool;
use NeuronAI\Tools\ToolProperty;

class YouTubeAgent extends Agent
{
    protected function provider(): AIProviderInterface
    {
        // 返回一个 AI 提供者实例（Anthropic、OpenAI、Ollama、Gemini 等）
        return new Anthropic(
            key: 'ANTHROPIC_API_KEY',
            model: 'ANTHROPIC_MODEL',
        );
    }
    
    protected function instructions(): string 
    {
        return (string) new SystemPrompt(
            background: ["你是一个专门撰写 YouTube 视频摘要的 AI 智能体。"],
            steps: [
                "获取一个 YouTube 视频的 URL，或者让用户提供一个。",
                "使用你可用的工具来检索视频的转录内容。",
                "撰写摘要。",
            ],
            output: [
                "用一段话写出摘要，不要使用列表。只使用流畅的文本。",
                "在摘要之后，添加一个由三句话组成的列表，作为视频最重要的三个要点。",
            ]
        );
    }
    
    protected function tools(): array
    {
        return [
            Tool::make(
                'get_transcription',
                'Retrieve the transcription of a youtube video.',
            )->addProperty(
                new ToolProperty(
                    name: 'video_url',
                    type: PropertyType::STRING,
                    description: 'YouTube 视频的 URL。',
                    required: true
                )
            ->setCallable(function (string $video_url) {
                return "视频转录内容...";
            })
        ];
    }
}

```

让我们拆解一下这段代码。

我们引入了新的方法 `tools()` 到 Agent 类中。该方法应返回一个 Tool 对象数组，AI 在需要时可以使用这些对象。

在这个示例中，我们只返回一个工具，名为 `get_transcription`.

注意 `ToolProperty` 的定义应与作为可调用函数使用的签名相匹配。该可调用函数接收 `$video_url` 参数，并且属性名称正好是 "video\_url"。

最重要的是你赋予工具及其属性的名称和描述。这些信息都会以自然语言传递给 LLM。你越明确、越清晰，LLM 就越有可能理解何时、如果以及为什么应该使用这个工具。

一旦智能体决定使用某个工具，可调用函数就会被执行。在这里，我们可以实现检索视频转录内容的逻辑，并将信息返回给 LLM。

Neuron 为你提供了这些清晰简洁的 API，并自动处理与 LLM 的所有底层交互。一旦你理解了这一点，它会立即打开一种可能性：把几乎任何你想要连接的东西接入智能体。能够执行本地函数意味着你可以调用任何外部 API 或应用组件。

### 自定义工具

得益于 Neuron 的模块化架构，工具是实现 `ToolInterface` 的组件。你可以自由创建预打包的工具类，让智能体能够执行特定操作，并将它们作为外部 composer 包发布，或者向我们的仓库提交 PR，以便将它们集成到核心框架中。

要创建一个新工具，请执行下面的控制台命令：

{% tabs %}
{% tab title="Unix" %}

```bash
vendor/bin/neuron make:tool App\\Neuron\\GetTranscriptionTool
```

{% endtab %}

{% tab title="Windows" %}

```powershell
.\vendor\bin\neuron make:tool App\Neuron\GetTranscriptionTool
```

{% endtab %}
{% endtabs %}

你可以使用下面的代码自定义这个工具的脚手架：

```php
<?php

namespace App\Neuron\Tools;

use GuzzleHttp\Client;
use NeuronAI\Tools\PropertyType;
use NeuronAI\Tools\Tool;
use NeuronAI\Tools\ToolProperty;

class GetTranscriptionTool extends Tool
{
    protected Client $client;
    
    public function __construct(protected string $key)
    {
        // 定义工具名称和描述
        parent::__construct(
            'get_transcription',
            'Retrieve the transcription of a youtube video.',
        );
    }
    
    /**
     * 返回属性列表。
     */
    protected function properties(): array
    {
        return [
            new ToolProperty(
                name: 'video_url',
                type: PropertyType::STRING,
                description: 'YouTube 视频的 URL。',
                required: true
            )
        ];
    }
    
    /**
     * 实现工具逻辑
     */
    public function __invoke(string $video_url): string
    {
        $response = $this->getClient()
            ->get('transcript?url=' . $video_url.'&text=true')
            ->getBody()
            ->getContents();

        $response = json_decode($response, true);

        return $response['content'];
    }
    
    protected function getClient(): Client
    {
        return $this->client ??= new Client([
            'base_uri' => 'https://api.supadata.ai/v1/youtube/',
            'headers' => [
                'x-api-key' => $this->key,
            ]
        ]);
    }
}
```

**工具名称和描述**：在工具构造函数中定义工具的名称和描述。投入提示工程，以帮助模型做出更好的决策。

**properties 方法**：实现此方法以返回工具期望的属性列表。

**该 `__invoke` 方法**：这里你需要实现工具的逻辑，并返回一个结果，再将其返回给模型。PHP `__invoke` 魔术方法默认会被使用。

注意 `__invoke()` 方法接收与 `ToolProperty` 定义的相同参数。在这个示例中，我使用一个名为 [Supadata.ai](https://supadata.ai/).

你可以像平常一样在智能体类中附加该工具：

```php
<?php

namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Providers\AIProviderInterface;
use App\Neuron\Tools\GetTranscriptionTool;

class YouTubeAgent extends Agent
{
    protected function provider(): AIProviderInterface
    {...}
    
    protected function instructions(): string
    {...}
    
    protected function tools(): array
    {
        return [
            GetTranscriptionTool::make('API_KEY'),
        ];
    }
}
```

GetTranscriptions 只是一个示例。你最终也可以实现其他工具，让智能体能够检索其他视频元数据，从而增强其视频分析能力。

最后，你可以和智能体对话，请它总结一个 YouTube 视频。

```php
use NeuronAI\Chat\Messages\UserMessage;

$message = YouTubeAgent::make($user)->chat(
    new UserMessage('这个视频怎么样：https://www.youtube.com/watch?v=WmVLcj-XKnM')
 )->getMessage();
    
echo $message->getContent();

/**

基于转录内容，我将提供这条强有力的环境 
来自“Mother Nature”的信息：
这个视频展示了……

三个最重要的要点：

1. 自然已经存在……

2. 人类的福祉……

3. 人类选择如何对待自然，将决定……

*/
```

### 最大运行次数

智能体具有一种安全机制，会跟踪在一次执行会话中工具被调用的次数。如果智能体超过此限制，执行将中断，并抛出 `ToolRunsExceededException` 。默认限制是 10 次调用，并且会分别对每个工具计数。

你可以使用 `toolMaxRuns()` 方法在智能体级别自定义该值，或者在 `setMaxRuns()` 上在工具级别进行设置。 **在单个工具上设置最大尝试次数会优先于全局设置**.

```php
try {

    $response = YouTubeAgent::make()
        ->toolMaxRuns(5) // 每个工具的最大调用次数
        ->addTool(
            // 工具级配置优先于全局设置
            CustomTool::make()->setMaxRuns(2)
        )
        ->chat(...)
        ->getMessage();
        
} catch (ToolMaxTriesException $exception) {
    // 执行某些操作
}
```

### 可见性

你可以基于自定义规则来限制工具的可用性。Tool 类为你提供了 `visible` 方法，用于判断智能体是否应该知道这个工具的存在：

```php
class YouTubeAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            GetTranscriptionTool::make('API_KEY')->visible(
                auth()->user()->can(...)
            ),
        ];
    }
}
```

如果 `visible` 方法返回 `false`，那么该工具在智能体执行期间将不可用。

### 工具审批

Neuron 为你提供了对人类在回路模式的完整支持，包括工具审批。它与可见性不同，因为“审批”是运行时的守门机制。框架会拦截工具调用并暂停，等待用户的最终决定。

你可以通过我们内置的 [ToolApproval](#tool-properties) 中间件将此功能接入你的智能体。

```php
new ToolApproval(
    tools: [
        BuyTicketTool::class => function (array $args): bool {
            return $args['amount'] > 100;
        }
    ]
)
```

{% content-ref url="/pages/9ea2066a9f786ede6683c1553ff65e839e3105e5" %}
[中间件](/neuron-v3-zh/agent/middleware.md)
{% endcontent-ref %}

### 动态工具搜索

默认情况下，每次调用提供者时，所有工具都会被加载并传输到后端 LLM。一个连接了电子邮件、日历、云盘、CRM 以及多个 MCP 服务器的复杂生产智能体，工具数量很容易达到数百个，每个工具都带有自己的名称、描述、参数模式和使用提示。

工具搜索将工具目录重新定义为智能体按需查询的对象，而不是每次请求都要随身携带的东西。

你可以使用全局中间件 `ToolSearchMiddleware` 来为你的智能体启用动态工具选择：

```php
new ToolSearchMiddleware([
    MyCustomTool::make(),
    ...CalculatorToolkit::make()->tools()
    ...MCPConnector::make([...])->tools()
])
```

{% content-ref url="/pages/9ea2066a9f786ede6683c1553ff65e839e3105e5" %}
[中间件](/neuron-v3-zh/agent/middleware.md)
{% endcontent-ref %}

### 监控与调试

Neuron 会根据 LLM 决定调用的内容，自动为你管理工具循环。

要在这个工作流中进行观察，你应该将你的智能体连接到 [Inspector 监控仪表板](https://inspector.dev/) ，以便实时查看工具调用执行流程。

{% embed url="<https://docs.inspector.dev/guides/neuron-ai>" %}

<figure><img src="/files/a0e757651565850e4051b31227594d853aff4f3c" alt=""><figcaption></figcaption></figure>

在下图中，你可以看到检索视频转录内容的工具执行的所有细节：

<figure><img src="/files/36b94af1bca6f01513de7ba71446282fd6a99bec" alt=""><figcaption></figcaption></figure>

## 工具属性

Neuron 允许你定义希望传入工具函数的数据格式。你可以将这些对象相互嵌套，以定义复杂的数据结构。

### ToolProperty

这个类表示一个简单的标量值，例如 string、int 或 boolean。

```php
namespace App\Neuron\Tools;

use NeuronAI\Tools\PropertyType;
use NeuronAI\Tools\Tool;
use NeuronAI\Tools\ToolProperty;

class MyTool extends Tool
{
    public function __construct(){...}
	
    protected function properties(): array
    {
        return [
            new ToolProperty(
                name: 'arg',
                type: PropertyType::STRING,
                description: '描述你期望的值',
                required: true,
                nullable: false
            )
        ];
    }
    
    public function __invoke(string $arg){...}
}
```

### ArrayProperty

该 `ArrayProperty` 允许你要求一个具有特定特征的项目列表。

使用参数 `items` 来指定数组元素的数据类型。在下面的示例中，我们要求一个字符串数组。

```php
namespace App\Neuron\Tools;

use NeuronAI\Tools\PropertyType;
use NeuronAI\Tools\Tool;
use NeuronAI\Tools\ArrayProperty;
use NeuronAI\Tools\ToolProperty;

class MyTool extends Tool
{
    public function __construct(){...}
	
    protected function properties(): array
    {
        return [
            new ArrayProperty(
                name: 'prop_array',
                description: '描述你期望的值',
                required: true,
                items: new ToolProperty(
                    name: 'prop',
                    type: PropertyType::STRING,
                    description: '描述你期望的值',
                    required: true
                )
            )
        ];
    }
    
    public function __invoke(string $arg){...}
}
```

#### 最大和最小限制

ArrayProperty 还允许你使用 `minItems` 和 `maxItems` 参数来定义期望数组大小的限制。

```php
$property = new ArrayProperty(
    name: "tags",
    description: "与该项目相关的标签列表",
    required: true,
    items: new ToolProperty(
        name: "tag",
        type: PropertyType::STRING,
        description: "单个标签",
        required: true
    ),
    minItems: 1,
    maxItems: 10
);
```

### ObjectProperty

与上面的数组示例类似，你可以定义一个对象数据结构：

```php
namespace App\Neuron\Tools;

use NeuronAI\Tools\PropertyType;
use NeuronAI\Tools\Tool;
use NeuronAI\Tools\ObjectProperty;
use NeuronAI\Tools\ToolProperty;

class MyTool extends Tool
{
    public function __construct(){...}
	
    protected function properties(): array
    {
        return [
            new ObjectProperty(
                name: 'colors',
                description: 'RGB 颜色',
                required: true,
                properties: [
                    new ToolProperty(
                        name: 'r',
                        type: PropertyType::NUMBER,
                        description: 'RGB 的红色部分',
                        required: true
                    ),
                    new ToolProperty(
                        name: 'g',
                        type: PropertyType::NUMBER,
                        description: 'RGB 的绿色部分',
                        required: true
                    ),
                    new ToolProperty(
                        name: 'b',
                        type: PropertyType::NUMBER,
                        description: 'RGB 的蓝色部分',
                        required: true
                    )
                ]
            )
        ];
    }
    
    public function __invoke(string $arg){...}
}
```

### 结构化工具输入

如果你想要的对象有很多属性，你可以将一个结构化的 PHP 类传给 `ObjectProperty` ，而不是手动定义 schema。Neuron 会为你提供该类的一个实例，作为工具函数的输入参数：

```php
namespace App\Neuron\Tools;

use App\Neuron\Dto\Color;
use NeuronAI\Tools\PropertyType;
use NeuronAI\Tools\Tool;
use NeuronAI\Tools\ToolProperty;

class MyTool extends Tool
{
    public function __construct(){...}
	
    protected function properties(): array
    {
        return [
            new ObjectProperty(
                name: 'color',
                description: '颜色组合',
                required: true,
                class: Color::class
            )
        ];
    }
    
    public function __invoke(Color $color){...}
}
```

Colors 类如下：

```php
<?php

namespace App\Neuron\Dto;

use NeuronAI\StructuredOutput\SchemaProperty;

class Color
{
    #[SchemaProperty(description: "RGB 的红色部分", required: true)]
    public float $r;
    
    #[SchemaProperty(description: "RGB 的绿色部分", required: true)]
    public float $g;
    
    #[SchemaProperty(description: "RGB 的蓝色部分", required: true)]
    public float $b;
}
```

## 提供者工具

有些提供者支持直接使用其内置工具，如 web\_search、file\_search 等，而不是依赖外部服务。即便它们提供这些服务，使用这些工具仍然会带来很多限制。为你的智能体添加能力时，最灵活且可靠的方式仍然是 Tools 和 Toolkit 系统。

你可以像平常一样在智能体的 tools 数组中添加一个提供者工具：

```php
use NeuronAI\Tools\ProviderTool;

class MyAgent extends Agent
{
    protected function provider(): AIProviderInterface
    {
        return new OpenAIResponses(
            key: 'OPENAI_API_KEY',
            model: 'OPENAI_MODEL',
        );
    }

    protected function tools(): array
    {
        return [
            ProviderTool:make(
                type: 'web_search'
             )->setOptions([...]),
        ];
    }
}
```

目前只有 [OpenAIResponses](/neuron-v3-zh/ti-gong-fang/ai-provider.md#openairesponses), [Gemini](/neuron-v3-zh/ti-gong-fang/ai-provider.md#gemini)，以及 [Anthropic](/neuron-v3-zh/ti-gong-fang/ai-provider.md#anthropic) 支持这些工具。

## 工具包

Neuron 的工具包系统背后的理念，源于 AI 智能体开发中的一个基本观察：单个工具虽然提供了特定能力，但现实世界中的 AI 智能体通常需要一组相互协调的相关功能。

与其强迫开发者为常见用例手动组装工具集合，Neuron 不如引入工具包作为一个抽象层，来改变我们对智能体能力组合方式的思考。下面是如何向智能体添加工具包的示例：

```php
<?php

namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Calculator\CalculatorToolkit;

class MyAgent extends Agent
{
    ...
	
    protected function tools(): array
    {
        return [
            CalculatorToolkit::make(),
        ];
    }
}
```

传统方式需要逐个实例化每个工具。设想你要构建需要数学推理的智能体——加法、减法、乘法、除法和幂运算工具都必须在智能体的工具配置中分别声明。当智能体需要完整功能集合时，这种细粒度方式很快就会变得难以管理。

工具包是 Neuron 对这种复杂性的解决方案：它将围绕相同范围创建的工具打包成一个统一、连贯的接口，只需一行代码即可附加到任何智能体上。

下面是 `CalculatorToolkit`:

```php
namespace NeuronAI\Tools\Toolkits\Calculator;

use NeuronAI\Tools\Toolkits\AbstractToolkit;

class CalculatorToolkit extends AbstractToolkit
{
    public function guidelines(): ?string
    {
        return "此工具包允许你执行数学运算。你也可以使用这些函数，通过逐步执行更小的操作来求解
        数学表达式，从而计算最终结果。";
    }

    public function provide(): array
    {
        return [
            SumTool::make(),
            SubtractTool::make(),
            MultiplyTool::make(),
            DivideTool::make(),
            ExponentiateTool::make(),
        ];
    }
}
```

该 `AbstractToolkit` 基类建立了一个一致的接口，所有工具包都会继承它，从而确保整个框架中具有可预测的行为。

**指南**

该 `guidelines()` 方法在智能体开发中承担着一个特别重要的作用——它提供上下文信息，帮助底层语言模型不仅理解有哪些工具可用，还理解它们应如何协同使用。对于 `CalculatorToolkit`而言，这些指南明确建议复杂的数学表达式可以通过逐步操作来求解，引导智能体采用有效的问题解决策略。

**提供**

该 `provide()` 方法默认返回工具包中包含的工具数组。当一个工具包附加到智能体时，这些单独工具会像分别添加一样可用，但无需管理多个工具声明所带来的认知负担。

### 过滤器

在复杂智能体的开发过程中，我经常遇到这样的场景：工具包大体上提供了正确的功能，但其中包含一些在特定上下文中可能导致不期望行为的工具，或者只是需要单独限制和配置。

#### 排除

该 `exclude()` 方法优雅地解决了这个问题：它允许开发者附加功能完整的工具包，同时对可用能力保持细粒度控制。当你处理需要特定能力但又希望降低智能体出错概率并减少令牌消耗的专用智能体时，这一点尤其有用。

```php
class MyAgent extends Agent
{
    ...
	
    protected function tools(): array
    {
    	return [
            CalculatorToolkit::make()->exclude([
                DivideTool::class,
                ExponentiateTool::class,
                MultiplyTool::class,
            ]),
        ];
    }
}
```

排除机制在类级别运行，使用完全限定类名来识别要移除的工具。

#### 仅

同样地，你也可以使用 `only()` 方法来请求工具包中可用工具的一个子集。

```php
class MyAgent extends Agent
{
    ...
	
    protected function tools(): array
    {
    	return [
            CalculatorToolkit::make()->only([
                StandardDeviationTool::class,
                MedianTool::class,
            ]),
        ];
    }
}
```

#### 借助

沿用相同的模式，你可能需要从工具包中获取某个特定工具的实例来更改其设置。你可以使用 `with()` 方法。你可以传入完全限定类名来声明你想检索的工具，工具实例会被注入到回调中，这样你就可以修改它的设置并将其返回。

```php
class MyAgent extends Agent
{
    ...
	
    protected function tools(): array
    {
    	return [
            MySQLToolkit::make()
                ->with(
                    MySQLSchemaTool::class, 
                    fn (ToolInterface $tool) => $tool->setMaxTries(1)
                ),
        ];
    }
}
```

从可扩展性的角度来看，工具包系统为社区贡献和生态增长打开了非凡的机会。统一的接口意味着第三方开发者可以创建与领域相关的工具包，并与 Neuron 的架构无缝集成。为金融应用构建智能体的开发者可能会创建一个 FinancialToolkit，其中包含货币转换、利息计算和风险评估等工具。同样，WebScrapingToolkit 也可以将 HTTP 请求工具、HTML 解析能力和数据提取实用工具打包成一个可复用的组件。

## 可用的工具包

Neuron 自带了多个内置工具和工具包，可让你快速为智能体赋予多种能力。你可以单独使用这些工具，也可以通过一行代码附加整个工具包。

### 计算器

CalculatorToolkit 提供了一整套计算工具，旨在帮助你的 AI 智能体执行准确计算。它可以与提供数据访问的互补工具包无缝集成——例如数据库连接器、CSV 处理器、API 客户端或电子表格读取器——使 AI 智能体能够进行复杂的统计计算，并针对复杂的业务查询给出全面的洞察。

```php
<?php

namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Calculator\CalculatorToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            CalculatorToolkit::make(),
        ];
    }
}
```

<table data-header-hidden><thead><tr><th width="253"></th><th></th></tr></thead><tbody><tr><td>求和</td><td>NeuronAI\Tools\Toolkits\Calculator\SumTool</td></tr><tr><td>减法</td><td>NeuronAI\Tools\Toolkits\Calculator\SubtractTool</td></tr><tr><td>乘法</td><td>NeuronAI\Tools\Toolkits\Calculator\MultiplyTool</td></tr><tr><td>除法</td><td>NeuronAI\Tools\Toolkits\Calculator\DivideTool</td></tr><tr><td>指数</td><td>NeuronAI\Tools\Toolkits\Calculator\ExponentialTool</td></tr><tr><td>平方根</td><td>NeuronAI\Tools\Toolkits\Calculator\SquareRootTool</td></tr><tr><td>n 次方根</td><td>NeuronAI\Tools\Toolkits\Calculator\NthRootTool</td></tr><tr><td>平均值</td><td>NeuronAI\Tools\Toolkits\Calculator\MeanTool</td></tr><tr><td>中位数</td><td>NeuronAI\Tools\Toolkits\Calculator\MedianTool</td></tr><tr><td>众数</td><td>NeuronAI\Tools\Toolkits\Calculator\ModeTool</td></tr><tr><td>标准差</td><td>NeuronAI\Tools\Toolkits\Calculator\StandardDeviationTool</td></tr><tr><td>方差</td><td>NeuronAI\Tools\Toolkits\Calculator\VarianceTool</td></tr></tbody></table>

### 日历

这个工具包提供全面的日期和时间操作。使用这些工具让你的智能体能够处理日期、时间、格式化、计算和时区转换。

```php
<?php

namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\CalendarToolkit\CalendarToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            CalendarToolkit::make(),
        ];
    }
}
```

<table data-header-hidden><thead><tr><th width="205"></th><th></th></tr></thead><tbody><tr><td>current_datetime</td><td>NeuronAI\Tools\Toolkits\Calendar\CurrentDateTimeTool</td></tr><tr><td>get_timestamp</td><td>NeuronAI\Tools\Toolkits\Calendar\GetTimestampTool</td></tr><tr><td>format_date</td><td>NeuronAI\Tools\Toolkits\Calendar\FormatDateTool</td></tr><tr><td>date_difference</td><td>NeuronAI\Tools\Toolkits\Calendar\DateDifferenceTool</td></tr><tr><td>add_time</td><td>NeuronAI\Tools\Toolkits\Calendar\AddTimeTool</td></tr><tr><td>subtract_time</td><td>NeuronAI\Tools\Toolkits\Calendar\SubtractTimeTool</td></tr><tr><td>calculate_age</td><td>NeuronAI\Tools\Toolkits\Calendar\CalculateAgeTool</td></tr><tr><td>convert_timezone</td><td>NeuronAI\Tools\Toolkits\Calendar\ConvertTimezoneTool</td></tr><tr><td>get_timezone_info</td><td>NeuronAI\Tools\Toolkits\Calendar\GetTimezoneInfoTool</td></tr><tr><td>get_weekday</td><td>NeuronAI\Tools\Toolkits\Calendar\GetWeekdayTool</td></tr><tr><td>是否为周末</td><td>NeuronAI\Tools\Toolkits\Calendar\IsWeekendTool</td></tr><tr><td>是否为闰年</td><td>NeuronAI\Tools\Toolkits\Calendar\IsLeapYearTool</td></tr><tr><td>获取每月天数</td><td>NeuronAI\Tools\Toolkits\Calendar\GetDaysInMonthTool</td></tr><tr><td>周期开始</td><td>NeuronAI\Tools\Toolkits\Calendar\StartOfPeriodTool</td></tr><tr><td>周期结束</td><td>NeuronAI\Tools\Toolkits\Calendar\EndOfPeriodTool</td></tr><tr><td>获取周数</td><td>NeuronAI\Tools\Toolkits\Calendar\GetWeekNumberTool</td></tr><tr><td>比较日期</td><td>NeuronAI\Tools\Toolkits\Calendar\CompareDatesTool</td></tr><tr><td>日期是否在范围内</td><td>NeuronAI\Tools\Toolkits\Calendar\IsDateInRangeTool</td></tr></tbody></table>

### MySQL 和 PostgreSQL

这些工具包让你的智能体能够与数据库交互。如果你问：“作者在过去 14 天里获得了多少投票？”，智能体不会猜测或幻觉出一个答案。相反，它会识别出这个问题需要数据库访问，找出相关的表，并从你的系统中检索真实数据。

<figure><img src="/files/b129c31d56d3cfa24254e4d9bdfd78916bbbe0cd" alt=""><figcaption></figcaption></figure>

MySQL 和 PostgreSQL 工具包中的所有工具都需要一个 [PDO](https://www.php.net/manual/en/class.pdo.php) 实例作为构造函数参数。如果你处于框架环境中，或者你已经在一般情况下使用 ORM，那么你可以从 ORM 中获取底层 PDO 实例，并将其传递给这些工具。你可以在这篇深入文章中了解更多关于这一实现策略的信息： <https://inspector.dev/mysql-ai-toolkit-bringing-intelligence-to-your-database-layer-in-php/>

PDO 实例本质上是到某个特定数据库的连接，因此你也可以考虑为你的智能体创建专用凭据。这样有助于控制智能体对数据库拥有的访问级别。

无论如何，你都有分别用于数据库读取和写入的工具。如果你对智能体的行为没有把握，也许可以不提供写入工具。

```php
<?php

namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\MySQL\MySQLToolkit;
use NeuronAI\Tools\Toolkits\MySQL\PGSQLToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            // 连接到 MySQL 数据库
            MySQLToolkit::make(
                new \\PDO("mysql:host=localhost;dbname=DB_NAME;charset=utf8mb4", "DB_USER", "DB_PASS"),
            ),
            
            // 或 Postgre 数据库
            PGSQLToolkit::make(
                new \\PDO("pgsql:host=localhost;dbname=DB_NAME;charset=utf8mb4", "DB_USER", "DB_PASS"),
            ),
        ];
    }
}
```

{% hint style="warning" %}
这些示例提到的是 `MySQLToolkit` 但使用 `PGSQLToolkit`.
{% endhint %}

#### MySQLSchemaTool / PGSQLSchemaTool

这个工具让智能体理解你的数据库结构，使其无需将表结构或关系硬编码到提示词中，也能构建智能查询。这个工具本质上让你的智能体具备类似数据库管理员对你的 schema 的理解，从而能够编写尊重数据模型并利用现有索引和关系的查询。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\MySQL\MySQLSchemaTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            MySQLSchemaTool::make(new \\PDO(...)),
            
            // PGSQLSchemaTool::make(new \\PDO(...)),
        ];
    }
}
```

这个工具还接受第二个参数 `$tables`。你基本上可以传入一个表列表，用于包含在传递给 LLM 的 schema 信息中。这本质上是一种限制智能体随后将在数据库上执行的查询范围的方法。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\MySQL\MySQLSchemaTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            MySQLSchemaTool::make(
                new \\PDO(...),
                ['users', 'categories', 'articles', 'tags']
            ),
        ];
    }
}
```

通过限制 schema 的范围，你可以创建专注于应用特定区域的专用智能体。内容管理智能体可能只需要访问 articles、categories 和 tags，而用户管理智能体则需要查看 users、roles 和 permissions 表。这个方法不仅提高性能，还能降低语言模型的认知负担，从而得到更准确、更聚焦的回答。

#### MySQLSelectTool / PGSQLSelectTool

使用此工具可让你的智能体对数据库执行 SELECT 查询。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\MySQL\MySQLSchemaTool;
use NeuronAI\Tools\Toolkits\MySQL\MySQLSelectTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            MySQLSchemaTool::make(new \\PDO(...)),
            MySQLSelectTool::make(new \\PDO(...)),
        ];
    }
}
```

#### MySQLWriteTool / PGSQLWriteTool

使用此工具可让你的智能体对数据库执行写入操作（INSERT、UPDATE、DELETE）。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\MySQL\MySQLSchemaTool;
use NeuronAI\Tools\Toolkits\MySQL\MySQLWriteTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            MySQLSchemaTool::make(new \\PDO(...)),
            MySQLWriteTool::make(new \\PDO(...)),
        ];
    }
}
```

### 文件系统

这个工具包让智能体能够与本地文件系统交互。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\FileSystem\FileSystemToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            FileSystemToolkit::make(),
        ];
    }
}
```

<table data-header-hidden><thead><tr><th width="256"></th><th></th></tr></thead><tbody><tr><td>描述目录内容</td><td>NeuronAI\Tools\Toolkits\FileSystem\DescribeDirectoryContentTool</td></tr><tr><td>读取文件</td><td>NeuronAI\Tools\Toolkits\FileSystem\ReadFileTool</td></tr><tr><td>grep 文件内容</td><td>NeuronAI\Tools\Toolkits\FileSystem\GrepFileContentTool</td></tr><tr><td>glob 路径</td><td>NeuronAI\Tools\Toolkits\FileSystem\GlobPathTool</td></tr><tr><td>预览文件</td><td>NeuronAI\Tools\Toolkits\FileSystem\PreviewFileTool</td></tr><tr><td>解析文件</td><td>NeuronAI\Tools\Toolkits\FileSystem\ParseFileTool</td></tr></tbody></table>

### Tavily

这个工具包让你的智能体能够执行网页搜索、页面内容提取和爬取。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Tavily\TavilyToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            TavilyToolkit::make(
                key: 'TAVILY_API_KEY'
            ),
        ];
    }
}
```

#### Tavily 网络搜索

它让你的智能体能够搜索网页。它需要访问 [Tavily API](https://tavily.com/).

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Tavily\TavilySearchTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            TavilySearchTool::make(
                key: 'TAVILY_API_KEY'
            ),
        ];
    }
}
```

你可以通过在 `withOptions` 方法中传入你的偏好来定制获取搜索结果的默认选项：

```php
TavilySearchTool::make(
    key: 'TAVILY_API_KEY'
)->withOptions([
    'days' => 30,
    'max_results' => 10,
]),
```

#### Tavily 提取

从 URL 提取网页内容。它需要访问 [Tavily API](https://tavily.com/).

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Tavily\TavilyExtractTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            TavilyExtractTool::make(
                key: 'TAVILY_API_KEY'
            ),
        ];
    }
}
```

#### Tavily 爬取

Tavily 爬取是一个基于图的站点遍历工具，可通过内置提取和智能发现并行探索数百条路径。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Tavily\TavilyCrawlTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            TavilyCrawlTool::make(
                key: 'TAVILY_API_KEY'
            ),
        ];
    }
}
```

### Jina

这个工具包让你的智能体能够执行网页搜索，并读取特定 URL 的内容。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Jina\JinaToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            JinaToolkit::make(
                key: 'JINA_API_KEY'
            ),
        ];
    }
}
```

#### Jina 网络搜索

它让你的智能体能够搜索网页。它需要访问 [Jina API](https://jina.ai/).

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Jina\JinaWebSearch;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            JinaWebSearch::make(
                key: 'JINA_API_KEY'
            ),
        ];
    }
}
```

#### Jina URL 阅读器

从 URL 提取网页内容。它需要访问 [Jina API](https://jina.ai/).

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Jina\JinaUrlReader;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            JinaUrlReader::make(
                key: 'JINA_API_KEY'
            ),
        ];
    }
}
```

### Zep 记忆

这个工具包将 NeuronAI 智能体连接到 [Zep](https://www.getzep.com/) 知识图谱。这类系统允许智能体随着时间推移存储在与智能体交互过程中可能出现的相关事实。从长时记忆的角度看，它不像 [ChatHistory](/neuron-v3-zh/agent/chat-history-and-memory.md) 组件那样只局限于当前对话。它是智能体用于存储和检索单条信息的外部持久化存储，这可以提供更个性化的回答。

要了解更多这类系统的能力，你可以访问 Zep 网站： <https://www.getzep.com/>

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Zep\ZepLongTermMemoryToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            ZepLongTermMemoryToolkit::make(
                key: 'ZEP_API_KEY',
                user_id: 'ID'
            ),
        ];
    }
}
```

该 `user_id` 参数允许你在想要服务多个用户时，将长时记忆划分到不同的隔离空间中。根据你的使用场景，你可以将此参数用作“键”，以区分智能体与之交互的不同实体（用户、公司等）的记忆。

### AWS SES

#### 简单邮件服务（SES）

这个工具允许智能体向一个或多个收件人发送电子邮件消息，发送通知、确认、报告或任何其他基于电子邮件的通信。该工具会自动处理正确的邮件投递和基本错误处理。

要使用这个工具，必须安装 PHP 的 AWS SDK。

```
composer require aws/aws-sdk-php
```

该工具获取 `SesClient` 类的一个实例，来自 AWS PHP SDK。

```php
namespace App\Neuron;

use Aws\Ses\SesClient;
use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\AWS\SESTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            SESTool::make(
                sesClient: new SesCleint(...),
                fromEmail: 'my-address@email.com'
            ),
        ];
    }
}
```

### Supadata YouTube

这个工具包通过 Supadata.ai 提供对 YouTube 视频转录、元数据、频道信息和播放列表数据的访问，用于内容分析和研究。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Supadata\SupadataYouTubeToolkit;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            SupadataYouTubeToolkit::make(
                key: 'SUPADATA_API_KEY',
            ),
        ];
    }
}
```

#### 视频转录

允许智能体检索 YouTube 视频的转录内容。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Supadata\SupadataVideoTranscriptTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            SupadataVideoTranscriptTool::make(
                key: 'SUPADATA_API_KEY',
            ),
        ];
    }
}
```

#### 视频元数据

允许智能体检索 YouTube 视频的元数据。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Supadata\SupadataVideoMetadataTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            SupadataVideoMetadataTool::make(
                key: 'SUPADATA_API_KEY',
            ),
        ];
    }
}
```

#### 频道元数据

允许智能体检索 YouTube 频道的元数据，包括名称、描述、订阅者数量等。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Supadata\SupadataYoutubeChannelTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            SupadataYoutubeChannelTool::make(
                key: 'SUPADATA_API_KEY',
            ),
        ];
    }
}
```

#### 播放列表元数据

允许智能体检索 YouTube 播放列表的元数据，包括标题、描述、视频数量等。

```php
namespace App\Neuron;

use NeuronAI\Agent;
use NeuronAI\Tools\Toolkits\Supadata\SupadataYoutubePlaylistTool;

class MyAgent extends Agent
{
    ...
    
    protected function tools(): array
    {
        return [
            SupadataYoutubePlaylistTool::make(
                key: 'SUPADATA_API_KEY',
            ),
        ];
    }
}
```

## 并行工具调用

如果你的智能体非常依赖工具，而模型在一次请求中提出多个工具调用，你可以启用并行执行。

#### 顺序执行（标准）

智能体调用工具 **一次一个**，等待每个工具完成后再开始下一个：

```
1. 调用工具 A → 等待结果
2. 调用工具 B → 等待结果  
3. 调用工具 C → 等待结果

总时间：Time(A) + Time(B) + Time(C)
```

#### 并行执行（使用 `pcntl`)

智能体调用 **多个工具同时执行**，让它们同时运行：

```
1. 一次调用工具 A、B 和 C
2. 等待全部完成

总时间：Max(Time(A), Time(B), Time(C))
```

### 要求

要使用此功能，你需要安装 `spatie/fork` 包。更多信息请查看 GitHub 仓库： <https://github.com/spatie/fork>

```shellscript
composer require spatie/fork
```

{% hint style="warning" %}

### 限制

这个实现需要 `pcntl` 扩展，该扩展在许多 Unix 和 Mac 系统中默认已安装。

**pcntl 仅适用于 CLI 进程，不适用于 Web 环境。**

如果 `pcntl` 如果运行智能体的系统中没有该扩展（例如 Windows 机器），该 trait 会自动回退到标准的工具调用执行。如果你的本地开发环境与生产环境之间存在不一致，这会很有帮助。你可以在本地禁用 `pcntl` ，然后部署到可能启用它的生产环境——**无需修改一行代码**。智能体会根据它所处的执行环境自动适配。
{% endhint %}

### 启用并行执行

在你的 Agent 或 RAG 中设置 `parallelToolCalls(true)` 。框架会在工作流中注入专用节点 `ParallelToolNode` ，而不是标准的 `ToolNode` 。

```php
class DemoAgent extends Agent
{
    public function __construct()
    {
        parent::__construct();
        $this->parallelToolCalls(true);
    }
    
    protected function provider(): AIProviderInterface
    {
        ...
    }

    protected function tools(): array
    {
        return [
            CalculatorToolkit::make(),
        ];
    }
}
```

## 错误处理器

现在的问题是如何处理工具错误。为适应不同场景和需求，这里有几种选择。

该 `ToolNode` 接受一个 `$errorHandler` 参数 [（代码）](https://github.com/neuron-core/neuron-ai/blob/3.x/src/Agent/Nodes/ToolNode.php#L37)。这是一个回调，它接收工具抛出的异常以及失败工具的实例。

它允许你在发生工具错误时实现自定义逻辑（通用工具异常，或 `ToolRunsExceededException`). **如果你返回一个值，它将作为工具结果返回给模型。** 默认情况下，ToolNode 会重新抛出执行错误。

**流式定义：**

```php
$agent = Agent::make()
    ->toolErrorHandler(
        fn(Throwable $e, ToolInterface $tool): string => "错误：{$e->getMessage()}"
    );
```

**扩展 Agent**

你也可以直接实现 `resolveToolErrorHandler()` 来定义要运行的回调。

```php
class MyAgent extends Agent
{
    ...

    protected function resolveToolErrorHandler(): ?callable
    {
        return fn(Throwable $e, ToolInterface $tool): string => "错误：{$e->getMessage()}";
    }
}
```
