> 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/zhi-neng-ti/tools.md).

# 工具与工具包

赋予智能体与您的应用上下文和服务交互的能力。

核心代理循环涉及调用模型，让它选择要执行的工具，然后在不再需要工具来提供响应时结束：

<figure><img src="https://99736354-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FGHx4l2LknIex7vFIUg1R%2Fuploads%2F59ZHXSPR086dp25ry7ou%2Fneuron-tool-call.png?alt=media&amp;token=f1a11d32-9024-4efc-8f44-6c329f2d102a" alt=""><figcaption></figcaption></figure>

### 什么是工具

工具使代理能够超越文本生成，通过促进与您的应用服务或外部 API 的交互来实现更多能力。

可以把工具看作 AI 代理在需要执行特定任务时可使用的特殊函数。它们让你能够通过让代理访问代码中可调用的特定函数来扩展其能力。

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

在 [YouTubeAgent](/neuron-v3-zh/zhi-neng-ti/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 provider 实例（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',
                '检索 YouTube 视频的转录内容。',
            )->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',
            '检索 YouTube 视频的转录内容。',
        );
    }
    
    /**
     * 返回属性列表。
     */
    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` 中定义的相同参数。在这个示例中，我使用一个外部服务来获取 YouTube 视频转录内容，名为 [Supadata.ai](https://supadata.ai/).

你可以像往常一样在 agent 类中附加该工具：

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

/**

根据转录内容，我将提供这段强大的环境 
来自“地球母亲”的信息摘要：
这个视频展示了 ...

三个最重要的要点：

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 为你提供对 human-in-the-loop 模式的完整支持，包括工具审批。它与可见性不同，因为“审批”是运行时的守门人。框架会拦截工具调用并暂停，等待用户的最终决定。

你可以通过我们内置的 [ToolApproval](/neuron-v3-zh/zhi-neng-ti/middleware.md#tool-approval-human-in-the-loop) 中间件将此功能接入你的代理。

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

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

### 工具搜索

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

工具搜索将工具目录重新定义为代理按需查询的内容，而不是每次请求都携带的内容。

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

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

### 监控与调试

要在工具循环内部查看，你可以将你的 Agent 连接到 [Inspector 监控仪表板](https://inspector.dev/) ，以便实时查看工具调用执行流程。

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

## 工具属性

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){...}
}
```

### 数组属性

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

使用参数 `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
);
```

### 对象属性

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

```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 类传递给 `对象属性` ，而不是手动定义 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){...}
}
```

下面是 Color 类的样子：

```php
<?php

namespace App\\Neuron\\Dto;

use NeuronAI\\StructuredOutput\\SchemaProperty;

class Color
{
    #[SchemaProperty(description: "RGB 的 RED 部分", required: true)]
    public float $r;
    
    #[SchemaProperty(description: "RGB 的 GREEN 部分", required: true)]
    public float $g;
    
    #[SchemaProperty(description: "RGB 的 BLUE 部分", 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-shang/ai-provider.md#openairesponses), [Gemini](/neuron-v3-zh/ti-gong-shang/ai-provider.md#gemini)，以及 [Anthropic](/neuron-v3-zh/ti-gong-shang/ai-provider.md#anthropic) 支持这些工具。

## 工具包

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

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

```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()` 方法优雅地解决了这一挑战，使开发者能够附加完整的工具包，同时对可用能力保持精细控制。当处理需要特定能力但你又想降低代理出错概率并减少 token 消耗的专用代理时，这一点尤其有用。

```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>添加时间</td><td>NeuronAI\Tools\Toolkits\Calendar\AddTimeTool</td></tr><tr><td>减去时间</td><td>NeuronAI\Tools\Toolkits\Calendar\SubtractTimeTool</td></tr><tr><td>计算年龄</td><td>NeuronAI\Tools\Toolkits\Calendar\CalculateAgeTool</td></tr><tr><td>转换时区</td><td>NeuronAI\Tools\Toolkits\Calendar\ConvertTimezoneTool</td></tr><tr><td>获取时区信息</td><td>NeuronAI\Tools\Toolkits\Calendar\GetTimezoneInfoTool</td></tr><tr><td>获取星期几</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="https://content.gitbook.com/content/GHx4l2LknIex7vFIUg1R/blobs/se2Qip6z2M3hyHvV8rQm/data-analyst-ai-agent-php-report.png" 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

此工具允许代理理解你的数据库结构，使其能够构建智能查询，而无需你在提示中硬编码表结构或关系。该工具本质上为你的代理提供了相当于数据库管理员对你的架构的理解，使其能够编写符合你的数据模型并利用现有索引和关系的查询。

```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 的架构信息中。这基本上是一种限制代理随后将在数据库上执行查询范围的方法。

```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']
            ),
        ];
    }
}
```

通过限制架构范围，你可以创建专注于应用特定领域的专用代理。内容管理代理可能只需要访问 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 Crawl 是一种基于图的网站遍历工具，借助内置提取和智能发现功能，可并行探索数百条路径。

```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/zhi-neng-ti/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 → 等待结果

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

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

代理会调用 **多个工具同时执行**，让它们同时运行：

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

总耗时：最大值(时间(A), 时间(B), 时间(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 => "Error: {$e->getMessage()}"
    );
```

**扩展 Agent**

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

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

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