Scoring Agent Answers: Quality Metrics and LLM-as-Judge

Routing tests tell you the agent picked the right topic. They say nothing about whether the answer was correct, complete, on-tone — or invented. To ship, you need to score responses, not just match them.

The scenario

CarePoint Health, a regional healthcare provider, runs a patient-services agent that answers questions about appointments, billing, insurance coverage and pre-visit preparation. The routing suite from Part 1 is green: the right topics fire, the right actions run.

Then a compliance reviewer reads twenty transcripts and rejects three of them. In one, the agent told a patient their colonoscopy prep "requires fasting from midnight" — the grounded Knowledge article said six hours, and the agent had blended in generic advice. In another, the answer was correct but incomplete: it gave the copay amount without the deductible caveat, which the policy document states in the same paragraph. The third had a tone problem — a patient asking about an overdue bill for a deceased family member got a chirpy upsell for paperless billing.

All three passed routing. All three would have been publishable failures. CarePoint's conclusion: correct routing with a wrong answer is still a wrong answer, and in healthcare it's a reportable one. So they built a response-quality evaluation on top of the routing suite. This post shows how.

For beginners: an LLM's response is free text. You can't assert equality against an expected string, because two perfect answers can be worded completely differently. The standard technique is a rubric — a small set of named quality dimensions with pass criteria — scored either by humans (accurate, slow, expensive) or by another LLM given strict grading instructions, known as LLM-as-judge. Testing Center's own outcome expectation works this way under the hood; here we build an explicit version you control.

Step 1 — Define the rubric before writing any code

CarePoint scores every test response on five dimensions. Each is a yes/no gate, not a 1–10 scale — vague scales produce vague judges. The rubric:

Write the rubric with the people who will veto the launch. CarePoint's compliance team co-authored the compliance and tone gates. That turned sign-off from a subjective transcript review into "the suite passes the gates we wrote". Same reviewers, far faster approvals.

Step 2 — Write the judge prompt

The judge is only as good as its instructions. Three rules that made CarePoint's judge reliable: give it the grounding source (not just the answer), force a per-dimension verdict with a quoted justification, and demand structured JSON so Apex can parse it.

You are a strict quality auditor for a healthcare service agent.
Judge the AGENT RESPONSE only against the GROUNDING SOURCE.
Do not use your own medical or billing knowledge.

USER QUESTION:
{{question}}

GROUNDING SOURCE (the only permitted facts):
{{source}}

AGENT RESPONSE:
{{response}}

Evaluate these dimensions. For each, answer "pass" or "fail"
and quote the exact words that justify your verdict.

1. accuracy: every number, time, duration and policy term in the
   response matches the source exactly.
2. completeness: every caveat or condition the source attaches to
   the quoted facts is present in the response.
3. groundedness: the response contains no factual claim that is
   absent from the source.
4. tone: plain and empathetic; no promotional language.
5. compliance: no diagnosis, no treatment advice, no coverage
   promise; required disclaimers present.

Return only JSON:
{"accuracy":{"verdict":"","evidence":""},
 "completeness":{"verdict":"","evidence":""},
 "groundedness":{"verdict":"","evidence":""},
 "tone":{"verdict":"","evidence":""},
 "compliance":{"verdict":"","evidence":""},
 "overall":"pass|fail"}

The colonoscopy failure is a good trace of why the source must be in the prompt. Without it, a judge model "knows" fasting-from-midnight is common advice and passes the answer. With the source present and the instruction "do not use your own knowledge", it fails groundedness and quotes the six-hour line as evidence.

Step 3 — Automate the judge in Apex with the Models API

CarePoint runs the judge through the Models API from the aiplatform Apex namespace — the same gateway their agent uses, so everything stays behind the Einstein Trust Layer. The scorer is an invocable, which means Flow can call it too (for sampled scoring of live conversations later):

