> 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/evaluation.md).

# 评估

本指南涵盖了评估智能体的方法。有效的评估对于衡量智能体性能、跟踪改进以及确保你的智能体系统满足质量标准至关重要。

在构建 AI 应用时，评估其输出的一致性至关重要，这不仅有助于维护智能体，也有助于在初始设计阶段评估不同的架构或提示方式。

需要考虑各种定性和定量因素，包括响应语法、任务完成情况、成功率，以及不准确或幻觉。在评估中，也应比较不同配置，以针对特定期望结果进行优化。鉴于 LLM 的动态且非确定性特性，进行严格且频繁的评估也很重要，以确保有一个一致的基线来跟踪改进或回退。

### 配置你的应用

像单元测试一样，最好将 AI 系统的评估器收集到一个专门的目录中。这样，你可以把下面的配置添加到你的应用中 `composer.json` 文件，以便告诉 Composer 如何将你的评估器包含到应用命名空间中：

```json
"autoload-dev": {
    "psr-4": {
        ...,
        "App\\Evaluators\\": "evaluators/"
    }
},
```

接下来创建 `evaluators` 在项目根目录中创建该目录。将评估代码与生产代码分离，可以清楚地区分哪些内容会部署到生产环境，哪些内容只是用于开发和质量保证。

### 创建评估器

使用下面的命令在 `AgentEvaluator` 类到 evaluators 文件夹中：

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

```bash
vendor/bin/neuron make:evaluator App\\Neuron\\Evaluators\\AgentEvaluator
```

{% endtab %}

{% tab title="Windows" %}

```powershell
.\vendor\bin\neuron make:evaluators App\Neuron\Evaluators\AgentEvaluator
```

{% endtab %}
{% endtabs %}

创建的类将具有以下结构：

```php
namespace App\Neuron\Evaluators;

use NeuronAI\Evaluation\Assertions\StringContains;
use NeuronAI\Evaluation\BaseEvaluator;
use NeuronAI\Evaluation\Contracts\DatasetInterface;
use NeuronAI\Evaluation\Dataset\JsonDataset;

class AgentEvaluator extends BaseEvaluator
{
    /**
     * 1. 获取要评估的数据集
     */
    public function getDataset(): DatasetInterface
    {
        return new JsonDataset(__DIR__ . '/datasets/dataset.json');
    }

    /**
     * 2. 运行被测试的智能体逻辑
     */
    public function run(array $datasetItem): mixed
    {
        $response = MyAgent::make()->chat(
            new UserMessage($datasetItem['input'])
        )->getMessage();
        
        return $response->getContent();
    }

    /**
     * 3. 使用断言将输出与预期结果进行评估
     */
    public function evaluate(mixed $output, array $datasetItem): void
    {
        $this->assert(
            new StringContains($datasetItem['reference']),
            $output,
        );
    }
} 
```

逻辑非常直接。评估器首先加载数据集，然后针对数据集中的每个条目运行评估。

在 `run` 方法中，你可以使用示例输入执行你的智能体实体，并返回输出。然后输出会传递给 `evaluate` 方法，在那里你可以执行断言，将输出与参考值或你想要的任何其他逻辑进行比较。

### 数据集加载器

你可以使用任何你想要的内容作为数据集。没有预定义格式。评估器类只是让你能够加载一组测试用例，并针对它们运行评估器。你有两个数据集加载器。

#### ArrayDataset

```php
class AgentEvaluator extends BaseEvaluator
{
    public function getDataset(): DatasetInterface
    {
        return new ArrayDataset([
            [
                'input' => '你好',
                'reference' => '帮助'
            ]
        ]);
    }
    
    ...
}
```

#### JsonDataset

```php
class AgentEvaluator extends BaseEvaluator
{
    public function getDataset(): DatasetInterface
    {
        return new JsonDataset(__DIR__ . '/datasets/dataset.json');
    }
    
    ...
}
```

最终，你还可以实现 `NeuronAI\Evaluation\Contracts\DatasetInterface`.

### 运行评估

如果你已正确配置 composer 文件，可以使用 Neuron CLI 启动评估器：

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

```bash
vendor/bin/neuron evaluations --path=evaluators
```

{% endtab %}

{% tab title="Windows" %}

```powershell
.\vendor\bin\neuron evaluations --path=evaluators
```

{% endtab %}
{% endtabs %}

### 断言

我们为最常见的用例提供了一组内置断言。你也可以实现自己的断言来设计自定义评分系统。请查看下一节。

**StringContains**

```php
$this->assert(new StringContains('positive'), $output);
```

**StringContainsAll**

检查输出是否包含所有关键字：

```php
$this->assert(new StringContainsAll(['hello', 'world']), $output);
```

**StringContainsAny**

检查输出是否包含任意一个关键字：

```php
$this->assert(new StringContainsAny(['success', 'completed']), $output);
```

**StringStartsWith**

检查输出是否以前缀开头：

```php
$this->assert(new StringStartsWith('Hello'), $output);
```

**StringEndsWith**

检查输出是否以后缀结尾：

