<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saurabh</title>
    <description>The latest articles on DEV Community by Saurabh (@saurabh_professional_54ad).</description>
    <link>https://dev.to/saurabh_professional_54ad</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4182523%2F69d5bc4e-6cd7-4003-a5b3-ea2f45c0294b.png</url>
      <title>DEV Community: Saurabh</title>
      <link>https://dev.to/saurabh_professional_54ad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saurabh_professional_54ad"/>
    <language>en</language>
    <item>
      <title>Beyond Unit Tests: How to Test and Evaluate LLM Applications</title>
      <dc:creator>Saurabh</dc:creator>
      <pubDate>Tue, 13 Oct 2026 15:12:21 +0000</pubDate>
      <link>https://dev.to/saurabh_professional_54ad/beyond-unit-tests-how-to-test-and-evaluate-llm-applications-4104</link>
      <guid>https://dev.to/saurabh_professional_54ad/beyond-unit-tests-how-to-test-and-evaluate-llm-applications-4104</guid>
      <description>&lt;h1&gt;
  
  
  Beyond Unit Tests: How to Test and Evaluate LLM Applications
&lt;/h1&gt;

&lt;p&gt;Imagine deploying an AI-powered chatbot. Your API returns &lt;code&gt;200 OK&lt;/code&gt;, all your unit tests pass, and the application responds within an acceptable time.&lt;/p&gt;

&lt;p&gt;But there is one problem: &lt;strong&gt;the answer is wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The chatbot confidently provides an incorrect refund policy, invents a step that does not exist in the documentation, or answers a question that it does not have enough information to answer.&lt;/p&gt;

&lt;p&gt;From a traditional software testing perspective, everything looks fine. From the user's perspective, the application has failed.&lt;/p&gt;

&lt;p&gt;This is one of the biggest challenges when building applications powered by Large Language Models (LLMs).&lt;/p&gt;

&lt;p&gt;Unlike traditional functions, LLMs can generate different responses to the same input. A response can be grammatically correct and well-structured while still being factually incorrect.&lt;/p&gt;

&lt;p&gt;So, how do we test these applications effectively?&lt;/p&gt;

&lt;p&gt;In this article, we'll explore practical approaches to LLM evaluation, hallucination testing, regression testing, and production monitoring, with examples relevant to JavaScript and backend engineers.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why Traditional Unit Tests Are Not Enough
&lt;/h2&gt;

&lt;p&gt;Consider a traditional function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discountPercentage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;discountPercentage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 900&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a given input, we can define an expected output and assert that the result matches it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a straightforward and deterministic test.&lt;/p&gt;

&lt;p&gt;Now consider an AI assistant that answers questions using internal company documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"What is the refund processing time?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected answer:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Refunds are processed within five business days."&lt;/p&gt;

&lt;p&gt;The LLM might answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Refunds are processed within five business days."&lt;/li&gt;
&lt;li&gt;"You should receive your refund within five working days."&lt;/li&gt;
&lt;li&gt;"The refund process typically takes about a week."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These answers may not be equivalent in every context. The third answer, for example, is less precise and might introduce ambiguity.&lt;/p&gt;

&lt;p&gt;An exact string comparison cannot reliably tell us whether the response preserves the intended meaning.&lt;/p&gt;

&lt;p&gt;This is why AI application testing needs more than traditional unit tests.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional testing&lt;/th&gt;
&lt;th&gt;AI evaluation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Does the function return the expected value?&lt;/td&gt;
&lt;td&gt;Does the answer communicate the correct meaning?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the API return a successful status?&lt;/td&gt;
&lt;td&gt;Does the response actually solve the user's problem?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the output match a fixed structure?&lt;/td&gt;
&lt;td&gt;Is the response relevant, accurate, and supported by evidence?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the application handle errors?&lt;/td&gt;
&lt;td&gt;Does it also behave safely when the answer is unknown?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The solution is not to replace unit tests. It's to combine them with additional evaluation techniques.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What Should We Measure in an AI Application?
&lt;/h2&gt;

&lt;p&gt;Before evaluating an AI system, we need to define what a good response looks like.&lt;/p&gt;

&lt;p&gt;There is no single metric that can measure every aspect of AI quality. Different applications require different evaluation criteria.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Correctness
&lt;/h3&gt;

&lt;p&gt;Does the response provide the correct answer?&lt;/p&gt;

&lt;p&gt;For a customer support assistant, correctness might mean that refund periods, eligibility conditions, and other policy details match the approved source.&lt;/p&gt;

&lt;p&gt;Possible approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comparing answers against reference answers.&lt;/li&gt;
&lt;li&gt;Checking required facts or claims.&lt;/li&gt;
&lt;li&gt;Having domain experts review a representative sample.&lt;/li&gt;
&lt;li&gt;Using an automated evaluator with clearly defined criteria.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  B. Relevance
&lt;/h3&gt;

