Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
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 végrehajtásához, 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 csomagból) LLM-et használnak az értékelések elvégzéséhez; az egyéni kiértékelő nem használ AI-t.
Előfeltételek
- .NET 8 vagy újabb verzió
- Visual Studio Code (nem kötelező)
Az AI-szolgáltatás konfigurálása
Ha Azure OpenAI-szolgáltatást és modellt szeretne kiépíteni az Azure Portalon, végezze el az Azure OpenAI-szolgáltatás erőforrásának 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-4o modellt.
A tesztalkalmazás létrehozása
Hajtsa végre az alábbi lépéseket az AI-modellhez gpt-4o 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 --prerelease 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 az Azure OpenAI-végponthoz, a modellnévhez é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_OPENAI_GPT_NAME gpt-4o 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 model = config["AZURE_OPENAI_GPT_NAME"]; string tenantId = config["AZURE_TENANT_ID"]; // 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 CreateScenarioRunAsync(String, String, IEnumerable<String>, IEnumerable<String>, CancellationToken) hívásakor azonban bármilyen tetszőleges sztringet megadhat. Íme néhány szempont a forgatókönyv nevének kiválasztásához:
- 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.
- Alapértelmezés szerint a létrehozott kiértékelési jelentés felosztja a forgatókönyvek nevét
., hogy az eredmények hierarchikus nézetben, megfelelő csoportosítással, beágyazással és összesítéssel jelenjenek meg. Ez különösen akkor hasznos, ha a forgatókönyv neve a megfelelő tesztmetódus teljes neve, mivel lehetővé teszi az eredmények névterek és osztálynevek szerinti csoportosítását 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
Defaultfogja használni. Ebben az esetben az egyik futtatás eredményeit a következő felülírja, és elveszíti a különböző futtatások eredményeinek összehasonlítási képességét.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-t ad vissza, ami tartalmazza 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. Ezt az alapértelmezett értelmezést a hívó felül tudja bírálni, ha szükséges.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 válasz a megfelelő gyorsítótár-bejegyzés lejáratáig (alapértelmezés szerint 14 napon belül) vagy addig lesz újra felhasználva, amíg bármilyen kérelemparaméter , például az LLM-végpont vagy a feltett kérdés nem módosul.
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. A
await usinghasználata biztosítja, hogy aScenarioRunmegfelelő módon el legyen távolítva, és hogy a kiértékelés eredményeit helyesen tárolják az eredménytárolóban.Megkapja az LLM válaszát egy adott csillagászati kérdésre. A rendszer ugyanazt IChatClient adja át a metódusnak, amelyet a
GetAstronomyConversationAsynckiértékelésre használt elsődleges LLM-válasz válasz gyorsítótárazásának lekéréséhez használ. (Emellett lehetővé teszi az értékelők által a belső értékelések során használt LLM-válaszok gyorsítótárazását.) Ezzel a gyorsítótárazással az LLM-válaszok a következő módokon lesznek lekérve:- 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
s_defaultReportingConfigurationkonfigurá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 során a kiértékelést a
s_defaultReportingConfiguration-ben konfigurált (lemezalapú) válaszgyorsítótárból kérik le.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, mivel az LLM-válaszok és az é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é, ha az értékelő tesztek "sikertelenek" lennének, és blokkolnák a build folyamatokat a CI/CD pipeline-okban, amikor ez történik. 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özt a következő parancs terminálablakból való futtatásával:
dotnet tool install --local Microsoft.Extensions.AI.Evaluation.ConsoleJótanács
Előfordulhat, hogy először létre kell hoznia egy jegyzékfájlt. Erről és a helyi eszközök telepítéséről további információt a Helyi eszközök című témakörben talál.
Hozzon 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. Valahogy így kell kinéznie.
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 további 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 eltérő 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.