GuidesStress tests

Stress tests

A stress test turns your failure-mode taxonomy from a backward-looking record into a forward-looking asset: pick the failure modes your agent already got caught on, and Neens generates fresh synthetic test scenarios grounded in those real failures — then hands them straight to the pre-prod evaluation gate so you can attack your next candidate with last quarter’s failures before it ships.

Where a pre-prod eval replays a golden dataset you curated by hand, a stress test builds that golden dataset for you from the sessions where the agent actually failed — so the set that guards a release grows automatically as your taxonomy does.

At a glance

WhereThe Stress tests tab on the Pre-prod Evals page
Key API routesPOST /scenario-suites, GET /scenario-suites, GET /scenario-suites/{id}, POST /scenario-suites/{id}/regenerate, POST /scenario-suites/{id}/launch, DELETE /scenario-suites/{id}
AuthThe same nk_live_ agent API key that drives pre-prod evals — so CI can run the whole generate → launch → gate flow with one credential.
NeedsOne or more confirmed failure modes with exemplar traces (Issues & failure modes). An LLM connection makes the generated scenarios richer; without one, a suite still builds from the deterministic replay floor.
ScopeAgent-scoped, like all your data; generation runs on the eval worker fleet.
ProducesA scenario suite whose scenarios become an immutable golden dataset version, which a one-click launch replays as a normal pre-prod run (scores tagged source: preprod — their own score type, filterable out of production widgets by Score source).

How it fits together

A stress test is three moving parts, and the last one is just a pre-prod eval:

  1. A scenario suite is generated from failure modes you pick. Each scenario is a user input plus an expected_output — a behavioral assertion of what a correct response must do or avoid (a synthetic prompt has no ground-truth answer, so the judges grade behavior, not a fixed string).
  2. The suite’s scenarios are materialized as an immutable golden dataset version — the same frozen-golden contract a hand-curated dataset uses.
  3. Launching the suite creates a pre-prod eval run over that golden version. From there everything is a normal pre-prod eval — Neens calls your agent, or your own harness emits the traces — scored by your judges, compared to a baseline, and gated on regressions and pass rate.

So a stress test adds no new scoring, comparison, or gate machinery — it feeds the pre-prod gate you already know.

Generate a suite

Confirm the failure modes you want to attack

A stress test grounds every scenario in real exemplar traces — the sessions where the agent actually exhibited that failure mode. Curate and confirm the modes you care about first in Issues & failure modes; confirmed modes carry the strongest exemplar evidence and float to the top of the picker.

Open the Generate stress test wizard

On Pre-prod Evals → Stress tests, start a new suite. The wizard asks for:

FieldPurpose
NameA human name for the suite (e.g. Refund-policy regressions).
Failure modesOne or more of your agent’s non-archived modes. Each row shows its severity and how many exemplar traces it carries — more exemplars means more grounding.
Scenarios per modeHow many new (LLM-authored) scenarios to request for each mode. Default 5; capped at 25.
StrategiesWhich kinds of synthetic scenario to ask for — paraphrase, escalate, boundary (explained below). The deterministic replay floor is always available and isn’t picked here.
Include replayOn by default — always add the deterministic replay scenarios (the literal past-failing inputs) alongside the LLM-authored ones.

Let it generate

Submitting kicks off generation on the eval worker fleet. For each selected mode, Neens gathers its real failing sessions, reduces each to the user’s input, the agent’s (wrong) output, and any error, and asks the tenant’s LLM to author fresh scenarios that probe the same weakness from new angles — never copying the exemplars verbatim. It always adds the deterministic replay floor so the suite is runnable regardless.

The suite moves pending → generating → ready (or failed). While it generates you can inspect it via GET /scenario-suites/{id}.

Via the API — the whole body except name and failure_mode_ids has a default:

