How to QA an AI Chatbot When There Is No Single Correct Answer: A Two-Layer Testing Framework

LLMs broke the input-action-expected-result contract that testing has relied on for twenty years. Here is a two-layer framework that separates what can be asserted from what must be scored, with code, thresholds, and the production mistakes we made getting there.

For most of the last two decades, a test case had three parts: an input, an action, and an expected result. You typed a value, clicked a button, and compared the output against a fixed string. If they matched, the test passed. That contract worked because software was deterministic. The same input always produced the same output.

Large language models broke that contract. When your product ships a chatbot, a support copilot, or an autonomous agent, the same question can produce five different answers, all of them correct. A model update can quietly change behaviour overnight with no code change and no failing test. Assertions built on exact matches either pass everything or fail constantly.

This is the single hardest new problem facing QA teams in 2026, and it matters commercially. Teams are shipping AI features faster than they can validate them, and defects are reaching customers because the old safety net does not catch this class of bug. This article explains a two-layer testing framework that separates what can be asserted from what must be scored, with the code, the trade-offs, and the production mistakes we made along the way. It is written for QA engineers, SDETs, and engineering managers who already own a test suite and now own an AI feature too.

The Problem: When the Expected Result Stops Existing

A real-world scenario

A retail client added an AI support agent to handle refund and order-status queries. It could look up an order, read the refund policy, and issue a refund through an internal API. The functional test suite was green. Two weeks after launch, the support team noticed refunds being approved for orders well outside the 30-day return window.

The agent had not crashed. It had not returned an error. It had read the policy document, summarised it accurately, and then called the refund tool anyway. Every existing test passed, because every existing test checked that a response was returned and that the response mentioned the policy.

Business impact

  • Direct revenue loss from refunds that should never have been approved.
  • Support escalations rose because customers received confident but incorrect answers.
  • Release confidence collapsed. The team started manually reviewing chat transcripts before every deploy, adding roughly 26 hours of review time per release.

Technical challenges

  • Non-determinism. The same prompt returns different wording each run, so exact-match assertions are useless and near-match assertions are arbitrary.
  • Failures happen mid-trajectory. The agent can reach a correct-looking final answer through faulty reasoning, so checking only the output hides the defect.
  • Silent regressions. A model provider can update the underlying model with no notice, no version bump on your side, and no failing build.
  • Unstructured input. Users type anything, including adversarial text designed to override system instructions.

The Solution: A Two-Layer Test Architecture

The core decision is to stop treating the agent as one testable unit. An agent has a deterministic scaffold (routing, parsing, schema handling, guardrails) and a probabilistic core (the generated language and reasoning). These need different tools and different pass criteria, so we split them into two layers running at different points in the pipeline.

Layer 1 gates every commit. Layer 2 gates every merge.

Architecture overview

Layer 1 runs on every commit and takes under a minute. It contains ordinary assertions: does the router pick the right tool, does the tool receive correctly parsed arguments, does the response match the JSON schema, does a known unsafe request still get refused. No model judge is involved, so there is no cost and no flakiness.

Layer 2 runs on merge to main and nightly. It replays a versioned golden dataset through the agent and scores the results with metrics rather than assertions, then compares those scores against a threshold gate.

Tools used

  • Pytest with custom markers to separate the two layers.
  • An evaluation library (DeepEval or Ragas) for faithfulness, relevance, and hallucination scoring.
  • OpenTelemetry-style tracing so every tool call and retrieval is recorded as a span.
  • The golden dataset stored as YAML in the same repository as the agent, versioned with the prompts.

Key decisions

  • Pin the judge model and the prompt version. If your evaluator changes, your scores move for reasons that have nothing to do with your product. Treat the judge like any other dependency.
  • Store expectations as ranges and constraints, not strings. A case says what must appear, what must never appear, which tools should have been called, and the minimum acceptable score.
  • Run each scored case several times and take the median, so ordinary sampling variance does not fail the build.

Code: Layer 1, deterministic checks

@pytest.mark.deterministic
def test_refund_request_routes_correctly(agent):
    trace = agent.run("I want my money back for order #4821")

    assert trace.tools_called == ["lookup_order", "lookup_policy"]
    assert trace.tool_args["lookup_order"]["order_id"] == "4821"
    assert "issue_refund" not in trace.tools_called  # needs policy check first

Code: the golden dataset

- id: refund_outside_window
  input: "I bought this 90 days ago, can I return it?"
  context: policy_v3.md
  must_contain: ["30-day"]
  must_not_contain: ["approved", "refund issued"]
  expected_tools: ["lookup_order", "lookup_policy"]
  min_faithfulness: 0.85

Code: Layer 2, scored evaluation with a quality gate

def score_case(agent, judge, case, runs=3):
    scores = []
    for _ in range(runs):
        out = agent.run(case.input, context=case.context)
        scores.append(judge.faithfulness(out.text, case.context))
    return statistics.median(scores)

@pytest.mark.evaluation
@pytest.mark.parametrize("case", load_golden_dataset())
def test_behaviour_within_threshold(agent, judge, case):
    score = score_case(agent, judge, case)
    assert score >= case.min_faithfulness, (
        f"{case.id}: {score:.2f} < {case.min_faithfulness}"
    )