&lt;p&gt;Does the response answer the question that was asked?&lt;/p&gt;

&lt;p&gt;For example, if someone asks how to reset a password, a detailed explanation of account registration is not a relevant answer, even if all the information is accurate.&lt;/p&gt;

&lt;p&gt;Relevance matters when building documentation assistants, support chatbots, and search applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Faithfulness and hallucinations
&lt;/h3&gt;

&lt;p&gt;For applications that retrieve documents before generating an answer, faithfulness means that the answer's claims are supported by the retrieved context.&lt;/p&gt;

&lt;p&gt;Suppose the source document states:&lt;/p&gt;

&lt;p&gt;"Refunds are processed within five business days."&lt;/p&gt;

&lt;p&gt;The assistant answers:&lt;/p&gt;

&lt;p&gt;"Refunds are processed within five business days, and a confirmation email is always sent within one hour."&lt;/p&gt;

&lt;p&gt;If the source says nothing about a confirmation email, the additional claim is unsupported.&lt;/p&gt;

&lt;p&gt;This is a useful way to test hallucinations in Retrieval-Augmented Generation (RAG) applications.&lt;/p&gt;

&lt;p&gt;However, an answer that is faithful to a document is not automatically correct in the real world. The source itself could be outdated or incorrect, so source quality matters too.&lt;/p&gt;

&lt;h3&gt;
  
  
  D. Completeness
&lt;/h3&gt;

&lt;p&gt;Does the response cover all the important parts of the question?&lt;/p&gt;

&lt;p&gt;If a user asks for three steps and the assistant provides only two, the answer may be accurate but incomplete.&lt;/p&gt;

&lt;h3&gt;
  
  
  E. Safety
&lt;/h3&gt;

&lt;p&gt;Does the application avoid unsafe or unauthorized behavior?&lt;/p&gt;

&lt;p&gt;Examples include refusing to reveal another customer's private information, following access restrictions, and resisting attempts to bypass system instructions.&lt;/p&gt;

&lt;p&gt;Safety tests should cover both ordinary usage and adversarial inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The important lesson:&lt;/strong&gt; Define your evaluation criteria before choosing the metrics. A high score on one metric does not guarantee that the application is good overall.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Building a Small Evaluation Dataset
&lt;/h2&gt;

&lt;p&gt;One of the most practical ways to test an LLM application is to maintain a fixed dataset of representative questions and expected behavior.&lt;/p&gt;

&lt;p&gt;This dataset can be treated as an evaluation suite and run whenever you change a prompt, model, retrieval configuration, or important application component.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test scenario&lt;/th&gt;
&lt;th&gt;Expected behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ask for the refund processing time&lt;/td&gt;
&lt;td&gt;Return the documented time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask about an unsupported policy&lt;/td&gt;
&lt;td&gt;Acknowledge that the information is unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request a short summary&lt;/td&gt;
&lt;td&gt;Preserve the key facts without adding claims&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask a question unrelated to the documentation&lt;/td&gt;
&lt;td&gt;Avoid inventing an answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request another customer's private data&lt;/td&gt;
&lt;td&gt;Do not disclose unauthorized information&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A basic dataset might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;evaluationCases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Refund processing time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How long does a refund take?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;requiredPhrases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five business days&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the refund policy for a product not covered by the documentation?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;uncertaintyPhrases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not specified&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cannot confirm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;don't have enough information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The required phrases and accepted uncertainty phrases are illustrative. They should be aligned with the actual policy and the response conventions used by your application.&lt;/p&gt;

&lt;p&gt;As the application grows, include more examples covering different user intents, edge cases, and failure modes. Keep the dataset representative rather than filling it only with easy questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Writing AI Regression Tests in JavaScript
&lt;/h2&gt;

&lt;p&gt;Let's turn the dataset into a simple automated test suite.&lt;/p&gt;