```php
$this->assert(new StringEndsWith('!'), $output);
```

**StringLengthBetween**

检查字符串长度是否在范围内：

```php
$this->assert(new StringLengthBetween(10, 100), $output);
```

**StringDistance**

使用 Levenshtein 距离检查字符串相似度：

```php
$this->assert(new StringDistance(
    reference: '预期文本',
    threshold: 0.5, // 最低相似度分数
    maxDistance: 50 // 允许的最大编辑次数
), $output);
```

**StringSimilarity**

使用嵌入检查字符串相似度：

```php
use NeuronAI\Evaluation\Assertions\StringSimilarity;
use NeuronAI\RAG\Embeddings\OpenAI\OpenAIEmbeddings;

$this->assert(new StringSimilarity(
    reference: '敏捷的棕色狐狸',
    embeddingsProvider: new OpenAIEmbeddings(key: 'YOUR_KEY'),
    threshold: 0.6
), $output);
```

**MatchesRegex**

匹配正则表达式：

```php
$this->assert(new MatchesRegex('/^\d{3}-\d{2}-\d{4}$/'), $output);
```

**IsValidJson**

检查输出是否为有效的 JSON：

```php
$this->assert(new IsValidJson(), $output);
```

### 作为裁判的 AI

使用 AI 智能体通过自定义标准评估输出。Neuron 为你提供了原始类 AgentJudge 来定义自定义标准，否则你也可以使用内置的裁判断言之一。

```php
use NeuronAI\Evaluation\Assertions\AgentJudge;

class AgentJudgeEvaluator extends BaseEvaluator
{
    protected AgentInterface $judge;

    public function setUp(): void
    {
        $this->judge = Agent::make()
            ->setAiProvider(
                new Antrhopic(...)
            )
            ->setInstructions('你是客户支持回复的专家评估员。');
    }

    public function getDataset(): DatasetInterface
    {
        return new JsonDataset(...);
    }

    public function run(array $datasetItem): mixed
    {
        $response = MyAgent::make()->chat(
            new UserMessage($datasetItem['input'])
        )->getMessage();
        
        return $response->getContent();
    }
    
    public function evaluate(mixed $output, array $datasetItem): void
    {
        $this->assert(new AgentJudge(
            judge: $this->judge,
            criteria: '回复应该有帮助、礼貌，并直接回答客户的问题',
            threshold: $datasetItem['threshold']
        ), $output);
    }
}
```

#### 忠实度裁判

检查输出是否基于上下文（没有幻觉）：

```php
$this->assert(new FaithfulnessJudge(
    judge: $this->judge,
    context: $retrievedDocuments,
    threshold: 0.7
), $output);
```

#### 正确性裁判

与预期答案比较：

```php
$this->assert(new CorrectnessJudge(
    judge: $judge,
    expected: $datasetItem['expected_answer'],
    threshold: 0.7
), $output);
```

#### 相关性裁判

检查输出是否回答了问题：

```php
$this->assert(new RelevanceJudge(
    judge: $judge,
    question: $datasetItem['question'],
    threshold: 0.7
), $output);
```

#### 有用性裁判

评估实用性和可操作性：

```php
$this->assert(new HelpfulnessJudge(
    judge: $judge,
    threshold: 0.7
), $output);
```

### 创建自定义断言

```php
use NeuronAI\Evaluation\Assertions\AbstractAssertion;
use NeuronAI\Evaluation\AssertionResult;

class GreaterThanAssertion extends AbstractAssertion
{
    public function __construct(
        private readonly float $threshold
    ) {}

    public function evaluate(mixed $actual): AssertionResult
    {
        if (!is_numeric($actual)) {
            return AssertionResult::fail(
                0.0,
                '期望数值，得到的是 ' . gettype($actual),
            );
        }

        if ($actual > $this->threshold) {
            return AssertionResult::pass(1.0);
        }

        return AssertionResult::fail(
            0.0,
            "期望 {$actual} 大于 {$this->threshold}"
        );
    }
}
```

### 输出

评估模块使用一个 PHP 配置文件来控制评估结果的显示方式。配置系统支持多个输出驱动，从而使结果能够同时发送到控制台、文件、数据库或外部 API。

#### **配置文件**

创建 `evaluation.php` 文件到你的项目根目录：

```php
<?php

use NeuronAI\Evaluation\OutputDrivers\ConsoleDriver;
use NeuronAI\Evaluation\OutputDrivers\JsonDriver;

return [
    'output' => [
        // 在控制台输出结果
        ConsoleDriver::class => ['verbose' => true],

        // 将结果保存到 json 文件
        JsonDriver::class => ['path' => 'evaluation-results.json'],
    ],
];
```

你可以为每个输出类声明一个选项数组。这些配置将作为参数传递给输出类实现的构造函数。

**如果不存在配置文件**，系统默认使用 `ConsoleOutputDriver` 并输出到标准输出。

#### 创建自定义输出

实现 `EvaluationOutputInterface` 来创建自定义输出驱动：

