Continuous evaluation
Continuous evaluation scores your traffic as it arrives: every time new traces are ingested, Neens samples them and runs your continuous judges against the sample — no manual runs, no schedules to manage. It’s how an agent accumulates a live quality signal (and a failure set for clustering) without anyone pressing a button.
At a glance
| Where | The Judges page — a scorer with Apply for set to Continuous (score new traces) |
| What runs by default | Primary Score, the out-of-the-box composite every agent ships with |
| Needs | An LLM connection (Settings → Connections) — see LLM connections |
| Produces | Score rows, written moments after each trace arrives |
| Cost controls | Percentage sampling per scorer + a per-scorer daily cap (default 500 traces/day) |
| When nothing scores | The run ends Skipped with a reason code — transient reasons are retried, permanent ones are not |
How it works
After a batch of traces is ingested and persisted, Neens checks the agent’s enabled scorers whose trigger is Continuous (score new traces) and, for each one:
- Samples the just-arrived traces at the scorer’s effective sampling rate (a coin flip per trace at the configured percentage).
- Applies the daily budget. Each continuous scorer has its own daily cap
(
max_scored_per_day); once a scorer has dispatched its cap for the day, further traces are skipped until the next UTC day. Budgets are per scorer, so one continuous judge can never starve another’s. - Creates a small eval run scoped to exactly the sampled traces and queues it for the eval worker — the same scoring path, retries, and run outcomes as a manual run. These runs are attributed as Automated in run history and the Activity feed.
- Writes scores. Verdicts land in the score catalogue with source
llm_judge(orcompositefor composites). A composite like Primary Score inline-scores any component that has no score yet, so its component metrics fill in as byproducts.
Continuous evaluation is best-effort by design: it runs after ingestion completes and can never block, slow down, or fail a trace write. If scoring hiccups, the traces are still safely stored.
Scores are asynchronous. A trace appears on the Traces page immediately; its scores land shortly after — typically seconds, longer under queue depth or a slow LLM provider. Expect a brief window where a new trace is visible but not yet scored.
Primary Score, cold start, and steady state
Every new agent ships with one continuous scorer enabled: the Primary Score composite (Faithfulness + Answer Relevancy + Coherence). Its defaults balance signal against cost:
| Phase | Sampling rate | Why |
|---|---|---|
| Cold start — until 200 traces have been dispatched | 100% | A brand-new agent builds a scoreable failure set fast. |
| Steady state — after that | 5% | A representative ongoing sample. |
| Always | Capped at 500 traces/day | Percentage sampling alone isn’t safe on a firehose. |
The cold-start boost graduates automatically once the threshold is crossed — if your scoring rate drops from “everything” to “a sample” after the first couple hundred traces, that’s expected, not a failure.
What continuous evaluation is not
- It is not retroactive. A continuous scorer only scores traces that arrive after it’s enabled (or resumed). To score existing traces, use Run now on the scorer — a one-off run over eligible historical traffic.
- It is not the Issue Classification path. The built-in Issue Classification judge also shows up as a continuous scorer, but it’s dispatched by a background sweep (every 30 minutes) over the agent’s failure set only — not on ingest, and never over healthy traffic. See Issues & failure modes.
Enable, pause, and resume
Make a scorer continuous
On the Judges page, Configure the judge and set Apply for to Continuous (score new traces). The What traces to score percentage becomes the continuous sampling rate. Saving configuration never runs anything by itself — new arrivals start being scored from that point on, and the hint reminds you: “Use ‘Run now’ to also score existing traces.”
A user-created continuous scorer that ships without a daily cap automatically gets the default
(500 traces/day) so an unbounded on-ingest scorer can’t become a cost surprise.
Pause when needed
In the Enabled scorers table, a continuous scorer’s toggle reads Pause / Resume (manual scorers say Disable / Enable). Pause stops automatic scoring of new traces, keeps every existing score, and cancels any in-flight run.
Resume — forward only
Resume turns automatic scoring back on for traces arriving from that moment. Traffic that came in while paused is not backfilled; use Run now if you want it scored.
Sampling and cost
Every trace a continuous scorer grades costs at least one LLM call on the agent’s configured LLM connection — and a composite costs one call per component that doesn’t already have a score (up to three for Primary Score). Your levers:
- Sampling percentage (per scorer, in Configure → What traces to score) — the fraction of arriving traces that get scored.
- Daily cap (per scorer) — the hard ceiling regardless of traffic volume. Resets at midnight UTC.
- Scoring connections (pool) — spread continuous load across several connections to dodge provider rate limits.
If the agent has no LLM connection, continuous runs still fire, but they score nothing:
the run finishes Skipped and every target records the reason no_llm_connection. Neens
never borrows another agent’s credentials, and ingestion is unaffected. Configure one under
Settings → Connections (LLM connections).
When a run is Skipped
A run finishes Skipped when it reached the end having scored nothing — no target succeeded and none failed either. It is deliberately neither green nor red: skipping can be perfectly legitimate (there is genuinely nothing to score yet) or it can mean something is misconfigured. The reason tells you which.
Every skipped target carries a reason code plus a plain sentence, visible on the Activity feed row and in its Details drawer. A run can carry more than one code; the drawer lists each with how many targets it accounts for.
“Unknown” is a real answer. If Neens cannot classify a skip it says
unknown — “Neens could not determine why this target was skipped” — rather than showing a
blank. An unknown skip is a gap in Neens, not in your configuration: please report the run id.
Runs that predate skip reasons are also shown as unknown; their history was never rewritten to
guess a cause.
Skip reason codes
| Code | What it means | Retried? | What to do |
|---|---|---|---|
no_llm_connection | No LLM connection is configured or visible to this agent, so the judge could not run. | No | Add a connection in Settings → Connections. |
llm_connection_error | The connection could not be resolved on this attempt (a credential-store or database blip). | Yes | Nothing — Neens retries. If it persists, check the connection’s credential. |
component_judge_missing | A judge the composite aggregates no longer exists. | No | Re-point or remove that component in the composite’s definition. |
component_not_scoreable | A component isn’t an LLM-prompt judge and has no existing score for the target. | No | Score that component separately, or drop it from the composite. |
component_llm_error | A component’s LLM call kept failing (timeout / rate limit / provider error) after the per-target retries. | Yes | Nothing immediately. A persistent failure points at the provider or your quota. |
component_scoring_failed | A component returned output Neens could not read as a score. | No | Check that component judge’s prompt and output spec. |
insufficient_components | Fewer component scores were available than the composite’s min_components requires. | No | Lower min_components, or make the missing components scoreable. |
expression_component_missing | The composite’s expression references a component that had no value for that target. | No | Give the component a score, or set missing_policy. |
no_taxonomy | Issue Classification has no failure modes to classify against yet. | No | Add failure modes under Taxonomy. This one is benign. |
target_not_found | The target trace wasn’t visible yet when the run resolved its targets (ingest was still landing). | Yes | Nothing — Neens re-dispatches it. |
unknown | Neens could not classify the skip. | No | Report the run id. |
Are skipped targets retried?
Transient causes are; permanent ones are not. A background sweep (every 30 minutes) looks for terminal runs holding targets skipped for a retryable reason and opens a new run scoped to exactly those targets — never a re-drive of the finished run, so the original’s history and counts stay intact. Both runs appear in the Activity feed, and the drawer links them (“Retried as …” on the original, “Retry 1 of 2 — re-attempt of …” on the new one).
Every retry is bounded twice:
- Attempts. The retry chain is capped, so a target is attempted at most three times, ever.
- Rate. The wait before the first retry is 10 minutes and doubles each attempt (10 → 20 → 40 minutes), so a provider that is already struggling isn’t hammered. At most 20 runs per agent are re-dispatched per sweep.
A permanent reason is never retried — it would produce the identical outcome every time and
cost real money doing it. Neither is an unknown skip: Neens cannot argue the condition will
clear, so it surfaces it loudly instead of looping on it.
Debugging a skipped run, concretely
Open the run
Go to Activity, filter Status → Skipped, and click Details on the run.
Read the header
The drawer’s header lists each reason with a count, whether it is retryable, and what to do — for example:
3× no_llm_connection not retryable
No LLM connection is configured for this agent, so the judge could not run.
Add an LLM connection in Settings → Connections, then re-run the scorer.
No retry: none of these reasons clear on their own.Check an individual target
Each target card carries the same code plus the specific detail the scorer recorded — e.g. “only 1 of 3 components available (min_components=2)” — so you can tell an agent-wide misconfiguration from one odd trace.
Fix and re-run
Permanent reasons need an action from you (a connection, a taxonomy, a component). After fixing it, click Run now on the scorer to score the targets that were skipped — continuous scoring itself is forward-only.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| New traces aren’t being scored at all | The scorer is paused, or Apply for is Manual (run on demand) | Check the Enabled scorers table: Resume it, or Configure and switch to Continuous (score new traces). |
New traces aren’t scored, runs show Skipped with no_llm_connection | No LLM connection visible to the agent | Add a connection in Settings → Connections, then Run now to score what was skipped. |
Runs show Skipped with insufficient_components | The composite could not gather min_components scores for those traces | Lower min_components on the composite, or make the missing component scoreable. |
A run is Skipped with reason unknown | Neens could not classify the cause (or the run predates skip reasons) | Report the run id — an unclassified skip is a gap in Neens, not in your setup. |
| Only some new traces get scored | Percentage sampling — that’s the design | Raise the sampling percentage in Configure if you want more coverage (mind the cost). |
| Scoring stops partway through the day, resumes tomorrow | The scorer’s daily cap was reached | Raise the cap deliberately, or accept it as your cost ceiling; the budget resets at midnight UTC. |
| Scoring rate suddenly dropped after the first ~200 traces | Cold-start graduation (100% → steady rate) | Expected behavior for Primary Score in a new agent — not an outage. |
| Scores trail traces by minutes | Eval queue depth or a slow LLM provider | Normal under load; check the run’s status on the Judges page. Transient provider errors are retried automatically. |
| Old traces never got scored after enabling | Continuous scoring is forward-only | Click Run now on the scorer to score existing eligible traces once. |
Related: Judges · Scores · Pre-prod evaluations · Metrics catalogue