curl -sf -X POST "$NEENS_BASE_URL/scenario-suites" \
  -H "Authorization: Bearer $NEENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Refund-policy regressions",
    "failure_mode_ids": ["fm_abc", "fm_def"],
    "per_mode": 5,
    "strategies": ["paraphrase", "escalate", "boundary"],
    "include_replay": true
  }'

Optional fields: per_mode (LLM scenarios per mode, 1–25), strategies (subset of paraphrase/escalate/boundary), include_replay (default true), and connection_id to pin a specific LLM connection for generation.

Only failure modes that belong to your agent are accepted — a request that names none of your agent’s modes is rejected (422), so a suite can never be seeded from another agent’s taxonomy.

Review the scenarios

A ready suite lists its generated scenarios. Each one carries:

  • Input — the synthetic end-user prompt.
  • Expected output — the behavioral assertion the judges grade against (“a correct response must not exhibit the ’…’ failure mode…”).
  • Strategy — which kind of scenario it is (replay, paraphrase, escalate, or boundary).
  • Source failure mode — which mode it was generated from, so every scenario is traceable back to the real failure that inspired it.

Not happy with the set — added more exemplars, or just configured an LLM connection? Regenerate the suite (POST /scenario-suites/{id}/regenerate) to rebuild it, optionally overriding per_mode, strategies, include_replay, or connection_id. Regeneration replaces the suite’s scenarios and snapshots a fresh golden version.

The strategies

Each scenario is generated with one strategy. Three are LLM-authored variations grounded in the mode’s real exemplars; the fourth is a deterministic floor that needs no model:

StrategyWhat it producesNeeds an LLM
replayRe-uses the literal past-failing inputs verbatim — the exact prompts that triggered the mode last time. Deduplicated across the suite.No — deterministic.
paraphraseRewords a real failing input: same intent, fresh surface wording.Yes
escalateA harder, more adversarial variant that pushes the failure mode further.Yes
boundaryAn edge-case input that sits right on the boundary of the failure.Yes

No LLM connection? A suite still builds. With no LLM connection configured, Neens degrades to the replay floor alone — re-attacking the candidate with the real prompts that failed before. It never fabricates a scenario it can’t ground. A mode with no exemplar traces and no model contributes nothing; a suite that ends up with zero scenarios is marked failed with a reason, never left wedged in generating.

Launch a stress test

A ready suite launches as a pre-prod eval run in one step.

Launch the suite

In the Stress tests tab, launch a ready suite. You supply:

  • Version label — required; names the candidate agent version under test (a git SHA, a branch, v2.1). Recorded on every captured trace.
  • Agent endpoint (optional) — an agent endpoint connection. Leave it on Auto and Neens decides how the run executes.

Neens picks the execution mode

  • If the agent has an agent endpoint (or you selected one), Neens calls your agent — once per scenario, recording each response and scoring it. No harness.
  • Otherwise you run your agent — you drive it over the scenarios (by hand or in CI) and the traces it emits are correlated back to the run.

Either way, launching lands you on the created pre-prod run, where scoring, the candidate-vs-baseline comparison, and the gate work exactly as in Pre-prod evaluations.

Via the API, POST /scenario-suites/{id}/launch delegates to the pre-prod eval create endpoint and returns the created run:

curl -sf -X POST "$NEENS_BASE_URL/scenario-suites/$SUITE_ID/launch" \
  -H "Authorization: Bearer $NEENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "version_label": "'"$GIT_SHA"'",
    "baseline": {"kind": "prod_window", "range": "7d"},
    "gate": {"max_regressions": 0, "min_pass_rate": 0.9}
  }'

The launch body accepts the same knobs a pre-prod run does — version_label (required), runner_mode ("push"/"runner", defaulted for you), agent_connection_id, judge_deployment_ids, baseline, and gate. Omit judge_deployment_ids to score with your agent’s Primary Score, like any pre-prod run.

⚠️