```php
namespace App\Neuron\Evaluations;

use NeuronAI\Evaluation\Contracts\EvaluationOutputInterface;
use NeuronAI\Evaluation\Runner\EvaluatorSummary;

class DatabaseOutput implements EvaluationOutputInterface
{
    public function __construct(
        private readonly \PDO $pdo,
        private readonly string $table = 'evaluations'
    ) {}

    public function output(EvaluatorSummary $summary): void
    {
        $stmt = $this->pdo->prepare(
            "INSERT INTO {$this->table} (passed, failed, success_rate, total_time, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())"
        );
        $stmt->execute([
            $summary->getPassedCount(),
            $summary->getFailedCount(),
            $summary->getSuccessRate(),
            $summary->getTotalExecutionTime(),
        ]);
    }
}
```

一旦你创建了输出类，就可以将其注册到配置文件中，以便在下次运行评估时使用。

```php
<?php

use NeuronAI\Evaluation\OutputDrivers\ConsoleDriver;
use NeuronAI\Evaluation\OutputDrivers\JsonDriver;

return [
    'output' => [
        // 在控制台输出结果
        ConsoleDriver::class => ['verbose' => true],

        // 将结果保存到 json 文件
        //JsonDriver::class => ['path' => 'evaluation-results.json'],
        
        // 将结果保存到数据库
        DatabaseOutput::class => [
            'pdo' => new \PDO(...),
            'table' => 'evaluations',
        ]
    ],
];
```

### 并行执行

默认情况下，评估命令一次处理一个数据集条目。由于大多数评估器的时间都花在等待 AI 提供方响应上，通过使用 `--concurrency` 选项并行处理多个数据集条目，可以大幅缩短总运行时间：

```bash
vendor/bin/neuron evaluation path/to/evaluators --concurrency=3
```

使用 `--concurrency=3`时，最多可以同时评估 3 个数据集条目，每个条目都在各自的 PHP 子进程中运行。对于一个包含 100 个条目的数据集，如果每个条目都需要一次 2 秒的 LLM 调用，评估时间会从约 200 秒降至约 66 秒。

#### 要求

并行执行依赖于进程 fork，这需要：

* 该 [pcntl](https://www.php.net/manual/en/book.pcntl.php) PHP 扩展（Linux 和 macOS 可用，Windows 不可用）
* 该 [spatie/fork](https://github.com/spatie/fork) 包：

```bash
composer require --dev spatie/fork
```

如果缺少其中任何一个，命令会打印提示并自动回退到顺序执行，因此同一个命令可在任何环境中运行。

#### 如何选择并发级别

正在执行中的每个条目都是向你的 AI 提供方发出的一个活动请求。先从一个中等值（3–5）开始，只要没有碰到提供方的速率限制，就逐步提高。如果你看到速率限制错误以测试失败的形式出现，就降低这个值。

#### 其工作原理，以及需要注意什么

每个数据集条目都在你的评估器的一个 fork 副本中运行，其结果会发送回父进程。这带来一些实际影响：

* **结果不受影响。** 条目彼此独立地评估，结果保持其数据集顺序，最终报告与顺序运行完全一致。
* **状态不会在条目之间共享。** 每个条目看到的评估器状态，都是在 `setUp()`之后的状态。处理某个条目时产生的副作用（增加属性、向文件追加内容）对其他条目不可见。如果你的评估器依赖于在多个条目之间累积状态，请继续顺序运行。
* **输出必须可序列化。** 由 `run()` 返回的值会通过 `serialize()`跨越进程边界。如果它无法被序列化（例如其中包含闭包或打开的连接），断言结果会保留，但报告中显示的输出会被替换为占位字符串。

#### 执行时间报告

报告的总时间是真实的墙钟运行时长，而每个测试的平均时间反映的是每个单独条目的实际时长——因此在并行执行下，单个测试的平均时间可能大于总时间除以测试数量。

### 自定义 bootstrap 文件

默认情况下， `neuron` CLI 只会加载你项目的 Composer 自动加载器（`vendor/autoload.php`）。当你的类只是由 Composer 可解析的普通 PHP 类时，这就足够了，但通常并非如此：评估器可能需要通过框架辅助函数读取配置，需要从 `.env`加载环境变量，依赖常量，或需要初始化服务容器。在这些情况下，命令会因“找不到类”或缺少配置而失败，因为通常用于准备该环境的代码——也就是你的框架 bootstrap——从未运行。

该 `--autoload-file` 选项通过让你指定一个 PHP 文件来解决这个问题，这个文件会在 *之前* 命令启动时执行，并且会在默认 Composer 自动加载器之外额外加载：

```bash
vendor/bin/neuron evaluation /path/to/evaluators --autoload-file=bootstrap.php
```

这个文件可以像普通 bootstrap 一样做任何事情——注册额外的自动加载器、加载环境变量、定义常量，或启动你的框架。例如，要运行依赖 Laravel 应用的评估器：

```php
<?php
// bootstrap.php

require __DIR__.'/vendor/autoload.php';

$app = require_once __DIR__.'/bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
```