public with sharing class AgentResponseJudge {

  public class Request {
    @InvocableVariable(required=true label='User Question')
    public String question;
    @InvocableVariable(required=true label='Grounding Source')
    public String source;
    @InvocableVariable(required=true label='Agent Response')
    public String response;
  }

  public class Result {
    @InvocableVariable(label='Verdict JSON')
    public String verdictJson;
    @InvocableVariable(label='Overall Pass')
    public Boolean overallPass;
  }

  @InvocableMethod(label='Judge Agent Response'
    description='Scores an agent response against its grounding source.')
  public static List<Result> judge(List<Request> requests) {
    List<Result> results = new List<Result>();
    for (Request req : requests) {
      aiplatform.ModelsAPI.createGenerations_Request genReq =
        new aiplatform.ModelsAPI.createGenerations_Request();
      genReq.modelName = 'sfdc_ai__DefaultGPT4Omni';

      aiplatform.ModelsAPI_GenerationRequest body =
        new aiplatform.ModelsAPI_GenerationRequest();
      body.prompt = buildJudgePrompt(req.question, req.source, req.response);
      genReq.body = body;

      aiplatform.ModelsAPI.createGenerations_Response genRes =
        new aiplatform.ModelsAPI().createGenerations(genReq);

      Result r = new Result();
      r.verdictJson = genRes.Code200.generation.generatedText;
      Map<String, Object> parsed =
        (Map<String, Object>) JSON.deserializeUntyped(r.verdictJson);
      r.overallPass = 'pass'.equals((String) parsed.get('overall'));
      results.add(r);
    }
    return results;
  }

  private static String buildJudgePrompt(String question,
      String source, String response) {
    return JUDGE_TEMPLATE
      .replace('{{question}}', question)
      .replace('{{source}}', source)
      .replace('{{response}}', response);
  }

  private static final String JUDGE_TEMPLATE = '...'; // the prompt above
}

A driver script feeds it the evaluation set — question, source article body, and the response captured from a batch run — and writes verdicts to a custom object, Agent_Eval_Result__c, one record per case per dimension. A simple report then answers the questions that matter: pass rate by dimension, by topic, over time.

Use a different model for the judge than the agent, when you can. A model grading its own output shares its blind spots. CarePoint's agent runs on one model family and the judge on another, selected purely by changing modelName. If you must use the same family, the source-quoting requirement is your main defense against self-agreement.

Step 4 — Calibrate the judge before trusting it

An unvalidated judge is just a second opinion with confidence. CarePoint calibrated theirs in an afternoon:

  1. Compliance reviewers hand-labeled 40 responses (pass/fail per dimension) — including the three original rejects.
  2. The judge scored the same 40 blind. Agreement: 36 of 40, and crucially all three known-bad responses failed.
  3. The four disagreements were all tone. The rubric said "empathetic where sensitive" but didn't define sensitive. They enumerated it — collections, bereavement, clinical anxiety — and re-ran: 39 of 40.

That loop — human labels, measure agreement, tighten the rubric where the judge and humans disagree — is the whole method. Repeat it whenever you change the judge prompt or model. And keep a small human sample forever: CarePoint's reviewers still read ten random transcripts a week, which is how they'll spot the failure modes the rubric doesn't cover yet.

Thresholds, agreed with compliance and enforced later in CI:

Where this goes next

You now have the two halves of agent quality: behavior tests (Part 1, Part 2) and response scoring (this post). The obvious next question: when someone edits a topic instruction six months from now, what reruns all of this automatically? Next in the series: regression testing and CI for agents — including the one-line change that silently broke refund routing at an e-commerce company. The adversarial cousin of this post is Part 5, guardrail and red-team testing.

For the Models API plumbing used above in a different context, see Call LLMs from Apex with the Models API. The full series lives on the Agentforce Testing & Evaluation track.

Sources: Access Models API with Apex (Developer Guide) · Models API (Developer Guide) · Agentforce Testing Center (Salesforce Help) · Einstein Trust Layer (Trailhead)