Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu öğreticide, OpenAI modelinin sohbet yanıtını değerlendirmek için bir MSTest uygulaması oluşturacaksınız. Test uygulaması değerlendirmeleri gerçekleştirmek, model yanıtlarını önbelleğe almak ve raporlar oluşturmak için Microsoft.Extensions.AI.Evaluation kitaplıklarını kullanır. Öğreticide hem yerleşik hem de özel değerlendiriciler kullanılır. Yerleşik kalite değerlendiricileri (Microsoft. Extensions.AI.Evaluation.Quality package) değerlendirmeleri gerçekleştirmek için LLM kullanır; özel değerlendirici yapay zeka kullanmaz.
Önkoşullar
- .NET 8 veya sonraki bir sürüm
- Visual Studio Code (isteğe bağlı)
Yapay zeka hizmetini yapılandırma
Azure portalını kullanarak bir Azure OpenAI service ve model sağlamak için Azure OpenAI Service kaynağı oluşturma ve dağıtma makalesindeki adımları tamamlayın. "Model dağıtma" adımında gpt-5 modeli seçin.
Test uygulamasını oluşturma
Bir yapay zeka modeline bağlanan bir MSTest projesi oluşturmak için aşağıdaki adımları tamamlayın.
Terminal penceresinde, uygulamanızı oluşturmak istediğiniz dizine gidin ve komutuyla
dotnet newyeni bir MSTest uygulaması oluşturun:dotnet new mstest -o TestAIWithReportingTestAIWithReportingdizinine gidin ve gerekli paketleri uygulamanıza ekleyin:dotnet add package Azure.AI.OpenAI dotnet add package Azure.Identity dotnet add package Microsoft.Extensions.AI.Abstractions dotnet add package Microsoft.Extensions.AI.Evaluation dotnet add package Microsoft.Extensions.AI.Evaluation.Quality dotnet add package Microsoft.Extensions.AI.Evaluation.Reporting dotnet add package Microsoft.Extensions.AI.OpenAI dotnet add package Microsoft.Extensions.Configuration dotnet add package Microsoft.Extensions.Configuration.UserSecretsAzure OpenAI uç noktanız ve kiracı kimliğiniz için uygulama gizli anahtarları eklemek üzere aşağıdaki komutları çalıştırın:
dotnet user-secrets init dotnet user-secrets set AZURE_OPENAI_ENDPOINT <your-Azure-OpenAI-endpoint> dotnet user-secrets set AZURE_TENANT_ID <your-tenant-ID>Ortamınıza bağlı olarak kiracı kimliği gerekli olmayabilir. Bu durumda, onu DefaultAzureCredential kodundan kaldırın.
Yeni uygulamayı kendi seçtiğiniz düzenleyicide açın.
Test uygulaması kodunu ekleme
Test1.cs dosyasını MyTests.cs olarak yeniden adlandırın, sonra dosyayı açın ve sınıfını olarak
MyTestsyeniden adlandırın. BoşTestMethod1yöntemi silin.Dosyanın en üstüne gerekli
usingyönergeleri ekleyin.using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Extensions.AI.Evaluation; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.AI.Evaluation.Reporting.Storage; using Microsoft.Extensions.AI.Evaluation.Reporting; using Microsoft.Extensions.AI.Evaluation.Quality;TestContext özelliğini sınıfına ekleyin.
// The value of the TestContext property is populated by MSTest. public TestContext? TestContext { get; set; }GetAzureOpenAIChatConfigurationyöntemini ekleyin, bu yöntem değerlendirme aracının modelle iletişim kurmasını sağlayan IChatClient'yi oluşturur.private static ChatConfiguration GetAzureOpenAIChatConfiguration() { IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets<MyTests>().Build(); string endpoint = config["AZURE_OPENAI_ENDPOINT"]; string tenantId = config["AZURE_TENANT_ID"]; string model = "gpt-5"; // Get an instance of Microsoft.Extensions.AI's <see cref="IChatClient"/> // interface for the selected LLM endpoint. AzureOpenAIClient azureClient = new( new Uri(endpoint), new DefaultAzureCredential(new DefaultAzureCredentialOptions() { TenantId = tenantId })); IChatClient client = azureClient.GetChatClient(deploymentName: model).AsIChatClient(); // Create an instance of <see cref="ChatConfiguration"/> // to communicate with the LLM. return new ChatConfiguration(client); }Raporlama işlevini ayarlayın.
private string ScenarioName => $"{TestContext!.FullyQualifiedTestClassName}.{TestContext.TestName}"; private static string ExecutionName => $"{DateTime.Now:yyyyMMddTHHmmss}"; private static readonly ReportingConfiguration s_defaultReportingConfiguration = DiskBasedReportingConfiguration.Create( storageRootPath: "C:\\TestReports", evaluators: GetEvaluators(), chatConfiguration: GetAzureOpenAIChatConfiguration(), enableResponseCaching: true, executionName: ExecutionName);Senaryo adı
Senaryo adı, geçerli test yönteminin tam adı olarak ayarlanır. Ancak, CreateScenarioRunAsync(String, String, IEnumerable<String>, IEnumerable<String>, CancellationToken) çağırdığınızda bunu herhangi bir dizeye ayarlayabilirsiniz. Bir senaryo adı seçerken şu faktörleri göz önünde bulundurun:
- Disk tabanlı depolama kullanılırken senaryo adı, ilgili değerlendirme sonuçlarının depolandığı klasörün adı olarak kullanılır. Bu nedenle, adın oldukça kısa tutulması ve dosya ve dizin adlarında izin verilmeyen karakterlerden kaçınılması iyi bir fikirdir.
- Varsayılan olarak, oluşturulan değerlendirme raporu senaryo adlarını üzerine
.bölerek sonuçların uygun gruplandırma, iç içe yerleştirme ve toplama ile hiyerarşik bir görünümde görüntülenmesini sağlar. Hiyerarşik görünüm, sonuçları hiyerarşideki ad alanlarına ve sınıf adlarına göre gruplandırdığından, senaryo adı ilgili test yönteminin tam adı olduğunda özellikle yararlıdır. Ancak, senaryolarınız için en uygun raporlama hiyerarşisini oluşturmak için kendi özel senaryo adlarınıza nokta (.) ekleyerek de bu özelliğin avantajlarından yararlanabilirsiniz.
Yürütme adı
Yürütme adı, değerlendirme sonuçları depolandığında aynı değerlendirme çalıştırmasının (veya test çalıştırmasının) parçası olan değerlendirme sonuçlarını gruplandırmak için kullanılır. ReportingConfiguration oluştururken bir yürütme adı sağlamazsanız, tüm değerlendirme çalıştırmaları varsayılan bir yürütme adı olan
Defaultkullanır. Bu durumda, bir çalıştırmadaki sonuçların üzerine bir sonraki çalıştırma yazılır ve farklı çalıştırmaların sonuçlarını karşılaştırma yeteneğinizi kaybedersiniz.Bu örnekte yürütme adı olarak bir zaman damgası kullanılır. Projenizde birden fazla test varsa, testlerde kullanılan tüm raporlama yapılandırmalarında aynı yürütme adını kullanarak sonuçların doğru gruplandığından emin olun.
Daha gerçekçi bir senaryoda, farklı derlemelere yayılan ve farklı test işlemlerinde yürütülen değerlendirme amaçlı testlerde aynı yürütme adını paylaşmak isteyebilirsiniz. Bu gibi durumlarda, testleri çalıştırmadan önce ortam değişkenini uygun bir yürütme adıyla (CI/CD sisteminiz tarafından atanan geçerli derleme numarası gibi) güncelleştirmek için bir betik kullanabilirsiniz. Ya da derleme sisteminiz monoton olarak artan derleme dosyası sürümleri oluşturuyorsa,
dosyasını test kodunun içinden okuyabilir ve bunu farklı ürün sürümleri arasında sonuçları karşılaştırmak için yürütme adı olarak kullanabilirsiniz. Raporlama yapılandırması
A ReportingConfiguration tanımlar:
- Çağrılarak ScenarioRun oluşturulan her CreateScenarioRunAsync(String, String, IEnumerable<String>, IEnumerable<String>, CancellationToken) için çağrılması gereken değerlendirici kümesi.
- Değerlendiricilerin kullanması gereken LLM uç noktası (bkz ReportingConfiguration.ChatConfiguration. ).
- Senaryo çalıştırma sonuçlarının nasıl ve nerede depolanması gerekir.
- Senaryo çalıştırmalarıyla ilgili LLM yanıtlarının nasıl önbelleğe alınacağı belirlenmelidir.
- Sonuçların raporlanması sırasında kullanılacak senaryo çalıştırma adı.
Bu test disk tabanlı raporlama yapılandırması kullanır.
Ayrı bir dosyada,
WordCountEvaluatoröğesini uygulayan özel bir değerlendirici olan IEvaluator sınıfını ekleyin.using System.Text.RegularExpressions; using Microsoft.Extensions.AI; using Microsoft.Extensions.AI.Evaluation; namespace TestAIWithReporting; public class WordCountEvaluator : IEvaluator { public const string WordCountMetricName = "Words"; public IReadOnlyCollection<string> EvaluationMetricNames => [WordCountMetricName]; /// <summary> /// Counts the number of words in the supplied string. /// </summary> private static int CountWords(string? input) { if (string.IsNullOrWhiteSpace(input)) { return 0; } MatchCollection matches = Regex.Matches(input, @"\b\w+\b"); return matches.Count; } /// <summary> /// Provides a default interpretation for the supplied <paramref name="metric"/>. /// </summary> private static void Interpret(NumericMetric metric) { if (metric.Value is null) { metric.Interpretation = new EvaluationMetricInterpretation( EvaluationRating.Unknown, failed: true, reason: "Failed to calculate word count for the response."); } else { if (metric.Value <= 100 && metric.Value > 5) metric.Interpretation = new EvaluationMetricInterpretation( EvaluationRating.Good, reason: "The response was between 6 and 100 words."); else metric.Interpretation = new EvaluationMetricInterpretation( EvaluationRating.Unacceptable, failed: true, reason: "The response was either too short or greater than 100 words."); } } public ValueTask<EvaluationResult> EvaluateAsync( IEnumerable<ChatMessage> messages, ChatResponse modelResponse, ChatConfiguration? chatConfiguration = null, IEnumerable<EvaluationContext>? additionalContext = null, CancellationToken cancellationToken = default) { // Count the number of words in the supplied <see cref="modelResponse"/>. int wordCount = CountWords(modelResponse.Text); string reason = $"This {WordCountMetricName} metric has a value of {wordCount} because " + $"the evaluated model response contained {wordCount} words."; // Create a <see cref="NumericMetric"/> with value set to the word count. // Include a reason that explains the score. var metric = new NumericMetric(WordCountMetricName, value: wordCount, reason); // Attach a default <see cref="EvaluationMetricInterpretation"/> for the metric. Interpret(metric); return new ValueTask<EvaluationResult>(new EvaluationResult(metric)); } },
WordCountEvaluatoryanıtta bulunan sözcük sayısını sayar. Bazı değerlendiricilerden farklı olarak yapay zekaya dayalı değildir.EvaluateAsyncyöntemi, sözcük sayısını içeren bir NumericMetric barındıran bir EvaluationResult döndürür.yöntemi ayrıca
EvaluateAsyncölçüme varsayılan bir yorum ekler. Algılanan sözcük sayısı 6 ile 100 arasındaysa, varsayılan yorum ölçümü iyi (kabul edilebilir) olarak kabul eder. Aksi takdirde ölçüm başarısız olarak kabul edilir. Arayan gerekirse bu varsayılan yorumu geçersiz kılabilir.geri dön
MyTests.csiçinde, değerlendirmede kullanılacak değerlendiricileri toplamak için bir fonksiyon ekleyin.private static IEnumerable<IEvaluator> GetEvaluators() { IEvaluator relevanceEvaluator = new RelevanceEvaluator(); IEvaluator coherenceEvaluator = new CoherenceEvaluator(); IEvaluator wordCountEvaluator = new WordCountEvaluator(); return [relevanceEvaluator, coherenceEvaluator, wordCountEvaluator]; }Sistem istemi ChatMessageeklemek için bir yöntem ekleyin, sohbet seçeneklerini tanımlayın ve modelden belirli bir soruya yanıt isteyin.
private static async Task<(IList<ChatMessage> Messages, ChatResponse ModelResponse)> GetAstronomyConversationAsync( IChatClient chatClient, string astronomyQuestion) { const string SystemPrompt = """ You're an AI assistant that can answer questions related to astronomy. Keep your responses concise and under 100 words. Use the imperial measurement system for all measurements in your response. """; IList<ChatMessage> messages = [ new ChatMessage(ChatRole.System, SystemPrompt), new ChatMessage(ChatRole.User, astronomyQuestion) ]; var chatOptions = new ChatOptions { Temperature = 0.0f, ResponseFormat = ChatResponseFormat.Text }; ChatResponse response = await chatClient.GetResponseAsync(messages, chatOptions); return (messages, response); }Bu öğreticideki test, LLM'nin bir astronomi sorusuna verdiği yanıtı değerlendirir. ReportingConfiguration yanıt önbelleği etkinleştirildiği için ve sağlanan IChatClient, bu raporlama yapılandırması kullanılarak oluşturulan ScenarioRun'den her zaman getirildiği için, test için LLM yanıtı önbelleğe alınır ve tekrar kullanılır. Yanıt, karşılık gelen önbellek girdisinin süresi dolana kadar (varsayılan olarak 14 gün içinde) veya LLM uç noktası veya sorulan soru gibi herhangi bir istek parametresi değişene kadar yeniden kullanılır.
Yanıtı doğrulamak için bir yöntem ekleyin.
/// <summary> /// Runs basic validation on the supplied <see cref="EvaluationResult"/>. /// </summary> private static void Validate(EvaluationResult result) { // Retrieve the score for relevance from the <see cref="EvaluationResult"/>. NumericMetric relevance = result.Get<NumericMetric>(RelevanceEvaluator.RelevanceMetricName); Assert.IsFalse(relevance.Interpretation!.Failed, relevance.Reason); Assert.IsTrue(relevance.Interpretation.Rating is EvaluationRating.Good or EvaluationRating.Exceptional); // Retrieve the score for coherence from the <see cref="EvaluationResult"/>. NumericMetric coherence = result.Get<NumericMetric>(CoherenceEvaluator.CoherenceMetricName); Assert.IsFalse(coherence.Interpretation!.Failed, coherence.Reason); Assert.IsTrue(coherence.Interpretation.Rating is EvaluationRating.Good or EvaluationRating.Exceptional); // Retrieve the word count from the <see cref="EvaluationResult"/>. NumericMetric wordCount = result.Get<NumericMetric>(WordCountEvaluator.WordCountMetricName); Assert.IsFalse(wordCount.Interpretation!.Failed, wordCount.Reason); Assert.IsTrue(wordCount.Interpretation.Rating is EvaluationRating.Good or EvaluationRating.Exceptional); Assert.IsFalse(wordCount.ContainsDiagnostics()); Assert.IsTrue(wordCount.Value > 5 && wordCount.Value <= 100); }Tip
Ölçümlerin her birinde puanın nedenini açıklayan bir
Reasonözellik bulunur. Bunun nedeni oluşturulan rapora dahil edilir ve ilgili ölçümün kartındaki bilgi simgesine tıklayarak görüntülenebilir.Son olarak test yönteminin kendisini ekleyin.
[TestMethod] public async Task SampleAndEvaluateResponse() { // Create a <see cref="ScenarioRun"/> with the scenario name // set to the fully qualified name of the current test method. await using ScenarioRun scenarioRun = await s_defaultReportingConfiguration.CreateScenarioRunAsync( ScenarioName, additionalTags: ["Moon"]); // Use the <see cref="IChatClient"/> that's included in the // <see cref="ScenarioRun.ChatConfiguration"/> to get the LLM response. (IList<ChatMessage> messages, ChatResponse modelResponse) = await GetAstronomyConversationAsync( chatClient: scenarioRun.ChatConfiguration!.ChatClient, astronomyQuestion: "How far is the Moon from the Earth at its closest and furthest points?"); // Run the evaluators configured in <see cref="s_defaultReportingConfiguration"/> against the response. EvaluationResult result = await scenarioRun.EvaluateAsync(messages, modelResponse); // Run some basic validation on the evaluation result. Validate(result); }Bu test yöntemi:
Oluşturur ScenarioRun.
await usingdeğerlendirme sonuçlarınınScenarioRundoğru şekilde işlenmesini ve sonuç deposuna kalıcı olarak kaydedilmesini sağlar.LLM'nin belirli bir astronomi sorusuna verdiği yanıtı alır. Test, değerlendirilmekte olan birincil LLM IChatClient yöntemine değerlendirme
GetAstronomyConversationAsynciçin kullanılanın aynısını geçirir. Aynı istemcinin kullanılması, değerlendiricilerin dahili değerlendirmeleri sırasında kullandıkları dönüşler için LLM yanıtıyla sağlanan yanıt önbelleğe almayı da etkinleştirir. Yanıt önbelleğe alma işlemiyle LLM yanıtı aşağıdaki yöntemlerle alınır:- Geçerli testin ilk çalıştırmasında doğrudan LLM uç noktasından veya önbelleğe alınan girişin süresi dolduysa sonraki çalıştırmalarda (varsayılan olarak 14 gün).
- Testin sonraki çalıştırmalarında,
s_defaultReportingConfiguration'de yapılandırılan disk tabanlı yanıt önbelleğinden.
Değerlendiricileri yanıt üzerinde çalıştırır. LLM yanıtı gibi, sonraki çalıştırmalar da içinde
s_defaultReportingConfigurationyapılandırılan (disk tabanlı) yanıt önbelleğinden değerlendirmeyi getirir.Değerlendirme sonucu üzerinde bazı temel doğrulamalar çalıştırır.
Bu adım isteğe bağlıdır ve çoğunlukla tanıtım amaçlıdır. Gerçek dünyadaki değerlendirmelerde, ürününüz (ve kullanılan modeller) geliştikçe LLM yanıtları ve değerlendirme puanları zaman içinde değişebileceğinden sonuçları tek tek doğrulamak istemeyebilirsiniz. Sonuçlar değiştiğinde tek tek değerlendirme testlerinin "başarısız olmasını" ve CI/CD işlem hatlarınızda derlemeleri engellemesini istemeyebilirsiniz. Bunun yerine, oluşturulan rapora güvenmek ve zaman içinde farklı senaryolarda değerlendirme puanlarının genel eğilimlerini izlemek daha iyi olabilir (ve yalnızca birden çok farklı testte değerlendirme puanlarında önemli bir düşüş olduğunda tek tek derlemelerde başarısız olabilir). Burada bazı nüanslar bulunmaktadır ve bireysel sonuçların doğrulanıp doğrulanmaması kararı, belirli kullanım senaryosuna bağlı olarak değişebilir.
Yöntem döndüğünde
scenarioRunnesnesi yok edilir ve değerlendirme sonucu,s_defaultReportingConfiguration'de yapılandırılan (disk tabanlı) sonuç deposunda saklanır.
Testi/değerlendirmeyi çalıştırma
Örneğin CLI komutunu dotnet test kullanarak veya Test Gezgini aracılığıyla tercih ettiğiniz test iş akışını kullanarak testi çalıştırın.
Rapor oluşturma
Microsoft.Extensions.AI.Evaluation.Console .NET aracını yüklemek için, bir terminal penceresinden aşağıdaki komutu çalıştırın:
dotnet tool install --create-manifest-if-needed Microsoft.Extensions.AI.Evaluation.ConsoleAşağıdaki komutu çalıştırarak bir rapor oluşturun:
dotnet tool run aieval report --path <path\to\your\cache\storage> --output report.htmlreport.htmldosyasını açın. Rapor aşağıdaki ekran görüntüsüne benzer.
Sonraki Adımlar
- Test sonuçlarının depolandığı dizine gidin (varsayılan konum
C:\TestReports, ancak ReportingConfiguration öğesini oluşturduğunuzda konumu değiştirmediyseniz). Alt dizinresultsiçerisinde, her test çalıştırması için zaman damgasıExecutionNameile adlandırılmış bir klasör olduğuna dikkat edin. Bu klasörlerin her birinin içinde her senaryo adı için bir klasör bulunur; bu örnekte projedeki tek bir test yöntemidir. Bu klasör iletiler, yanıt ve değerlendirme sonucu dahil olmak üzere tüm verileri içeren bir JSON dosyası içerir. - Değerlendirmeyi genişletin. İşte birkaç fikir:
- Yanıtta kullanılan ölçüm sistemini belirlemek için yapay zeka kullanan bir değerlendirici gibi başka bir özel değerlendirici ekleyin.
- LlM'den birden çok yanıtı değerlendiren bir yöntem gibi başka bir test yöntemi ekleyin. Her yanıt farklı olabileceği için, bir soruya en az birkaç yanıt örneklemek ve değerlendirmek iyi olur. Bu durumda, her çağrı yaptığınızda CreateScenarioRunAsync(String, String, IEnumerable<String>, IEnumerable<String>, CancellationToken) bir yineleme adı belirtirsiniz.