Agent的评估-01:针对MAF的三种Agent评估方式针对三种IAgentEvaluator的不同实现,介绍了三种评估MAF Agent的方式。在介绍IAgentEvaluator接口的时候,我们提到MAF以此为核心的评估系统。这个系统由IAgentEvaluator、EvalItem和AgentEvaluationResults三个核心类型组成。
1. IAgentEvaluator
MAF的评估系统以IAgentEvaluator为核心。如下面的代码片段所示,每个IAgentEvaluator对象具有一个通过只读属性Name表示的名称,具体的评估工作实现在EvaluateAsync方法中。每一次针对EvaluateAsync方法的调用都会利用evalName参数为本次评估是定一个名称,默认值为Agent Framework Eval。评估所需的所有输入承载与作为输入参数的EvalItem列表上,而评估结果则体现在返回的AgentEvaluationResults对象上。
public interface IAgentEvaluator
{
string Name { get; }
Task<AgentEvaluationResults> EvaluateAsync(
IReadOnlyList<EvalItem> items,
string evalName = "Agent Framework Eval",
CancellationToken cancellationToken = default);
}
2. EvalItem
EvalItem列表作为IAgentEvaluator对象的输入,承载着所有评估目标Agent所需的所有输入。对于针对AIAgent的一次评估,指的是通过完成一次AIAgent调用,评估返回的响应与理想预期的差距,这项基本的评估工作对应着一个独立的EvalItem对象。
2.1 IConversationSplitter
在正式介绍EvalItem类型之前,我们有必要先来了解如下这个与它有关的IConversationSplitter接口。IConversationSplitter接口代表对话分割器,旨在利用Split方法将指定的一个代表对话历史的ChatMessage列表中分别提取作为查询和响应的消息列表。不同的实现采用不同的策略确定这条作为查询和响应的分割线。
public interface IConversationSplitter
{
(IReadOnlyList<ChatMessage> QueryMessages, IReadOnlyList<ChatMessage> ResponseMessages) Split(IReadOnlyList<ChatMessage> conversation);
}
MAF预定了两个私有的分割器类型,对应的单例对象分别通过ConversationSplitters类型的静态属性LastTurn和Full返回。
public static class ConversationSplitters
{
public static IConversationSplitter LastTurn { get; } = new LastTurnSplitter();
public static IConversationSplitter Full { get; } = new FullSplitter();
private sealed class LastTurnSplitter : IConversationSplitter {}
private sealed class FullSplitter : IConversationSplitter{}
}
LastTurn属性返回的一个LastTurnSplitter对象。顾名思义,LastTurnSplitter关注对话历史的最后一轮对话。由于一轮对话总是以User消息开始,所以如果对话历史存在User消息,那么最后一条User消息之前(包含自身)的消息会作为查询,后面的将作为响应。如果整个对话历史没有User消息,意味着整个列表将作为响应,查询为空。如下的演示程序很好地演示了这样的划分规则。
var systemMessage = new ChatMessage(role: ChatRole.System, content: null);
ChatMessage[] userMessages = [new ChatMessage(role: ChatRole.User, content: null),
new ChatMessage(role: ChatRole.User, content: null)];
ChatMessage[] assistantMessages = [new ChatMessage(role: ChatRole.Assistant, content: null),
new ChatMessage(role: ChatRole.Assistant, content: null)];
List<ChatMessage> history = [systemMessage, userMessages[0], assistantMessages[0], userMessages[1], assistantMessages[1]];
var (query, response) = ConversationSplitters.LastTurn.Split(history);
Debug.Assert(query.Count == 4);
Debug.Assert(response.Count == 1);
history = [systemMessage, userMessages[0], assistantMessages[0], userMessages[1]];
(query, response) = ConversationSplitters.LastTurn.Split(history);
Debug.Assert(query.Count == 4);
Debug.Assert(response.Count == 0);
history = [ assistantMessages[0], assistantMessages[1]];
(query, response) = ConversationSplitters.LastTurn.Split(history);
Debug.Assert(query.Count == 0);
Debug.Assert(response.Count == 2);
LastTurnSplitter相当于将前面轮次的消息作为少样本提示词,而FullSplitter则将提供的消息列表视为经历多次循环的完整对话历史,所以它会采用截然相反的划分方式:将**第一条User消息之前(含自身)**的部分作为查询,后面的部分作为响应。如下的演示程序体现了这种分割规则。
var systemMessage = new ChatMessage(role: ChatRole.System, content: null);
ChatMessage[] userMessages = [new ChatMessage(role: ChatRole.User, content: null), new ChatMessage(role: ChatRole.User, content: null)];
ChatMessage[] assistantMessages = [new ChatMessage(role: ChatRole.Assistant, content: null), new ChatMessage(role: ChatRole.Assistant, content: null)];
List<ChatMessage> history = [systemMessage, userMessages[0], assistantMessages[0], userMessages[1], assistantMessages[1]];
var (query, response) = ConversationSplitters.Full.Split(history);
Debug.Assert(query.Count == 2);
Debug.Assert(response.Count == 3);
history = [systemMessage, userMessages[0], assistantMessages[0], userMessages[1]];
(query, response) = ConversationSplitters.Full.Split(history);
Debug.Assert(query.Count == 2);
Debug.Assert(response.Count == 2);
history = [ assistantMessages[0], assistantMessages[1]];
(query, response) = ConversationSplitters.Full.Split(history);
Debug.Assert(query.Count == 0);
Debug.Assert(response.Count == 2);
2.2 EvalItem的两种构建方式
EvalItem利用定义的两个构造函数提供了两种构建方式。其中是直接指定查询和响应文本以及对话历史,另一种则是提供对话历史和作为分割器的IConversationSplitter对象,后者是可选的,默认传入的是一个LastTurnSplitter对象。
public sealed class EvalItem
{
public string Query { get; }
public string Response { get; }
public IReadOnlyList<ChatMessage> Conversation { get; }
public bool HasImageContent { get; }
public IReadOnlyList<AITool>? Tools { get; set; }
public string? Context { get; set; }
public string? ExpectedOutput { get; set; }
public IReadOnlyList<ExpectedToolCall>? ExpectedToolCalls { get; set; }
public ChatResponse? RawResponse { get; set; }
public IConversationSplitter? Splitter { get; set; }
public EvalItem(string query, string response, IReadOnlyList<ChatMessage> conversation);
public EvalItem(IReadOnlyList<ChatMessage> conversation, IConversationSplitter? splitter = null);
}
public record ExpectedToolCall(string Name, IReadOnlyDictionary<string, object>? Arguments = null);
各属性说明如下:
- Query: 作为用户输入的查询文本:
- 第一种构造方式:直接由
query参数指定; - 第二种构造方式:如果经过分割后的查询存在,那么查询消息列表最后一条User消息的文本将作为此属性,否则返回一个空字符串。
- 第一种构造方式:直接由
- Response:作为输出的响应文本:
- 第一种构造函数:通过
response参数指定; - 第二种构造函数:分割后的响应消息中的所有Assistant消息的文本利用空格进行拼接。
- 第一种构造函数:通过
- Conversation:由两个构造函数的
conversation参数指定; - HasImageContent:对话历史中的消息中,是否存在这样一条消息,它的
Contents列表中存在MIME类型为image的DataContent或者UriContent; - Context:落地上下文(Grounding context),即在评估或推理时,提供给模型的真实世界参考信息,用于约束、校准、验证Agent的回答是否基于正确的事实或业务规则;
- ExpectedOutput: 期望的输出文本;
- ExpectedToolCalls:希望的执行的工具调用;
- RawResponse:
IChatClient对象调用LLM的原始响应; - Splitter: 通过第二个构造函数的
splitter参数指定的IConversationSplitter对象。
1.3 EvalItem的初始化
对于如下五个用来评估指定AIAgent的EvaluateAsync方法重载来说,如果利用参数responses显式指定了作为Agent响应的AgentResponse列表,将针对每个AgentResponse对象创建创建一个EvalItem。否则将按照numRepetitions参数指定的重复初始,基于queries指定的每个查询文本调用指定的AIAgent,然后根据返回的AgentResponse创建一个EvalItem,此时创建EvalItem的数量为numRepetitions与queries数量的乘积。
public static async Task<AgentEvaluationResults> EvaluateAsync(
this AIAgent agent,
IEnumerable<string> queries,
IAgentEvaluator evaluator,
string evalName = DefaultEvalName,
IEnumerable<string>? expectedOutput = null,
IEnumerable<IEnumerable<ExpectedToolCall>>? expectedToolCalls = null,
IConversationSplitter? splitter = null,
int numRepetitions = 1,
CancellationToken cancellationToken = default);
public static async Task<AgentEvaluationResults> EvaluateAsync(
this AIAgent agent,
IEnumerable<string> queries,
IEvaluator evaluator,
ChatConfiguration chatConfiguration,
string evalName = DefaultEvalName,
IEnumerable<string>? expectedOutput = null,
IEnumerable<IEnumerable<ExpectedToolCall>>? expectedToolCalls = null,
IConversationSplitter? splitter = null,
int numRepetitions = 1,
CancellationToken cancellationToken = default);
public static async Task<IReadOnlyList<AgentEvaluationResults>> EvaluateAsync(
this AIAgent agent,
IEnumerable<string> queries,
IEnumerable<IAgentEvaluator> evaluators,
string evalName = DefaultEvalName,
IEnumerable<string>? expectedOutput = null,
IEnumerable<IEnumerable<ExpectedToolCall>>? expectedToolCalls = null,
IConversationSplitter? splitter = null,
int numRepetitions = 1,
CancellationToken cancellationToken = default);
public static async Task<AgentEvaluationResults> EvaluateAsync(
this AIAgent agent,
IEnumerable<AgentResponse> responses,
IEnumerable<string> queries,
IAgentEvaluator evaluator,
string evalName = DefaultEvalName,
IEnumerable<string>? expectedOutput = null,
IEnumerable<IEnumerable<ExpectedToolCall>>? expectedToolCalls = null,
CancellationToken cancellationToken = default);
public static async Task<AgentEvaluationResults> EvaluateAsync(
this AIAgent agent,
IEnumerable<AgentResponse> responses,
IEnumerable<string> queries,
IEvaluator evaluator,
ChatConfiguration chatConfiguration,
string evalName = DefaultEvalName,
IEnumerable<string>? expectedOutput = null,
IEnumerable<IEnumerable<ExpectedToolCall>>? expectedToolCalls = null,
CancellationToken cancellationToken = default);
3. AgentEvaluationResults
IAgentEvaluator的EvaluateAsync方法执行后会评估结果封装成一个AgentEvaluationResults对象,所以针对AIAgent上述这些EvaluateAsync扩展方法来说,如果指定的是单一的IAgentEvaluator对象,则返回单一的AgentEvaluationResults。如果指定的是一个IAgentEvaluator列表,则返回一个数据对等的AgentEvaluationResults列表。AgentEvaluationResults类型定义如下。
public sealed class AgentEvaluationResults
{
public string ProviderName { get; }
public Uri? ReportUrl { get; set; }
public string? EvalId { get; set; }
public string? RunId { get; set; }
public string? Status { get; set; }
public string? Error { get; set; }
public IReadOnlyList<EvaluationResult> Items {get; }
public IReadOnlyList<EvalItem>? InputItems { get; }
public IReadOnlyDictionary<string, AgentEvaluationResults>? SubResults { get; set; }
public IReadOnlyDictionary<string, PerEvaluatorResult>? PerEvaluator { get; set; }
public IReadOnlyList<EvalItemResult>? DetailedItems { get; set; }
public int Passed {get; }
public int Failed {get; }
public int Total {get; }
public bool AllPassed {get; }
}
public record PerEvaluatorResult(int Passed, int Failed);
各属性说明如下:
- ProviderName: 提供的评估提供者,一般会直接使用
IAgentEvaluator的名称; - ReportUrl:Foundry评估报告的URL,用于查看详细结果;
- EvalId:Foundry评估任务的唯一ID;
- RunId:Foundry评估运行的唯一ID;
- Status:评估运行状态,例如
completed、failed、canceled和timeout; - Error: 当评估失败时的错误信息;
- Items:每个评估项的结果列表,对应每个
EvalItem的EvaluationResult; - InputItems: 原始的评估输入项,用于审计和溯源,与Items按位置一一对应。
- SubResults:工作流评估中每个子Agent的结果,形成嵌套结构;
- PerEvaluator:每个评估器的通过/失败统计(Foundry专用);
- DetailedItems: 基于具体EvalItem的详细评估结果,包括分数、错误信息、token使用等;
- Passed、Failed、Total:通过/失败/总的评估项数量;
- AllPassed:是否所有评估项都通过。
3.1 EvalItemResult
EvalItemResult是Foundry评估系统中针对一个EvalItem的评估任务中的描述,同时也保安相关的评估指标。它记录某个输出项的评估状态,并包含每个评估指标的评分结果,用于判断该项是否通过或失败。它还保存错误代码、错误信息、输入/输出文本回显,以及token使用情况,便于调试和审计。通过IsPassed、IsFailed、IsError等属性,可以快速判断该项的整体评估结果,是Foundry细粒度评估的核心数据结构。
public sealed class EvalItemResult
{
public string ItemId { get; }
public string Status { get; }
public IReadOnlyList<EvalScoreResult> Scores { get; }
public string? ErrorCode { get; set; }
public string? ErrorMessage { get; set; }
public string? ResponseId { get; set; }
public string? InputText { get; set; }
public string? OutputText { get; set; }
public IReadOnlyDictionary<string, int>? TokenUsage { get; set; }
public bool IsError{ get; }
public bool IsPassed { get; }
public bool IsFailed { get; }
}
public record EvalScoreResult(string Name, double Score, bool? Passed = null);
各个属性说明如下:
- ItemId:该评估项在Foundry evaluation API中的ID;
- Status:该项的评估状态,例如"pass", “fail”, "error"和"errored"等;
- Scores:每个评估指标的得分列表;
- ErrorCode:当该项评估发生错误时的错误代码;
- ErrorMessage:当该项评估发生错误时的错误信息;
- ResponseId:评估API返回的Response ID;
- InputText:评估API回显的输入文本;
- OutputText:评估API回显的输出文本;
- TokenUsage:评估过程中使用的token数量;
- IsError:如果状态为
error或errored,则该项处于错误状态; - IsPassed:如果所有指标的
Passed == true,则该项整体通过; - IsFailed:如果任一指标的
Passed == false,则该项整体失败。
3.2 EvaluationResult & EvaluationMetric
和EvalItemResult一样,EvaluationResult也是针对一个具体的EvalItem,但它只承载最终的评估结果,具体体现在通过其Metrics属性返回的评估指标。具体的指标通过EvaluationMetric类型标识,每个EvaluationMetric对象具有一个确定的名称,这个名称就是Metrics字典的Key。
public sealed class EvaluationResult
{
public IDictionary<string, EvaluationMetric> Metrics { get; set; }
}
public class EvaluationMetric
{
public string Name { get; set; }
public string? Reason { get; set; }
public EvaluationMetricInterpretation? Interpretation { get; set; }
public IDictionary<string, EvaluationContext>? Context { get; set; }
public IList<EvaluationDiagnostic>? Diagnostics { get; set; }
public IDictionary<string, string>? Metadata { get; set; }
}
EvaluationMetric各属性说明如下:
- Name:评估指标的名称,比如"task_completion"、“tool_accuracy”、"safety"和"coherence_score"等;
- Reason: 可选的解释性文本,用于说明为什么得到这个结果,例如:“模型没有按照要求调用工具”,“输出包含敏感内容”和“评分偏低,因为回答不完整”等;
- Interpretation: 用于表达该指标的好坏/通过/失败等语义,它让指标不仅有值,还能表达是否符合预期;
- Context: 用于记录生成当前指标参考的上下文,例如Ground truth,任务描述,模型输出,工具调用信息等;
- Diagnostics: 用于记录更细粒度的诊断信息,例如哪一段文本不符合要求,哪个工具调用参数错误,哪个安全规则被触发等;
- Metadata: 任意字符串键值对形式的额外元数据。
如下这个泛型的EvaluationMetric<T>类型派生于EvaluationMetric,并在基类基础上定义了作为指标值的Value属性,泛型参数T作为它的类型。BooleanMetric、NumericMetric和StringMetric继承自EvaluationMetric<T>,分别采用布尔值、数组和字符串来标识指标的值。
public class EvaluationMetric<T> : EvaluationMetric
{
public T? Value { get; set; }
}
public sealed class BooleanMetric : EvaluationMetric<bool?>;
public sealed class NumericMetric : EvaluationMetric<double?>;
public sealed class StringMetric : EvaluationMetric<string>;
2.3 EvaluationMetricInterpretation
EvaluationMetric利用Interpretation返回的EvaluationMetricInterpretation对象对指标作进一步解释。用于告诉评估系统:这个指标到底是好还是坏、是否失败、为什么这样判断。它不是指标本身,而是指标的语义层解释。
public sealed class EvaluationMetricInterpretation
{
public EvaluationRating Rating { get; set; }
public bool Failed { get; set; }
public string? Reason { get; set; }
}
public enum EvaluationRating
{
Unknown,
Inconclusive,
Unacceptable,
Poor,
Average,
Good,
Exceptional
}
三个属性说明如下:
- Rating:
EvaluationRating是一个枚举,用来表达结果的质量; - Failed:用来表达这个指标是否被视为失败;
- Reason:使用文字对
Rating和Failed的赋值原因作进一步解释。
2.4 EvaluationDiagnostic
EvaluationMetric利用Diagnostics是新返回的一组EvaluationDiagnostic对象输出一些诊断信息。每个EvaluationDiagnostic对象包含由Message属性提供的文字描述,和通过Severity属性体现的诊断等级。
public sealed class EvaluationDiagnostic
{
public EvaluationDiagnosticSeverity Severity { get; set; }
public string Message { get; set; }
}
public enum EvaluationDiagnosticSeverity
{
Informational,
Warning,
Error
}
2.4 EvaluationContext
EvaluationMetric的Context返回一个IDictionary<string, EvaluationContext>类型的字典。EvaluationContext是为评估提供的额外上下文输入容器,用于携带评估所需但不在对话历史中的信息(如ground truth、规则、工具规范、参考答案等)。它是一个抽象基类,要求派生类必须把所有上下文内容以AIContent的形式放入Contents,因为序列化与报告系统只依赖Contents。每个EvaluationContext一个确切的名称,这个名称就是上述字典的Key。
public abstract class EvaluationContext
{
public string Name { get; set; }
public IList<AIContent> Contents { get; set; }
}
public sealed class CompletenessEvaluatorContext : EvaluationContext
{
public static string GroundTruthContextName => "Ground Truth (Completeness)";
public string GroundTruth { get; }
}
public sealed class EquivalenceEvaluatorContext : EvaluationContext
{
public static string GroundTruthContextName => "Ground Truth (Equivalence)";
public string GroundTruth { get; }
}
public sealed class GroundednessEvaluatorContext : EvaluationContext
{
public static string GroundingContextName => "Grounding Context (Groundedness)";
public string GroundingContext { get; }
}
public sealed class IntentResolutionEvaluatorContext : EvaluationContext
{
public static string ToolDefinitionsContextName => "Tool Definitions (Intent Resolution)";
public IReadOnlyList<AITool> ToolDefinitions { get; }
}
public sealed class RetrievalEvaluatorContext : EvaluationContext
{
public static string RetrievedContextChunksContextName => "Retrieved Context Chunks (Retrieval)";
public IReadOnlyList<string> RetrievedContextChunks { get; }
}
public sealed class ToolCallAccuracyEvaluatorContext : EvaluationContext
{
public static string ToolDefinitionsContextName => "Tool Definitions (Tool Call Accuracy)";
public IReadOnlyList<AITool> ToolDefinitions { get; }
}
MEAI为EvaluationContext定义了如下这几个派生类:
ReferenceContext —— 提供正确答案(Ground Truth)
用于参考型评估(reference-based evaluation),它通常包含:
- 正确答案文本
- 多个参考答案(可选)
- 关键事实列表
评估器用它来:
- 比较模型输出与正确答案
- 做语义相似度
- 判断是否答对
RubricContext —— 提供评分标准(Rubric)
用于规则型评估(rule-based evaluation),包含:
- 必须满足的规则
- 必须覆盖的要点
- 写作规范
- 评分维度说明
评估器用它来:
- 检查模型是否遵守规则
- 判断是否覆盖关键点
- 给出 Good/Bad/Failed等解释
TaskSpecContext —— 提供任务要求(Task Specification)
用于任务完成度评估(task completion evaluation),它包含:
- 输出格式要求(如 JSON schema)
- 必须包含的字段
- 输出风格要求
- 任务目标描述
评估器用它来:
- 判断模型是否完成任务
- 检查格式是否正确
- 检查字段是否齐全
ToolSchemaContext —— 提供工具调用规范(Tool Schema)
用于工具调用准确性评估(tool-call accuracy evaluation),它包含:
- 工具名称
- 参数类型
- 必填参数
- 参数约束(如枚举、范围)
评估器用它来:
- 判断模型是否调用正确工具
- 参数是否正确
- 是否遗漏必填字段
SafetyRulesContext —— 提供安全规则(Safety Rules)
用于安全评估(safety evaluation)包含:
- 不得出现的内容(暴力、仇恨、色情等)
- 敏感主题列表
- 风险等级定义
评估器用它来:
- 检查模型是否违反安全规则
- 标记风险等级
- 给出失败原因
RagContext —— 提供检索文档(RAG Passages)
用于RAG相关评估(retrieval-based evaluation), 它包含:
- 检索到的文档片段
- 文档来源(URL、ID)
- 置信度分数(可选)
评估器用它来:
- 判断模型是否正确引用检索内容
- 是否幻觉(hallucination)
- 是否遗漏关键事实


被折叠的 条评论
为什么被折叠?



