Invarium Blog

Catching AI Agent Regressions Before Your Users Do

By Adish Jain · ·

The scariest agent bugs don't come from code you wrote. They come from a model you upgraded, a prompt someone tweaked, or a library that changed underneath you — and the agent's behavior quietly shifted while every string-match test stayed green.

This is AI agent regression testing: pin known-good behavior as a baseline, then catch drift the moment it lands. This post walks the full loop with a real, reproducible example — and ends with an honest note about when regressions don't show up (because pretending otherwise would be dishonest).

Background on asserting behavior instead of text: How to test AI agents: behavioral contracts, not string matching.

What a behavioral regression looks like

Take a LangGraph booking agent. Version 1 does the right thing: check availability → book the table → confirm. Then a routine change lands — a model upgrade, say — and version 2 gets chatty and confident. It skips the booking tool entirely but still tells the user "Your reservation is confirmed!"

To exact-text tests, nothing changed — the reply still contains "confirmed." To the customer, everything changed — there's no table. This is a behavioral regression, and it's invisible unless you're watching behavior.

flowchart TB
    subgraph V1["v1 — good (baseline)"]
      A1[search_restaurants] --> A2[book_table] --> A3["'Confirmed: BC-2-TONIGHT'"]
    end
    subgraph V2["v2 — regressed"]
      B1[search_restaurants] --> B3["'Your reservation is confirmed!'"]
      B1 -.->|book_table SKIPPED| B3
    end
    V1 -->|bless as baseline| BASE[(baseline)]
    V2 -->|compare| BASE
    BASE --> D["REGRESSION:<br/>book_table 100% → 0%"]
    classDef bad fill:#a61e4d,color:#fff,stroke:#6d1030;
    class D bad;

The workflow: bless, then compare

Regression detection is two commands. First, bless a known-good run as the baseline:

invarium bless framework_examples/langgraph_booking_regression

The contract asserts behavior, not wording — it must call book_table, in order, and must not claim confirmation without the tool:

@agent_test(runs=5, agent_factory=build_graph)
def test_booking_agent(graph):
    result = adapter.run(graph, "Book a table for 2 tonight")

    check = expect(result, collect=True)
    check.used_tool("search_restaurants")
    check.used_tool("book_table")
    check.used_tools_in_order(["search_restaurants", "book_table"])
    check.did_not_claim_confirmation_without_tool("book_table")
    check.verify()
    return result

On the good version, all five runs pass:

[PASS] test_booking_agent
  Runs 5   Passed 5   Success 100.0%
  Tools        book_table 100.0%, search_restaurants 100.0%
  Path         search_restaurants -> book_table (100.0%)

Now the regressed version ships. Run the same test and Invarium doesn't just fail — it produces a behavior diff against the baseline:

[REGRESSION] Baseline comparison
  Summary      Regression detected in 1 of 1 matched test(s).

[REGRESSION] test_booking_agent
  Success      100.0% -> 0.0%
  Step delta   -2.0
  Failure cats missing_required_tool:5, wrong_tool_order:5, unsupported_success_claim:5
  Tool drop    book_table 100.0% -> 0.0%
  Path         search_restaurants -> book_table (100.0%) -> search_restaurants (100.0%)

Read that Tool drop line again: book_table 100.0% -> 0.0%. That's not "your quality score dipped." It's a specific, actionable diagnosis — the agent stopped calling the booking tool — the kind of thing you can drop straight into a bug ticket or a CI failure comment. And because this example is deterministic (no API key needed), it reproduces identically on your laptop or in CI.

Wire it into CI

Because the baseline lives in the repo, the compare runs in your pipeline. A regression fails the build with the diff above in the step summary — so drift is caught in the pull request that introduced it, not in production:

invarium test framework_examples/langgraph_booking_regression   # non-zero exit on regression

That's the whole value proposition: the model changed, and you found out from a red build, not a support ticket.

An honest note: sometimes the regression doesn't appear

Here's something most tool demos won't tell you. While building the real, live-model examples, we tried to induce a regression by swapping in a cheaper model and relaxing the system prompt — expecting the agent to stop grounding its answers in web search.

It didn't. On the "current CEO of OpenAI" question, both gpt-4o-mini and gpt-3.5-turbo kept calling the search tool even under a deliberately weakened prompt. Well-aligned models are cautious about time-sensitive facts, so no regression surfaced. We didn't fake one to make a prettier demo — we recorded the truth.

The lesson is the actual point of behavioral testing: you find out which case you're in from the behavior, not from guessing. A weak prompt reliably drops search on stable facts a model is confident it already knows (try "Who wrote Hamlet?"), and reliably does not on volatile ones. You would never know which without measuring — and measuring is exactly what a blessed baseline plus repeated runs does for you.

Start catching drift

pip install invarium

Bless a baseline today, and the next time a model upgrade quietly changes your agent's behavior, you'll get a Tool drop: 100% -> 0% in your pull request — not an incident in production.

Invarium on GitHub · Back to: How to test AI agents

Open source · MIT

Test what your agent did, not what it said.

Invarium is pytest for AI agents — assert on tool usage, ordering, step budgets and honest success claims, then catch regressions against a baseline.

pip install invarium

⭐ Star Invarium on GitHub →