Skip to content

Commit 223e087

Browse files
Update Quality and Safety evaluators that use context to record the context used in the metrics they produce
1 parent a9d3710 commit 223e087

File tree

5 files changed

+101
-28
lines changed

5 files changed

+101
-28
lines changed

src/Libraries/Microsoft.Extensions.AI.Evaluation.Quality/ChatConversationEvaluator.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
54
using System.Collections.Generic;
65
using System.Linq;
76
using System.Text;
@@ -35,7 +34,7 @@ public abstract class ChatConversationEvaluator : IEvaluator
3534
protected virtual string? SystemPrompt => null;
3635

3736
/// <inheritdoc/>
38-
public async ValueTask<EvaluationResult> EvaluateAsync(
37+
public virtual async ValueTask<EvaluationResult> EvaluateAsync(
3938
IEnumerable<ChatMessage> messages,
4039
ChatResponse modelResponse,
4140
ChatConfiguration? chatConfiguration = null,

src/Libraries/Microsoft.Extensions.AI.Evaluation.Quality/EquivalenceEvaluator.cs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,28 @@ public sealed class EquivalenceEvaluator : SingleNumericMetricEvaluator
4949
/// <inheritdoc/>
5050
protected override bool IgnoresHistory => true;
5151

52+
/// <inheritdoc/>
53+
public override async ValueTask<EvaluationResult> EvaluateAsync(
54+
IEnumerable<ChatMessage> messages,
55+
ChatResponse modelResponse,
56+
ChatConfiguration? chatConfiguration = null,
57+
IEnumerable<EvaluationContext>? additionalContext = null,
58+
CancellationToken cancellationToken = default)
59+
{
60+
EvaluationResult result =
61+
await base.EvaluateAsync(
62+
messages,
63+
modelResponse,
64+
chatConfiguration,
65+
additionalContext,
66+
cancellationToken).ConfigureAwait(false);
67+
68+
EquivalenceEvaluatorContext context = GetRelevantContext(additionalContext);
69+
result.AddOrUpdateContextInAllMetrics("Ground Truth", context.GetContents());
70+
71+
return result;
72+
}
73+
5274
/// <inheritdoc/>
5375
protected override async ValueTask<string> RenderEvaluationPromptAsync(
5476
ChatMessage? userRequest,
@@ -66,18 +88,8 @@ userRequest is not null
6688
? await RenderAsync(userRequest, cancellationToken).ConfigureAwait(false)
6789
: string.Empty;
6890

69-
string groundTruth;
70-
71-
if (additionalContext?.OfType<EquivalenceEvaluatorContext>().FirstOrDefault()
72-
is EquivalenceEvaluatorContext context)
73-
{
74-
groundTruth = context.GroundTruth;
75-
}
76-
else
77-
{
78-
throw new InvalidOperationException(
79-
$"A value of type '{nameof(EquivalenceEvaluatorContext)}' was not found in the '{nameof(additionalContext)}' collection.");
80-
}
91+
EquivalenceEvaluatorContext context = GetRelevantContext(additionalContext);
92+
string groundTruth = context.GroundTruth;
8193

8294
string prompt =
8395
$$"""
@@ -149,4 +161,16 @@ alleviating stress and augmenting general mood.
149161

150162
return prompt;
151163
}
164+
165+
private static EquivalenceEvaluatorContext GetRelevantContext(IEnumerable<EvaluationContext>? additionalContext)
166+
{
167+
if (additionalContext?.OfType<EquivalenceEvaluatorContext>().FirstOrDefault()
168+
is EquivalenceEvaluatorContext context)
169+
{
170+
return context;
171+
}
172+
173+
throw new InvalidOperationException(
174+
$"A value of type '{nameof(EquivalenceEvaluatorContext)}' was not found in the '{nameof(additionalContext)}' collection.");
175+
}
152176
}

src/Libraries/Microsoft.Extensions.AI.Evaluation.Quality/GroundednessEvaluator.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,30 @@ public sealed class GroundednessEvaluator : SingleNumericMetricEvaluator
4949
/// <inheritdoc/>
5050
protected override bool IgnoresHistory => false;
5151

52+
/// <inheritdoc/>
53+
public override async ValueTask<EvaluationResult> EvaluateAsync(
54+
IEnumerable<ChatMessage> messages,
55+
ChatResponse modelResponse,
56+
ChatConfiguration? chatConfiguration = null,
57+
IEnumerable<EvaluationContext>? additionalContext = null,
58+
CancellationToken cancellationToken = default)
59+
{
60+
EvaluationResult result =
61+
await base.EvaluateAsync(
62+
messages,
63+
modelResponse,
64+
chatConfiguration,
65+
additionalContext,
66+
cancellationToken).ConfigureAwait(false);
67+
68+
if (GetRelevantContext(additionalContext) is GroundednessEvaluatorContext context)
69+
{
70+
result.AddOrUpdateContextInAllMetrics("Grounding Context", context.GetContents());
71+
}
72+
73+
return result;
74+
}
75+
5276
/// <inheritdoc/>
5377
protected override async ValueTask<string> RenderEvaluationPromptAsync(
5478
ChatMessage? userRequest,
@@ -68,8 +92,7 @@ userRequest is not null
6892

6993
var builder = new StringBuilder();
7094

71-
if (additionalContext?.OfType<GroundednessEvaluatorContext>().FirstOrDefault()
72-
is GroundednessEvaluatorContext context)
95+
if (GetRelevantContext(additionalContext) is GroundednessEvaluatorContext context)
7396
{
7497
_ = builder.Append(context.GroundingContext);
7598
_ = builder.AppendLine();
@@ -162,4 +185,15 @@ is not French.
162185

163186
return prompt;
164187
}
188+
189+
private static GroundednessEvaluatorContext? GetRelevantContext(IEnumerable<EvaluationContext>? additionalContext)
190+
{
191+
if (additionalContext?.OfType<GroundednessEvaluatorContext>().FirstOrDefault()
192+
is GroundednessEvaluatorContext context)
193+
{
194+
return context;
195+
}
196+
197+
return null;
198+
}
165199
}

src/Libraries/Microsoft.Extensions.AI.Evaluation.Safety/GroundednessProEvaluator.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,30 @@ await EvaluateContentSafetyAsync(
6363
contentSafetyServicePayloadFormat: ContentSafetyServicePayloadFormat.QuestionAnswer.ToString(),
6464
cancellationToken: cancellationToken).ConfigureAwait(false);
6565

66+
GroundednessProEvaluatorContext context = GetRelevantContext(additionalContext);
67+
result.AddOrUpdateContextInAllMetrics("Grounding Context", context.GetContents());
68+
6669
return result;
6770
}
6871

6972
/// <inheritdoc/>
7073
protected override IReadOnlyList<EvaluationContext>? FilterAdditionalContext(
7174
IEnumerable<EvaluationContext>? additionalContext)
75+
{
76+
GroundednessProEvaluatorContext context = GetRelevantContext(additionalContext);
77+
return [context];
78+
}
79+
80+
private static GroundednessProEvaluatorContext GetRelevantContext(
81+
IEnumerable<EvaluationContext>? additionalContext)
7282
{
7383
if (additionalContext?.OfType<GroundednessProEvaluatorContext>().FirstOrDefault()
7484
is GroundednessProEvaluatorContext context)
7585
{
76-
return [context];
77-
}
78-
else
79-
{
80-
throw new InvalidOperationException(
81-
$"A value of type '{nameof(GroundednessProEvaluatorContext)}' was not found in the '{nameof(additionalContext)}' collection.");
86+
return context;
8287
}
88+
89+
throw new InvalidOperationException(
90+
$"A value of type '{nameof(GroundednessProEvaluatorContext)}' was not found in the '{nameof(additionalContext)}' collection.");
8391
}
8492
}

src/Libraries/Microsoft.Extensions.AI.Evaluation.Safety/UngroundedAttributesEvaluator.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,22 +67,30 @@ await EvaluateContentSafetyAsync(
6767
contentSafetyServicePayloadFormat: ContentSafetyServicePayloadFormat.QueryResponse.ToString(),
6868
cancellationToken: cancellationToken).ConfigureAwait(false);
6969

70+
UngroundedAttributesEvaluatorContext context = GetRelevantContext(additionalContext);
71+
result.AddOrUpdateContextInAllMetrics("Grounding Context", context.GetContents());
72+
7073
return result;
7174
}
7275

7376
/// <inheritdoc/>
7477
protected override IReadOnlyList<EvaluationContext>? FilterAdditionalContext(
7578
IEnumerable<EvaluationContext>? additionalContext)
79+
{
80+
UngroundedAttributesEvaluatorContext context = GetRelevantContext(additionalContext);
81+
return [context];
82+
}
83+
84+
private static UngroundedAttributesEvaluatorContext GetRelevantContext(
85+
IEnumerable<EvaluationContext>? additionalContext)
7686
{
7787
if (additionalContext?.OfType<UngroundedAttributesEvaluatorContext>().FirstOrDefault()
7888
is UngroundedAttributesEvaluatorContext context)
7989
{
80-
return [context];
81-
}
82-
else
83-
{
84-
throw new InvalidOperationException(
85-
$"A value of type '{nameof(UngroundedAttributesEvaluatorContext)}' was not found in the '{nameof(additionalContext)}' collection.");
90+
return context;
8691
}
92+
93+
throw new InvalidOperationException(
94+
$"A value of type '{nameof(UngroundedAttributesEvaluatorContext)}' was not found in the '{nameof(additionalContext)}' collection.");
8795
}
8896
}

0 commit comments

Comments
 (0)