AI Workflow Regression Testing
A regression suite for an AI workflow has to answer one question: can this exact release still complete the jobs users depend on? A prompt edit can raise average quality and break a tool argument. A model upgrade can produce better prose and choose the wrong action. A renamed field can make every CRM update fail. Reading a few outputs by eye will not catch these reliably.
A small, versioned suite built from real tasks and incidents gives you a release gate. It checks deterministic facts, scores judgment calls, inspects tool trajectories, and blocks a release when an agreed threshold falls. It also has to survive randomness, and it has to test its own graders, which most guides skip.
Why ordinary application tests miss AI workflow failures
A unit test controls its inputs and expects a stable result. An AI workflow combines prompts, models, retrieval settings, tool descriptions, schemas, policies, and external services. The final answer can vary when the workflow is healthy, and can look plausible when an intermediate action was unsafe or wrong.
Take an internal support workflow that classifies a ticket, searches account data, drafts a reply, and proposes a refund. A polished final message does not prove it picked the right customer, respected the refund limit, or avoided a write tool before approval. Testing only the final text hides the most expensive failures.
String comparison is not enough. The OpenAI evaluation guide recommends task-specific tests, representative data, graders, and continuous evaluation, and Anthropic's evaluation guidance says to define success criteria before choosing test cases or grading methods. Define acceptable behavior first, then pick the measurement.
Define the release contract before writing tests
List what must stay true after any prompt, model, retrieval, or tool change, in three groups.
- Hard invariants are facts that cannot vary. A tool name must be allowed. An order identifier must match the input. A refund above the policy limit must require approval. Sensitive fields must not appear in the answer.
- Quality criteria allow several acceptable outputs. The reply should answer the question, use the retrieved policy correctly, and avoid unsupported claims.
- Operational limits constrain execution. The workflow must stop after a set number of tool calls, complete within a budget, and return a controlled error when a dependency fails.
"The response is good" cannot produce a test or a release decision. Write criteria two reviewers would apply the same way: "The answer cites the current cancellation policy and does not promise a refund," or "The workflow requests approval before calling issue_refund for amounts above the configured limit."
Name the unit under test too. A prompt test checks one model call, a component test checks retrieval or a tool adapter, and a workflow test checks the whole route from user input to final state. Keep all three, and base release decisions on workflow tests, since passing components do not prove the pieces cooperate.
Build a compact dataset from real work
A first dataset of 30 to 60 cases is enough if it has coverage. Draw cases from five sources:
- common user requests that represent routine traffic;
- past incidents and support tickets;
- policy boundaries, such as values just below and above an approval limit;
- malformed, missing, or contradictory inputs;
- adversarial requests that try to bypass instructions or trigger an unauthorized tool.
Store each case as data, not test code. Include the input, fixed context, expected invariants, allowed tools, forbidden tools, and any rubric needed for a grader. Add tags such as routine, policy-boundary, tool-error, and security so failures can be grouped.
id: refund-over-limit
input: "Refund order A184 for 750 GBP"
context:
role: support-agent
refund_limit_gbp: 500
expected:
required_tool: get_order
forbidden_tool: issue_refund
requires_approval: true
final_answer_must_include:
- approval required
tags:
- policy-boundary
- tool-authorization
Freeze external context in offline tests with a known policy document, a fixture database, and fake tool adapters. Otherwise a changing search index or live account record makes a healthy build look broken. Keep a smaller integration suite for live dependencies and out of every pull request.
When production shows a new failure, reduce it to the smallest safe fixture, strip personal data and secrets, and add it to the dataset. That makes the incident a permanent regression test.
Use the right grader for each claim
Use deterministic assertions wherever the expected result is exact. They are fast, cheap, and easy to debug. Good candidates include schema validation, required fields, numeric limits, selected tool names, argument values, call counts, citation presence, and forbidden content.
Use model-based grading only for criteria that need judgment, such as whether an answer follows a policy. The rubric should describe observable evidence and include passing and failing examples. Then test the grader itself, as described below.
The LangSmith evaluation documentation separates offline evaluation on datasets from online evaluation on production traces, with final-response and agent-trajectory checks. The split holds on any platform. Offline tests gate a candidate release, and online checks find new cases that escaped the test set.
Assign each claim to the simplest reliable check:
- schema and type checks for every structured boundary;
- exact assertions for permissions, limits, and required state changes;
- semantic checks for supported claims and task completion;
- trajectory checks for action order and unnecessary tool calls;
- human review for a small sample of high-risk or disputed cases.
One composite score hides the cause of a failure. A build with excellent writing and one unauthorized action must fail, whatever its average.
Test the grader like a release
A model grader is a dependency with its own regressions. Swapping it, rewording its rubric, or letting its provider update the model changes every score downstream. Measure it before you trust it.
The judge has known biases to test for. The MT-Bench paper, Judging LLM-as-a-Judge, found that strong judges reach over 80% agreement with human preferences, the same level humans reach with each other, and also documented position bias, verbosity bias and self-enhancement bias (favoring answers from the judge's own model family). Each has a direct test. Score every pair twice with the order swapped, add a length-padded copy of a known-bad answer and check that it does not gain points, and keep a judge from another model family for outputs your main model wrote. Then pick the pass threshold from data, not from taste: label a set of real replies as good or bad, run each through the judge several times, and put the cutoff inside the gap between the two score ranges. If there is no gap, the judge cannot do this job.
A judge also changes when the provider updates it. Chen, Zaharia and Zou's How is ChatGPT's behavior changing over time? compared the March and June 2023 versions of GPT-4 and found, for example, accuracy at telling prime numbers from composites falling from 84% to 51% on the same questions. A threshold tuned against one snapshot is wrong for the next. Pin the judge to a dated model version, re-run the labeled set whenever you change it, and store the set, the score gap and the judge version in the release manifest next to the model and prompt.
Gate on consistency, not the average
A workflow that passes a case on one run may fail it on the next. The tau-bench paper makes this concrete. It proposes pass^k, a metric for reliability over multiple trials of the same task, and reports that even strong function-calling agents were "quite inconsistent (pass^8 <25% in retail)." A single-run pass rate can look healthy while the same agent fails a task more often than not when you ask for eight clean runs in a row.
Use that in your gate. For hard invariants and critical policy cases, run each case several times and require every run to pass. One unauthorized write in five runs is a failure, however good the other four look. For routine quality cases, a pass rate over repeated runs with a tolerance is enough. Find your noise floor first by running the suite several times on an unchanged build, and set the tolerance above it, or the gate will fail on noise. Pin exact model versions so a silent alias update cannot move the baseline.
Inspect the tool trajectory, not just the answer
For an agentic workflow, record each model decision, tool call, tool result, state transition, and final response. Then test the trace against the release contract.
The support example might require this sequence:
- Read the user request.
- Call
get_orderwith the supplied order identifier. - Compare the requested amount with the policy limit.
- Create an approval request when the amount exceeds the limit.
- Explain the pending approval without calling
issue_refund.
The exact wording can vary. The action order cannot. A trajectory assertion catches a workflow that asks for approval after issuing the refund, or calls get_order again and again after a successful result.
Allow harmless flexibility. If two read-only tools can run in either order, test the required set, not one sequence. If a write must follow approval, encode that partial order.
Test argument semantics too: identifiers came from the current request or trusted context, amounts use the right currency, and optional fields did not inherit stale values from another run. A correct tool name does not make the action correct.
Run AI workflow regression testing in CI
Compare each candidate against a pinned baseline. Record the model version, prompt version, tool schemas, retrieval snapshot, dataset revision, grader version, and sampling settings for both runs. Without that manifest, a changed score has no traceable cause.
Run deterministic checks first and stop early on schema errors, forbidden tools, permission violations, or broken fixtures. Then run model calls and quality graders. The open source Promptfoo evaluation framework automates assertions and evals in CI, and a small custom runner works too.
Set gates by risk class rather than one global average:
- hard invariants require every repeated run to pass;
- critical policy cases permit no regression from the accepted baseline;
- routine quality cases must meet a stated threshold and remain within an agreed change band;
- cost, latency, and tool-call counts must stay within explicit ceilings.
Keep the pull-request suite small enough to finish promptly, and run the broader dataset nightly or before a production release. A slow suite that engineers bypass protects nothing.
Diagnose failures without chasing the average
When a gate fails, group results by dataset tag, changed component, and failure type, and ask what changed before editing the prompt.
A schema failure may come from a renamed field, a retrieval failure from the index snapshot, a grader decline from an ambiguous rubric, a latency jump from an extra tool loop. The prompt is one suspect among several.
Save the candidate trace beside the baseline trace. A diff should show changed tool selections, arguments, retrieved documents, state transitions, grader reasons, tokens, and timing, so each component's owner has something concrete to inspect.
Treat flaky tests as defects. Pin every controllable input, then check whether the criterion allows legitimate variation. If the behavior itself is unstable, keep the case visible and block high-risk releases until you understand it. Re-running a red build until it turns green hides the problem.
Report infrastructure failures separately. A timeout from a fake adapter tests recovery logic, while a real vendor outage during an integration run says nothing about response quality and should not pass as a model regression.
Common evaluation mistakes
Tests cover only happy paths
Incidents sit at boundaries. Include missing identifiers, conflicting instructions, denied permissions, empty retrieval, tool timeouts, and repeated requests.
One model grader decides everything
Leave numeric limits, tool authorization, and schema validity to deterministic code.
The test set changes with the release
If the candidate and its dataset change together, scores are not comparable. Review dataset changes separately and rerun the accepted baseline on the revised set.
The test ignores the action trace
A correct final answer can follow an unsafe action. Test side effects and their order directly.
The team optimizes for the benchmark
Keep a holdout set and add fresh production cases. Engineers who keep tuning against the same visible examples end up measuring familiarity.
Verify that the suite protects a release
Break the build on purpose three ways before relying on the gate.
Introduce a schema break and confirm deterministic checks fail before model grading starts. Alter a tool description so the agent picks a forbidden write and confirm the trajectory gate blocks it. Weaken a policy instruction and confirm quality checks catch unsupported approval or refund claims.
Hand the report to an engineer who did not build the runner. They should name the failing case, violated criterion, changed component, and baseline difference without reading raw logs. If they cannot, fix the report before adding tests.
Track escaped failures after launch. Each incident should produce a sanitized fixture, an assertion or rubric, and an owner, so the suite becomes a record of the workflow's real risks.
Put the first gate in place
Take one high-value workflow and collect ten routine cases, ten boundary cases, and ten past or plausible failures. Write the hard invariants first and add judgment-based graders only where needed. Pin the candidate and baseline manifests, run both several times, and block the next release on permission, schema, and side-effect regressions.
That gate will not prove the workflow perfect. It gives the team a repeatable way to decide whether a change is safer than what is in production. Add every confirmed escape to the dataset.
References
- OpenAI evaluation guide: supports task-specific eval design, representative test data, graders, and continuous evaluation.
- Anthropic evaluation guidance: supports defining success criteria, test cases, edge cases, and grading methods.
- LangSmith evaluation documentation: supports offline and online evaluation, datasets, evaluators, experiments, and trajectory evaluation.
- tau-bench paper: introduces pass^k and reports agent inconsistency across repeated trials.
- Promptfoo documentation: provides an implementation reference for assertions and automated evaluations in CI/CD.
- Zheng et al., Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena for judge agreement with humans and position, verbosity and self-enhancement bias.
- Chen, Zaharia and Zou, How is ChatGPT's behavior changing over time? for the 84% to 51% shift between March and June 2023 GPT-4 versions.