[Agent的评估-11]为MEAI的评估生成评估报告
本系列之前的文章都提供了在各种场景下执行评估的例子,我们无一例外都是在控制台中呈现评估的结果,实际上MEAI不仅可以将评估结果持久化存储,还能将其转换网页的形式以极具可读性的方式呈现出来。本篇文章就来介绍一下关于评估报告的生成问题。
1. 保留评估结果并呈现评估报告
MEAI基于评估报告相关的功能实现在NuGet包Microsoft.Extensions.AI.Evaluation.Reporting中,我们可以利用它实现基于场景的评估,也就是说我们可以在评估是指定评估场景和迭代名称,并以场景/迭代的结构持久化评估结果,并生成评估报告。作为演示,我们编写了如下的演示程序。如代码所示,我们常创建了与评估报告相关的ReportingConfiguration配置,并指定了如下三个参数:
- evaluators:执行评估采用的评估器,这里使用了基于一致性和完整性的
CoherenceEvaluator和CompletenessEvaluator对象; - chatConfiguration: 提供了用来提供调用LLM的
IChatClient对象的ChatConfiguration配置; - resultStore:提供的
DiskBasedResultStore将评估结果存储在本地文件系统,路径为e:\eval_results。
接下来我们使用模拟的响应内容和评估基准(Ground Truth)进行了两轮评估,来模拟同一个评估场景下的两轮迭代。具体来说,我们调用ReportingConfiguration的CreateScenarioRunAsync方法针对指定的评估场景和迭代名称创建了一个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文件会直接在浏览中打开。如下图所示,我们可以看出两次迭代的评估结果,第一次失败,第二次成功。
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: 为评估打上所需的标签。
用于创建ScenarioRun的CreateScenarioRunAsync方法定义如下,通过参数可以提供评估场景和跌打名称,以及作为响应结果缓存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();
}
更多推荐



所有评论(0)