本系列之前的文章都提供了在各种场景下执行评估的例子,我们无一例外都是在控制台中呈现评估的结果,实际上MEAI不仅可以将评估结果持久化存储,还能将其转换网页的形式以极具可读性的方式呈现出来。本篇文章就来介绍一下关于评估报告的生成问题。

1. 保留评估结果并呈现评估报告

MEAI基于评估报告相关的功能实现在NuGet包Microsoft.Extensions.AI.Evaluation.Reporting中,我们可以利用它实现基于场景的评估,也就是说我们可以在评估是指定评估场景和迭代名称,并以场景/迭代的结构持久化评估结果,并生成评估报告。作为演示,我们编写了如下的演示程序。如代码所示,我们常创建了与评估报告相关的ReportingConfiguration配置,并指定了如下三个参数:

  • evaluators:执行评估采用的评估器,这里使用了基于一致性和完整性的CoherenceEvaluatorCompletenessEvaluator对象;
  • chatConfiguration: 提供了用来提供调用LLM的IChatClient对象的ChatConfiguration配置;
  • resultStore:提供的DiskBasedResultStore将评估结果存储在本地文件系统,路径为e:\eval_results

接下来我们使用模拟的响应内容和评估基准(Ground Truth)进行了两轮评估,来模拟同一个评估场景下的两轮迭代。具体来说,我们调用ReportingConfigurationCreateScenarioRunAsync方法针对指定的评估场景和迭代名称创建了一个ScenarioRun对象,并调用它的EvaluateAsync方法实施评估。在针对该方法的调用中,我们利用modelResponse参数指定了模拟的响应文本,利用additionalContext参数提供了作为评估上下文的CompletenessEvaluatorContext对象。

using DotNetEnv;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Evaluation;
using Microsoft.Extensions.AI.Evaluation.Quality;
using Microsoft.Extensions.AI.Evaluation.Reporting;
using Microsoft.Extensions.AI.Evaluation.Reporting.Storage;
using OpenAI;
using System.ClientModel;