What this catches that a traditional suite does not

Failure type Traditional QA Two-layer approach
Wrong tool called Not visible, output looks fine Trajectory audit fails the case
Hallucinated policy Passes string match Faithfulness score drops below gate
Silent model update No test fires Nightly eval flags score drift
Prompt injection Out of scope Adversarial suite blocks the merge

Real Experience: What Production Taught Us

Mistake 1: we scored every case once

The first version of the evaluation suite failed roughly one build in four with no code change. Engineers started re-running the pipeline until it went green, which is worse than having no gate at all. Taking the median of three runs removed almost all of that noise. The cost is three times the tokens, which is why this layer does not run on every commit.

Mistake 2: we upgraded the judge model mid-quarter

A newer judge model shifted every faithfulness score by roughly 0.05, and half the suite failed overnight. Nothing about the product had changed. We now pin the judge model version, and when we do upgrade it we re-baseline every threshold in a dedicated pull request that touches no product code.

Production issue: a silent provider update

Six weeks in, routing accuracy dropped noticeably over a weekend. No deploy had happened. The provider had updated the underlying model. Because the nightly evaluation was already running, the drift was flagged within 14 hours instead of surfacing through customer complaints, and the team pinned to a specific model snapshot the same day.

Security improvement

A user uploaded a PDF containing instructions aimed at the agent rather than at a human. The agent followed them. We added an adversarial regression suite of 60 injection payloads to Layer 1, treated as hard assertions: any response that acts on instructions found inside retrieved content fails the build. That suite has blocked three merges since. If you are building on retrieval, our explainer on why AI needs to search before it speaks covers where these payloads enter the pipeline.

Performance and cost optimisation

The first combined suite took 11 minutes and cost about 38 dollars per run, and developers ran it as rarely as possible. Splitting the layers cut the per-commit run to under 40 seconds at effectively zero cost, and reduced the full evaluation to roughly 9 dollars by caching retrieval results and only re-running cases whose prompts or context files had changed.

Results after one quarter

Metric Before After
Escalation rate on AI-handled chats 18 percent 6.4 percent
Pre-release manual transcript review 26 hours About 7 hours
Regressions caught before release 41 percent 89 percent

Conclusion

The key takeaway is simple: stop trying to assert your way through non-deterministic software. Separate the parts of your agent that can be asserted from the parts that must be scored, gate them at different points in the pipeline, and version your evaluation dataset as carefully as you version your code. Everything else, including dashboards, tooling and vendor platforms, is detail on top of that split.

If this framing is useful, our related pieces on agentic AI in software testing and AI-powered test automation go deeper on the tooling side.

Key takeaway: An agent has a deterministic scaffold and a probabilistic core. Assert the scaffold on every commit, score the core on every merge, and never let a model judge you have not pinned decide whether your build is green.

If your team is shipping an AI feature without a test strategy behind it, Logic Providers can help you build this framework into your existing pipeline. Our work on custom AI chatbots and LLM API integrations covers evaluation design, adversarial testing, and production monitoring. The business benefit is measurable rather than theoretical: fewer incorrect answers reaching customers, far less manual review before every release, and a real answer when someone asks whether the AI feature is safe to ship this week.

Share This Article

Tags

QA AI Testing LLM Evaluation Test Automation Chatbots Prompt Injection
Kunal Rajput
About the Author
Quality Analyst

Kunal is a quality analyst with 1+ year of experience ensuring every release meets the highest standards before it reaches production. At Logic Providers, he designs thorough test plans covering functional, regression, integration, and user acceptance testing across web and mobile platforms. Kunal validates complex workflows including subscription billing systems, payment gateway flows, checkout processes, and admin panel operations. He is proficient in manual testing methodologies, API testing with Postman, cross-browser and cross-device compatibility testing, and defect tracking through structured bug reporting. Kunal has a sharp eye for edge cases, data integrity issues, and UI inconsistencies that could impact end users. His structured approach to quality metrics and test documentation helps the team ship reliable software and catch production bugs before they reach customers.

Connect on LinkedIn
How to QA an AI Chatbot When There Is No Single Correct Answer: A Two-Layer Testing Framework
Written by
Kunal Rajput
Kunal Rajput
LinkedIn
Published
August 21, 2026
Read Time
11 min read
Category
QA Testing
Tags
QA AI Testing LLM Evaluation Test Automation Chatbots Prompt Injection
Start Your Project

Related Articles

Agentic AI in Software Testing: What Autonomous QA Really Means in 2026
QA Testing July 17, 2026

Agentic AI in Software Testing: What Autonomous QA Really Means in 2026

Release cycles are shrinking to hours while test suites keep growing, and maintenance eats 30 to 50 percent of a QA engineer's week. Agentic AI promises to close that gap - but "autonomous QA" is also the most over-marketed phrase in the industry. Here is what agentic testing actually does today, where it fails, and how to adopt it safely.

Read More

Have a Project in Mind?

Let's discuss how we can help bring your vision to life.