&lt;p&gt;Assume that our application exposes an asynchronous &lt;code&gt;answerQuestion()&lt;/code&gt; function that returns an object containing an &lt;code&gt;answer&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;We can use Node.js's built-in test runner, avoiding the need for an additional test framework.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;answerQuestion&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/assistant.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;evaluationCases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Refund processing time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How long does a refund take?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;requiredPhrases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;five business days&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the refund policy for an unsupported product?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;uncertaintyPhrases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not available&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not specified&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cannot confirm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;don't have enough information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scenario&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;evaluationCases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;answerQuestion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requiredPhrases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phrase&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requiredPhrases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
          &lt;span class="s2"&gt;`Expected answer to contain: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uncertaintyPhrases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acknowledgesUncertainty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uncertaintyPhrases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;acknowledgesUncertainty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected the assistant to acknowledge missing information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the tests with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple baseline, not a complete semantic evaluator.&lt;/p&gt;

&lt;p&gt;The code checks whether the response contains expected wording. That can catch obvious regressions, but a correct answer that uses different wording might fail. Conversely, a response could include the required phrase and still contain incorrect additional claims.&lt;/p&gt;

&lt;p&gt;For production systems, combine these checks with claim-level verification, semantic evaluation, and human review where appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens when the prompt changes?
&lt;/h3&gt;

&lt;p&gt;Imagine that the team modifies the system prompt to make answers shorter.&lt;/p&gt;

&lt;p&gt;The change may improve response time and reduce token usage, but it could also remove important policy details.&lt;/p&gt;

&lt;p&gt;Running the evaluation dataset again helps determine whether the change improved the system overall or introduced regressions.&lt;/p&gt;

&lt;p&gt;This is the central benefit of an AI regression suite: changes are evaluated against a repeatable set of expectations instead of relying only on a few manual experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Using an LLM as an Evaluator
&lt;/h2&gt;

&lt;p&gt;Exact phrase matching is useful for specific requirements, but many answers need semantic evaluation.&lt;/p&gt;

&lt;p&gt;For example, these two answers communicate approximately the same information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"The request will be processed within five business days."&lt;/li&gt;
&lt;li&gt;"Please allow five working days for processing."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple string comparison may mark them as different even when both are acceptable.&lt;/p&gt;

&lt;p&gt;One approach is to use a separate LLM to evaluate the generated response. This technique is commonly known as LLM-as-a-judge.&lt;/p&gt;

&lt;p&gt;The evaluator can be given:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user's question.&lt;/li&gt;
&lt;li&gt;The relevant source context.&lt;/li&gt;
&lt;li&gt;The generated answer.&lt;/li&gt;
&lt;li&gt;A rubric describing how the answer should be assessed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, we might ask the evaluator to score each dimension from 1 to 5:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Evaluation question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Relevance&lt;/td&gt;
&lt;td&gt;Does the answer address the user's question?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faithfulness&lt;/td&gt;
&lt;td&gt;Are the answer's claims supported by the supplied context?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completeness&lt;/td&gt;
&lt;td&gt;Does it cover the required information?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clarity&lt;/td&gt;
&lt;td&gt;Is it understandable and unambiguous?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A simple evaluation instruction could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are evaluating an AI-generated answer.

Question:
{question}

Source context:
{context}

Generated answer:
{answer}

Evaluate relevance, faithfulness, and completeness
on a scale from 1 to 5.

Use the supplied source context to assess faithfulness.
Do not reward an answer merely because it sounds confident.
Identify any unsupported claims and explain the reason
for each score.

Return structured JSON containing the scores,
unsupported claims, and a short explanation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The evaluator should follow a consistent rubric and produce structured output that can be stored and compared.&lt;/p&gt;

&lt;p&gt;However, an LLM evaluator is not a perfect source of truth. It can overlook errors, prefer fluent answers, or make its own incorrect judgments.&lt;/p&gt;

&lt;p&gt;To improve reliability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate the evaluator's results against human-reviewed examples.&lt;/li&gt;
&lt;li&gt;Keep the evaluation instructions consistent across comparisons.&lt;/li&gt;
&lt;li&gt;Use trusted source material where available.&lt;/li&gt;
&lt;li&gt;Review borderline cases and disagreements.&lt;/li&gt;
&lt;li&gt;Do not rely on one automated score for critical safety decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LLM-as-a-judge is best treated as a scalable evaluation aid, not a replacement for ground truth or human judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Measuring Latency, Token Usage, and Cost
&lt;/h2&gt;

&lt;p&gt;Answer quality is only one part of production readiness.&lt;/p&gt;

&lt;p&gt;A response can be accurate but take too long to generate. Another response might be acceptable but consume significantly more tokens than expected.&lt;/p&gt;

&lt;p&gt;For this reason, quality evaluations should also track operational metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Latency
&lt;/h3&gt;

&lt;p&gt;Measure how long a request takes to complete.&lt;/p&gt;

&lt;p&gt;Useful metrics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Median latency (p50).&lt;/li&gt;
&lt;li&gt;p95 latency.&lt;/li&gt;
&lt;li&gt;Timeout rate.&lt;/li&gt;
&lt;li&gt;Error rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The p95 latency tells us the response time at or below which approximately 95% of measured requests complete. It helps identify slow experiences that an average can hide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token usage and cost
&lt;/h3&gt;

&lt;p&gt;When supported by the model provider, collect input and output token counts for every request.&lt;/p&gt;

&lt;p&gt;A simplified cost calculation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;estimateCost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;inputTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;outputTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;inputPricePerMillion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;outputPricePerMillion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputTokens&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;inputPricePerMillion&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputTokens&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;outputPricePerMillion&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The price values should come from the applicable model's current pricing configuration. This calculation is an estimate and may not include other infrastructure or service costs.&lt;/p&gt;

&lt;p&gt;Track cost per request, cost by feature, and cost per successfully completed user task.&lt;/p&gt;

&lt;p&gt;The last measure is particularly useful because reducing the cost of each response does not necessarily improve the system if the answers become less useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quality alongside performance
&lt;/h3&gt;

&lt;p&gt;Consider a new model or prompt configuration that reduces average latency by 20% but also increases incorrect answers.&lt;/p&gt;

&lt;p&gt;Looking only at latency would suggest an improvement. Evaluating quality and operational metrics together gives the team a more complete picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Monitoring an AI Application in Production
&lt;/h2&gt;

&lt;p&gt;Offline evaluation helps us test changes before deployment. It cannot predict every real-world input or failure.&lt;/p&gt;

&lt;p&gt;Production monitoring is therefore equally important.&lt;/p&gt;

&lt;p&gt;A useful monitoring strategy includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical health&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request volume and error rate.&lt;/li&gt;
&lt;li&gt;Model-provider failures and timeouts.&lt;/li&gt;
&lt;li&gt;Median and p95 latency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost and usage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input and output tokens.&lt;/li&gt;
&lt;li&gt;Estimated cost by model and feature.&lt;/li&gt;
&lt;li&gt;Unexpected usage spikes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI quality&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Periodic evaluation of sampled responses.&lt;/li&gt;
&lt;li&gt;Unsupported claims and retrieval failures.&lt;/li&gt;
&lt;li&gt;User feedback and task completion rates.&lt;/li&gt;
&lt;li&gt;Changes in quality after a model or prompt update.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a RAG system, it can also be valuable to monitor retrieval quality separately from answer generation. The retriever may return irrelevant documents even when the LLM is following those documents correctly.&lt;/p&gt;

&lt;p&gt;When logging requests, take care to protect customer information. Redact sensitive data, apply appropriate access controls, and define retention rules rather than storing every raw prompt and answer indefinitely.&lt;/p&gt;

&lt;p&gt;The goal is not to capture everything. It is to collect the right signals to detect failures and investigate them safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Putting It All Together: An AI Testing Workflow
&lt;/h2&gt;

&lt;p&gt;A practical workflow for an engineering team could look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define quality criteria.&lt;/strong&gt; Decide what a correct, relevant, safe response means for the feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build an evaluation dataset.&lt;/strong&gt; Include normal scenarios, edge cases, and known failure modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run traditional tests.&lt;/strong&gt; Verify API contracts, validation, authentication, error handling, and application logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run AI evaluations.&lt;/strong&gt; Check correctness, relevance, faithfulness, and the behaviors that matter to the feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare with the previous version.&lt;/strong&gt; Investigate regressions after prompt, model, or retrieval changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set deployment gates.&lt;/strong&gt; Use thresholds for critical metrics that reflect the application's actual requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor production.&lt;/strong&gt; Track latency, errors, cost, feedback, and sampled quality evaluations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feed real failures back into the dataset.&lt;/strong&gt; Turn recurring production problems into regression tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This creates a continuous improvement loop: evaluation informs deployment, and production observations inform future evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Testing AI applications requires a broader approach than checking whether an API responds successfully.&lt;/p&gt;

&lt;p&gt;Traditional unit and integration tests remain essential, but they do not establish that an LLM's answer is correct, relevant, complete, or safe.&lt;/p&gt;

&lt;p&gt;A stronger strategy combines repeatable evaluation datasets, clearly defined quality criteria, semantic evaluation, regression testing, and production monitoring.&lt;/p&gt;

&lt;p&gt;You do not need a complicated evaluation platform to get started. A small dataset, an automated test runner, a consistent evaluation rubric, and basic latency and cost tracking can already make AI development more disciplined.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key takeaway is simple: don't just test whether your AI application works. Test whether it works well enough for the user and the problem it is designed to solve.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Further Reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://platform.openai.com/docs/guides/evals" rel="noopener noreferrer"&gt;OpenAI Evals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.ragas.io/" rel="noopener noreferrer"&gt;Ragas Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.confident-ai.com/" rel="noopener noreferrer"&gt;DeepEval Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.smith.langchain.com/" rel="noopener noreferrer"&gt;LangSmith Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;How is your team currently testing LLM-powered applications? Are you using fixed evaluation datasets, LLM-as-a-judge, human review, or a combination of these approaches?&lt;/p&gt;

&lt;p&gt;I'd be interested to hear what has worked well for other engineering teams.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