Env.Load();
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var endpoint = Environment.GetEnvironmentVariable("OPENAI_BASE_URL")!;
var projectUrl = Environment.GetEnvironmentVariable("PROJECT_URL")!;
var evalChatClient = new OpenAIClient(new ApiKeyCredential(apiKey), new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
    .GetChatClient(model: "gpt-5.4-mini")
    .AsIChatClient();

var config = new ReportingConfiguration(
    evaluators: [new CoherenceEvaluator(), new CompletenessEvaluator()],
    chatConfiguration: new ChatConfiguration(evalChatClient),    
    resultStore: new DiskBasedResultStore("e:\\eval_results"));

var groundTruth = """
    高血压患者在饮食上需要严格控制钠盐摄入,每日不超过5克。
    同时应减少高脂肪和高胆固醇食物,多吃富含钾和镁的蔬菜水果。
    此外,保持适量运动和戒烟限酒也是控制血压的关键。
    """;

await using var run1 = await config.CreateScenarioRunAsync("Scenario4Test",iterationName: "iternation1");
var response = """
    高血压患者平时一定要少吃盐,每天盐的摄入量控制在5克以内。
    """;
await run1.EvaluateAsync(
    modelResponse: new ChatMessage(ChatRole.Assistant, response),
    additionalContext: [new CompletenessEvaluatorContext(groundTruth)]
    );

await using var run2 = await config.CreateScenarioRunAsync("Scenario4Test", iterationName: "iternation2");
response = """
    高血压患者平时一定要少吃盐,每天盐的摄入量控制在5克以内。
    饮食要清淡,多吃蔬菜和水果。平时还要注意戒烟和限酒。
    保持适量运动,并戒烟限酒
    """;
await run2.EvaluateAsync(
    modelResponse: new ChatMessage(ChatRole.Assistant, response),
    additionalContext: [new CompletenessEvaluatorContext(groundTruth)]
    );

ScenarioRun实现了IAsyncDisposable接口,并在实现的DisposeAsync方法中将EvaluateAsync方法收集的评估结果进行持久化,具体使用的就是构造ReportingConfigurationp配置时指定的IEvaluationResultStore对象(我们指定的DiskBasedResultStore实现了此接口),这就是我们会采用await using的方式创建ScenarioRun的原因。程序运行后,我们就会在DiskBasedResultStore指定的根目录(e:\eval_results)下看到承载评估结果的两个.json文件:

./results/Default/Scenario4Test
|__ iternation1.json
└── iternation2.json

针对生成的JSON文件生成评估报告还需要按照Microsoft.Extensions.AI.Evaluation.Console工具,我们可以执行如下的命令完成此工具的安装。

dotnet tool install -g Microsoft.Extensions.AI.Evaluation.Console

接下来我们按照如下的方式执行aieval命令根据--path参数指定的目录在--output目录指定的路径生成作为评估报告的.html文件。

aieval report --path e:\eval_results --output e:\eval_results\report.html --open

由于指定了--open参数,所以生成的.html文件会直接在浏览中打开。如下图所示,我们可以看出两次迭代的评估结果,第一次失败,第二次成功。

Alternative Text

2. ReportingConfiguration

从上面的演示程序可以看出,基于评估报告的编程涉及到的最核心的类型就是如下这个名为ReportingConfiguration的配置对象,它不仅提供了整个评估报告生成系统涉及到的所有配置,还利用CreateScenarioRunAsync方法创建了实施评估工作的ScenarioRun对象。

public sealed class ReportingConfiguration
{
	public IReadOnlyList<IEvaluator> Evaluators { get; }
	public IEvaluationResultStore ResultStore { get; }
	public ChatConfiguration? ChatConfiguration { get; }
	public IEvaluationResponseCacheProvider? ResponseCacheProvider { get; }
	public IReadOnlyList<string> CachingKeys { get; }
	public string ExecutionName { get; }
	public Func<EvaluationMetric, EvaluationMetricInterpretation?>? EvaluationMetricInterpreter { get; }
	public IReadOnlyList<string>? Tags { get; }

	public ReportingConfiguration(
        IEnumerable<IEvaluator> evaluators, 
        IEvaluationResultStore resultStore, 
        ChatConfiguration? chatConfiguration = null, 
        IEvaluationResponseCacheProvider? responseCacheProvider = null, 
        IEnumerable<string>? cachingKeys = null, string executionName = "Default", 
        Func<EvaluationMetric, EvaluationMetricInterpretation?>? evaluationMetricInterpreter = null, 
        IEnumerable<string>? tags = null);
}

ReportingConfiguration提供的配置选项说明如下:

  • Evaluators:提供作为评估器的IEvaluator对象列表;
  • ResultStore:提供持久化评估结果的IEvaluationResultStore对象;
  • ChatConfiguration:为执行IEvaluator提供IChatClient对象,为评估工作赋予调用LLM的能力;
  • ResponseCacheProvider:提供IEvaluationResponseCacheProvider对象缓存响应结果,避免在输入未发生变化时重复调用大模型,从而大幅省钱提速
  • CachingKeys: 在默认情况下,响应结果缓存的Key由提示词、模型配置和场景与迭代名称组件,利用此配置可以添加额外的成员;
  • ExecutionName: 执行批次的名称,默认为Default;
  • EvaluationMetricInterpreter: 提供一个委托用来根据原始的评估指标EvaluationMetric生成对应的EvaluationMetricInterpretation,让指标更具解释性;
  • Tags: 为评估打上所需的标签。

用于创建ScenarioRunCreateScenarioRunAsync方法定义如下,通过参数可以提供评估场景和跌打名称,以及作为响应结果缓存Key的额外成员和标签。

public sealed class ReportingConfiguration
{
	public async ValueTask<ScenarioRun> CreateScenarioRunAsync(
        string scenarioName, 
        string iterationName = "1", 
        IEnumerable<string>? additionalCachingKeys = null, 
        IEnumerable<string>? additionalTags = null, 
        CancellationToken cancellationToken = default);
}

3.1 IEvaluationResultStore

作为评估结果存储的抽象,IEvaluationResultStore接口提供了如下的方法实现了针对评估结果的读写和删除操作,以及针对执行名称、评估场景名称和迭代名称的查询和检索工作。

public interface IEvaluationResultStore
{
	IAsyncEnumerable<ScenarioRunResult> ReadResultsAsync(
        string? executionName = null, 
        string? scenarioName = null, string? 
        iterationName = null, 
        CancellationToken cancellationToken = default);

	ValueTask WriteResultsAsync(
        IEnumerable<ScenarioRunResult> results, 
        CancellationToken cancellationToken = default);

	ValueTask DeleteResultsAsync(
        string? executionName = null, 
        string? scenarioName = null, 
        string? iterationName = null, 
        CancellationToken cancellationToken = default);

	IAsyncEnumerable<string> GetLatestExecutionNamesAsync(
        int? count = null, 
        CancellationToken cancellationToken = default);

	IAsyncEnumerable<string> GetScenarioNamesAsync(
        string executionName, 
        CancellationToken cancellationToken = default);

	IAsyncEnumerable<string> GetIterationNamesAsync(
        string executionName, 
        string scenarioName, 
        CancellationToken cancellationToken = default);
}

这里作为评估结果的ScenarioRunResult不仅包括作为原始结果的EvaluationResult对象,还包括评估场景和迭代名称、执行名称、创建时间戳、作为请求的ChatMessage列表、作为响应的ChatResponse等。

public sealed class ScenarioRunResult
{
	public string ScenarioName { get; set; }
	public string IterationName { get; set; }
	public string ExecutionName { get; set; }
	public DateTime CreationTime { get; set; }
	public IList<ChatMessage> Messages { get; set; }
	public ChatResponse ModelResponse { get; set; }
	public EvaluationResult EvaluationResult { get; set; }
	public ChatDetails? ChatDetails { get; set; }
	public IList<string>? Tags { get; set; }
	public int? FormatVersion { get; set; }

NuGet包Microsoft.Extensions.AI.Evaluation.Reporting提供了针对IEvaluationResultStore接口的两个实现,分别基于本地磁盘存储的DiskBasedResultStore和针对Azure云存储的AzureStorageResultStore

public sealed class DiskBasedResultStore : IEvaluationResultStore;
public sealed class AzureStorageResultStore : IEvaluationResultStore;

3.2 IEvaluationResponseCacheProvider

为了避免针对LLM的频繁调用导致过多的Token消费和耗时,我们可以使用IEvaluationResponseCacheProvider对象对LL返回的评估响应进行缓存。

public interface IEvaluationResponseCacheProvider
{
	ValueTask<IDistributedCache> GetCacheAsync(string scenarioName, string iterationName, CancellationToken cancellationToken = default);
	ValueTask ResetAsync(CancellationToken cancellationToken = default);
	ValueTask DeleteExpiredCacheEntriesAsync(CancellationToken cancellationToken = default);
}

IEvaluationResponseCacheProvider提供了三个操作缓存的方法:

  • GetCacheAsync:根据指定的场景和迭代名称提取缓存的结果,具体返回的是一个IDistributedCache对象;
  • ResetAsync:清空换粗;
  • DeleteExpiredCacheEntriesAsync:清理过期缓存项。

3. ScenarioRun

虽然ScenarioRun提供了EvaluateAsync方法,但它并不是一个IEvaluator对象,但IEvaluator对象是通过此对象驱动执行的。当它的EvaluateAsync方法被调用后,它会驱动执行每一个IEvaluator对象,并将评估指标添加到_result字段表示的ScenarioRunResult对象中。当DisposeAsync方法被执行的时候,会调用IEvaluationResultStore对象的WriteResultsAsync方法对ScenarioRunResult对象实施持久化。

public sealed class ScenarioRun : IAsyncDisposable
{	
    private ScenarioRunResult? _result;

	public string ScenarioName { get; }
	public string IterationName { get; }
	public string ExecutionName { get; }
	public ChatConfiguration? ChatConfiguration { get; }

	internal ScenarioRun(
        string scenarioName, 
        string iterationName, 
        string executionName, 
        IEnumerable<IEvaluator> evaluators, IEvaluationResultStore resultStore,
        ChatConfiguration? chatConfiguration = null, 
        Func<EvaluationMetric, EvaluationMetricInterpretation?>? evaluationMetricInterpreter = null, 
        ChatDetails? chatDetails = null, 
        IEnumerable<string>? tags = null);

	public async ValueTask<EvaluationResult> EvaluateAsync(
        IEnumerable<ChatMessage> messages, ChatResponse modelResponse, 
        IEnumerable<EvaluationContext>? additionalContext = null, 
        CancellationToken cancellationToken = default);

	public async ValueTask DisposeAsync();
}
Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