A suite can only be launched once it’s ready — launching one that’s still generating (or that failed) returns 409. Deleting a suite removes it and its synthetic dataset, but a run you already launched snapshotted its scenarios and is unaffected.

Read the gate

Because a launched stress test is a pre-prod eval run, everything in Pre-prod evaluations applies unchanged:

  • Each scenario’s captured trace is scored by your judges; scores are tagged source: preprod so they stay out of production metrics and dashboards.
  • A scenario passes when the mean of its judge scores is at or above 0.7.
  • The run is compared to a baseline (a previous run or a production window), and any item that passed the baseline but fails the candidate is a regression.
  • The gate turns the run into a pass/fail verdict on your max regressions and min pass rate thresholds — and emits a Pre-prod regression insight when something regresses.

Review it on the run’s page, or drive and read the gate from CI (below).

In CI: generate → launch → gate

Because every route accepts the nk_live_ agent API key you already use to send traces, a CI job can build a stress-test suite from your live taxonomy and gate a release on it with a single credential — no login, no session token. Set the shared pre-prod environment variables once (see Pre-prod evals → Authentication):

export NEENS_BASE_URL="https://neens.example.com"
export NEENS_API_KEY="nk_live_..."   # your agent (ingest) API key

Then generate a suite, wait for it to become ready, and launch it against the commit under test:

# 1. Generate a suite from the confirmed failure modes you want to guard against.
SUITE=$(curl -sf -X POST "$NEENS_BASE_URL/scenario-suites" \
  -H "Authorization: Bearer $NEENS_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"CI stress test","failure_mode_ids":["fm_abc","fm_def"]}' | jq -r .id)
 
# 2. Poll until the suite is ready (generation runs on the worker fleet).
until [ "$(curl -sf -H "Authorization: Bearer $NEENS_API_KEY" \
  "$NEENS_BASE_URL/scenario-suites/$SUITE" | jq -r .status)" = "ready" ]; do sleep 5; done
 
# 3. Launch it as a pre-prod run against the commit under test, gating on regressions.
curl -sf -X POST "$NEENS_BASE_URL/scenario-suites/$SUITE/launch" \
  -H "Authorization: Bearer $NEENS_API_KEY" -H "Content-Type: application/json" \
  -d '{"version_label":"'"$GITHUB_SHA"'","gate":{"max_regressions":0,"min_pass_rate":0.9}}'

The launch returns a pre-prod run; from there the same exit-code gate contract and neens eval CLI described in Pre-prod evaluations turn the verdict into a passing or failing build step.

On the inline queue (local make demo / tests) generation runs synchronously, so the suite is already ready when the create call returns. On a Celery deployment, generation runs on the worker fleet and you poll GET /scenario-suites/{id} (step 2 above).

Run lifecycle

Suite statusMeaning
pendingCreated; generation is queued.
generatingThe worker is gathering exemplars and authoring scenarios.
readyScenarios generated and snapshotted as a golden version — launchable. Terminal until you regenerate.
failedGeneration produced no scenarios (e.g. the selected modes had no exemplar traces and no LLM was configured). The reason is recorded on the suite.

Troubleshooting

SymptomCauseFix
Create returns 422 “none of the supplied failure_mode_ids belong to this project”The mode ids are from another agent, are archived, or don’t existPass ids from this agent’s non-archived modes (Issues & failure modes)
Suite marked failed with “no scenarios generated”The selected modes have no exemplar traces and no LLM connection is configuredPick modes that carry real exemplar traces, or configure an LLM connection, then regenerate
Scenarios are all replay (no paraphrase/escalate/boundary)No LLM connection resolvedConfigure an LLM connection, then regenerate
Launch returns 409 “suite is not ready yet”The suite is still generating or failedWait for ready (poll GET /scenario-suites/{id}), or regenerate a failed suite
The launched run behaves unexpectedly (scoring, baseline, gate)It’s a normal pre-prod run under the hoodSee Pre-prod evaluations → Troubleshooting