Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ebben az oktatóanyagban egy MSTest-alkalmazást hoz létre egy OpenAI-modell csevegési válaszának kiértékeléséhez. A tesztalkalmazás a Microsoft.Extensions.AI.Evaluation kódtárakat használja az értékelések elvégzéséhez, a modellválaszok gyorsítótárazásához és jelentések létrehozásához. Az oktatóanyag beépített és egyéni kiértékelőket is használ. A beépített minőségi kiértékelők (a Microsoft.Extensions.AI.Evaluation.Quality csomag) LLM-et használnak a kiértékelések végrehajtásához; az egyéni kiértékelő nem alkalmaz mesterséges intelligenciát.
Előfeltételek
- .NET 8-es vagy újabb verzió
- Visual Studio Code (nem kötelező)
Az AI-szolgáltatás konfigurálása
Ha Azure OpenAI service és modellt szeretne kiépíteni a Azure portálon, végezze el a A Azure OpenAI Service erőforrás létrehozása és üzembe helyezése cikkben leírt lépéseket. A "Modell üzembe helyezése" lépésben válassza ki a gpt-5 modellt.
A tesztalkalmazás létrehozása
Hajtsa végre az alábbi lépéseket egy AI-modellhez csatlakozó MSTest-projekt létrehozásához.
Egy terminálablakban lépjen arra a könyvtárra, ahol létre szeretné hozni az alkalmazást, és hozzon létre egy új MSTest-alkalmazást a
dotnet newkövetkező paranccsal:dotnet new mstest -o TestAIWithReportingLépjen a
TestAIWithReportingkönyvtárra, és adja hozzá a szükséges csomagokat az alkalmazáshoz: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.UserSecretsFuttassa a következő parancsokat alkalmazás titkos kulcsainak hozzáadásához a Azure OpenAI-végponthoz és a bérlőazonosítóhoz:
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>(A környezettől függően előfordulhat, hogy a bérlőazonosítóra nincs szükség. Ebben az esetben távolítsa el a DefaultAzureCredential példányt a jelölő kódból.)
Nyissa meg az új alkalmazást a választott szerkesztőben.
A tesztalkalmazás kódjának hozzáadása
Nevezze át a Test1.cs fájlt MyTests.cs, majd nyissa meg a fájlt, és nevezze át az osztályt a következőre
MyTests: . Törölje az üresTestMethod1metódust.Adja hozzá a szükséges
usingirányelveket a fájl elejéhez.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;Adja hozzá a TestContext tulajdonságot az osztályhoz.
// The value of the TestContext property is populated by MSTest. public TestContext? TestContext { get; set; }Adja hozzá a
GetAzureOpenAIChatConfigurationmetódust, amely létrehozza azt, IChatClient amelyet az értékelő a modellel való kommunikációhoz használ.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); }A jelentéskészítési funkció beállítása.
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);Forgatókönyv neve
A forgatókönyv neve az aktuális tesztmetódus teljes neve. A híváskor azonban bármilyen karakterláncra beállíthatja CreateScenarioRunAsync(String, String, IEnumerable<String>, IEnumerable<String>, CancellationToken). A forgatókönyv nevének kiválasztásakor vegye figyelembe ezeket a tényezőket:
- Lemezalapú tároló használata esetén a forgatókönyv neve annak a mappának a neve, amelyben a megfelelő kiértékelési eredményeket tárolja. Ezért érdemes a nevet viszonylag röviden tartani, és elkerülni a fájl- és könyvtárnevekben nem engedélyezett karaktereket.
- A létrehozott kiértékelési jelentés alapértelmezés szerint felosztja a forgatókönyvek nevét
., így az eredmények hierarchikus nézetben jelennek meg, megfelelő csoportosítással, beágyazással és összesítéssel. A hierarchikus nézet különösen akkor hasznos, ha a forgatókönyv neve a megfelelő tesztmetódus teljes neve, mert névterek és osztálynevek szerint csoportosítja az eredményeket a hierarchiában. Ezt a funkciót azonban kihasználhatja úgy is, hogy pontokat (.) a saját egyéni forgatókönyvek neveibe is beiktatva létrehoz egy olyan jelentési hierarchiát, amely a legjobban megfelel a forgatókönyveinek.
Végrehajtási név
A végrehajtási név arra szolgál, hogy csoportosítsa a kiértékelési eredményeket, amelyek ugyanahhoz a próbafuttatáshoz (vagy tesztfuttatáshoz) tartoznak a kiértékelési eredmények tárolásakor. Ha nem ad meg végrehajtási nevet a ReportingConfigurationlétrehozáskor, az összes kiértékelési futtatás ugyanazt az alapértelmezett végrehajtási nevet
Defaulthasználja. Ebben az esetben az egyik futtatás eredményeit felülírja a következő, és nem tudja összehasonlítani az eredményeket a különböző futtatások között.Ez a példa egy időbélyeget használ a végrehajtás neveként. Ha egynél több teszt van a projektben, győződjön meg arról, hogy az eredmények megfelelően vannak csoportosítva ugyanazzal a végrehajtási névvel a tesztek során használt összes jelentéskonfigurációban.
Egy valósabb forgatókönyvben érdemes lehet ugyanazt a végrehajtási nevet megosztani a különböző szerelvényekben található és különböző tesztfolyamatokban végzett értékelési tesztek között. Ilyen esetekben szkripttel frissíthet egy környezeti változót a megfelelő végrehajtási névvel (például a CI/CD-rendszer által hozzárendelt aktuális buildszámmal), mielőtt futtatná a teszteket. Vagy ha a buildelési rendszer monoton módon növekvő szerelvényfájl-verziókat állít elő, a tesztkódból elolvashatja a AssemblyFileVersionAttribute forrást, és végrehajtási névként használhatja az eredményeket a különböző termékverziók eredményeinek összehasonlításához.
Jelentéskészítési konfiguráció
A ReportingConfiguration azonosít:
- Azoknak a kiértékelőknek a készlete, amelyeket minden egyes ScenarioRun esetén meg kell hívni, amit a CreateScenarioRunAsync(String, String, IEnumerable<String>, IEnumerable<String>, CancellationToken) hívásával hoznak létre.
- Az LLM-végpont, amelyet a kiértékelőknek használniuk kell (lásd ReportingConfiguration.ChatConfiguration: ).
- A forgatókönyv eredményeinek tárolási módja és helye.
- A forgatókönyv futtatásához kapcsolódó LLM-válaszok gyorsítótárazása.
- A forgatókönyv eredményeinek jelentéséhez használandó végrehajtási név.
Ez a teszt lemezalapú jelentéskészítési konfigurációt használ.
Egy külön fájlban adja hozzá az
WordCountEvaluatorosztályt, amely egy egyéni kiértékelő, és a(z) IEvaluator-t implementálja.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)); } }A
WordCountEvaluatorválaszban szereplő szavak számát számítja ki. Egyes kiértékelőktől eltérően ez nem AI-n alapul. AEvaluateAsyncmetódus egy EvaluationResult olyan értéket ad vissza, amely tartalmazza NumericMetric a szavak számát.A
EvaluateAsyncmetódus egy alapértelmezett értelmezést is csatol a metrikához. Az alapértelmezett értelmezés jónak (elfogadhatónak) tekinti a metrikát, ha az észlelt szószám 6 és 100 között van. Ellenkező esetben a metrika sikertelennek minősül. A hívó szükség esetén felülbírálhatja ezt az alapértelmezett értelmezést.A
MyTests.cshelyen adjon hozzá egy metódust, amely összegyűjti a kiértékelőket, hogy felhasználásra kerüljenek az értékelés során.private static IEnumerable<IEvaluator> GetEvaluators() { IEvaluator relevanceEvaluator = new RelevanceEvaluator(); IEvaluator coherenceEvaluator = new CoherenceEvaluator(); IEvaluator wordCountEvaluator = new WordCountEvaluator(); return [relevanceEvaluator, coherenceEvaluator, wordCountEvaluator]; }Adjon hozzá egy metódust egy rendszerkérés ChatMessagehozzáadásához, határozza meg a csevegési beállításokat, és kérjen választ a modelltől egy adott kérdésre.
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); }Az oktatóanyagban szereplő teszt kiértékeli az LLM válaszát egy csillagászati kérdésre. Mivel a ReportingConfiguration válasz gyorsítótárazása engedélyezve van, és mivel a megadott IChatClient adatokat a rendszer mindig ezzel a ScenarioRun jelentéskészítési konfigurációval hozza létre, a rendszer gyorsítótárazza és újra felhasználja a teszt LLM-válaszát. A rendszer mindaddig újra felhasználja a választ, amíg a megfelelő gyorsítótárbejegyzés le nem jár (alapértelmezés szerint 14 napon belül), vagy amíg bármilyen kérésparaméter , például az LLM-végpont vagy a feltett kérdés nem változik.
Adjon hozzá egy metódust a válasz ellenőrzéséhez.
/// <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); }Jótanács
A metrikák mindegyike tartalmaz egy tulajdonságot
Reason, amely elmagyarázza a pontszám indoklását. Ennek oka szerepel a létrehozott jelentésben , és a megfelelő metrika kártyáján az információ ikonra kattintva tekinthető meg.Végül adja hozzá magát a vizsgálati módszert .
[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); }Ez a vizsgálati módszer:
Létrehozza a ScenarioRun.
await usingbiztosítja aScenarioRunkiértékelési eredmények megfelelő ártalmatlanítását és helyes megőrzését az eredménytárolóba.Megkapja az LLM válaszát egy adott csillagászati kérdésre. A teszt ugyanazt IChatClient adja át, mint amelyet a kiértékeléshez használnak a
GetAstronomyConversationAsynckiértékelendő elsődleges LLM-válasz válasz gyorsítótárazásának lekérésére szolgáló metódusnak. (Ugyanannak az ügyfélnek az átadása lehetővé teszi a válaszok gyorsítótárazását az LLM interakciós fordulókhoz, amelyeket a kiértékelők belső értékeléseik során alkalmaznak.) A válaszok gyorsítótárazásával az LLM válaszai a következő módon lesznek elérhetők:- Közvetlenül az LLM-végpontról az aktuális teszt első futtatásakor, vagy az azt követő futtatások során, ha a gyorsítótárazott bejegyzés lejárt (alapértelmezés szerint 14 nap).
- A teszt későbbi futtatásaiban a
s_defaultReportingConfiguration-ben konfigurált lemezalapú válaszgyorsítótárból.
A kiértékelőket futtatja a válasz ellenőrzéséhez. Az LLM-válaszhoz hasonlóan az azt követő futtatások is lekérik a kiértékelést a (lemezalapú) válaszgyorsítótárból, amely a következőben konfigurálva van
s_defaultReportingConfiguration.Elvégez néhány alapvető érvényesítést a kiértékelési eredményen.
Ez a lépés nem kötelező, és elsősorban bemutató célokra szolgál. A valós értékelésekben előfordulhat, hogy nem szeretné ellenőrizni az egyes eredményeket, mert az LLM-válaszok és a kiértékelési pontszámok idővel változhatnak a termék (és a használt modellek) fejlődésével. Előfordulhat, hogy nem szeretné, hogy az egyes kiértékelési tesztek "sikertelenek", és az eredmények változásakor blokkolják a CI-/CD-folyamatok buildjeit. Ehelyett lehet, hogy célszerűbb lenne a létrehozott jelentésre támaszkodni, és az értékelési pontszámok általános trendjeit nyomon követni különböző forgatókönyvek esetén, hosszabb időszakon át (és csak akkor legyen sikertelen egyes build-ek futtatása, amikor több különböző teszt során jelentős csökkenés tapasztalható az értékelési pontszámokban). Mindazonáltal van némi árnyalat ebben, és az a választás, hogy ellenőrizzük-e az egyes eredményeket vagy sem, a konkrét használati esettől függően változhat.
Amikor a metódus visszatér, az
scenarioRunobjektum el lesz helyezve, és a kiértékelési eredmény a (lemezalapú) eredménytárolóban lesz tárolva, amely a következőbens_defaultReportingConfigurationvan konfigurálva: .
A teszt/kiértékelés futtatása
Futtassa a tesztet az előnyben részesített tesztelési munkafolyamattal, például a CLI paranccsal dotnet test vagy a Test Explorer használatával.
Jelentés létrehozása
Telepítse a Microsoft. Extensions.AI.Evaluation.Console .NET eszköz egy terminálablakból a következő parancs futtatásával:
dotnet tool install --create-manifest-if-needed Microsoft.Extensions.AI.Evaluation.ConsoleHozzon létre egy jelentést a következő parancs futtatásával:
dotnet tool run aieval report --path <path\to\your\cache\storage> --output report.htmlNyissa meg a(z)
report.htmlfájlt. A jelentés az alábbi képernyőképhez hasonlóan néz ki.
Következő lépések
- Lépjen arra a könyvtárra, ahol a teszteredmények tárolódnak (vagyis
C:\TestReports, hacsak nem módosította a helyet a ReportingConfigurationlétrehozáskor).resultsAz alkönyvtárban figyelje meg, hogy minden tesztfuttatáshoz van egy időbélyeggel (ExecutionName) elnevezett mappa. Az egyes mappákban található egy-egy mappa minden egyes forgatókönyv nevével, ebben az esetben a projekt egyetlen tesztmetódusa szerepel. Ez a mappa tartalmaz egy JSON-fájlt, amely tartalmazza az összes adatot, beleértve az üzeneteket, a választ és a kiértékelési eredményeket. - Terjessze ki a kiértékelést. Íme néhány ötlet:
- Adjon hozzá egy másik egyéni kiértékelőt, például egy kiértékelőt, amely AI-t használ a válaszban használt mérési rendszer meghatározásához .
- Adjon hozzá egy másik tesztmetódust, például egy metódust, amely több választ értékel ki az LLM-ből. Mivel minden válasz különböző lehet, érdemes mintát venni és kiértékelni legalább néhány választ egy kérdésre. Ebben az esetben minden CreateScenarioRunAsync(String, String, IEnumerable<String>, IEnumerable<String>, CancellationToken) híváskor meg kell adnia egy iterációs nevet.