Skip to main content
Failproof AI Observability can automatically score every finished agent run for quality: you supply a small scoring service, and Observability handles the rest. Use it to track the dimensions you care about (helpfulness, tool efficiency, factuality, safety; you choose), catch regressions early, and compare agents or environments at a glance. Scoring is opt-in: the pipeline does nothing until you set EVALUATOR_ENDPOINT on the server.
Note: You define the score dimensions. Your evaluator can return any numeric keys it likes; Observability stores, trends, and displays whatever you send back.

At a glance

  1. Write a scorer. Stand up a small HTTP service that reads a session transcript and returns scores. Observability ships a working reference you can copy. See Writing an evaluator with the SDK.
  2. Point Observability at it. Set EVALUATOR_ENDPOINT (and a shared EVALUATOR_TOKEN) on the server process.
  3. Watch the scores land. Every completed session is scored automatically; results show up on the session detail page, the sessions grid, and saved dashboards.
A session detail view with the evaluation summary, per-dimension score bars, and reasoning text in the right rail Once an evaluator is configured, each completed run is scored and the results appear in the session’s right rail: the summary on top, then per-dimension score bars with reasoning.

How it works

When the Observability SDK emits an agent_end event for a session, the server schedules an evaluation. It then POSTs the full event transcript to your evaluator service, which can either:
  • Return the result inline with {"status":"done", "scores":{...}, "reasoning":{...}, "summary":"..."}. The result is appended to the session’s evaluation timeline. reasoning and summary are optional.
  • Defer with {"status":"pending", "job_id":"abc-123"}. Observability then calls GET {EVALUATOR_ENDPOINT}/evaluate/abc-123 until your evaluator returns {"status":"done", ...} or {"status":"error", "error":"..."}. The polling cadence is per-job: a pending response may include next_poll_secs to override; otherwise Observability uses the default_poll_interval_secs value from GET /config; otherwise the server falls back to EVALUATOR_POLLING_INTERVAL_SECS (default 10s). All values are clamped to [1s, 1h].
Sessions that never emit agent_end (for example, a crashed agent process) can also be picked up: the evaluator’s GET /config may return {"inactivity_timeout_secs": 1800}, and Observability will evaluate any session that has gone idle for that long. Set the field to null or omit it to disable this fallback. The pipeline is fully no-op when EVALUATOR_ENDPOINT is unset. A session can accumulate multiple terminal evaluations over time: each agent_end event (and each manual re-eval from the dashboard) appends a fresh evaluation row. This is the supported way to evaluate a resumed conversation: a user ends an agent, comes back later, sends more events, ends the agent again, and a second evaluation runs against the full updated transcript. The dashboard renders the most-recent evaluation as the headline and the prior evaluations as a collapsible timeline. While one evaluation is running for a session, additional agent_end events for that session are ignored; the next one after the running evaluation completes will enqueue a fresh evaluation as usual. The inactivity fallback re-engages on resumed sessions too: if new events arrive after a previous terminal evaluation and the session then goes idle past inactivity_timeout_secs, a fresh evaluation is enqueued. Transient failures (5xx, 429, timeouts, network errors) are retried with exponential backoff up to EVALUATOR_MAX_ATTEMPTS; 4xx responses are terminal. Observability is safe to run with multiple horizontally-scaled server instances; work is partitioned so the same session is never dispatched twice concurrently.

HTTP contract

Every authenticated route uses bearer token auth. The same value must be configured on both sides:
  • Observability server: env var EVALUATOR_TOKEN
  • Evaluator service: configured the same way (the agenteye-evaluator SDK reads EVALUATOR_TOKEN by convention)
If EVALUATOR_TOKEN is unset, the server sends no Authorization header; the evaluator may then accept anonymous requests, which is fine for an internal-only network but discouraged on the public internet.

Routes the evaluator must serve

EvalRequest body sent by the server

Response shapes

Sync (done):
reasoning (a per-score justification map) and summary (an overall one-paragraph narrative) are both optional. Keys in reasoning should mirror keys in scores; the dashboard renders each entry inline under its score bar. Older evaluators that return only scores continue to work unchanged; reasoning and summary simply read as null and the corresponding UI affordances are omitted. Async (deferred):
next_poll_secs is optional; if omitted the server falls back to the evaluator’s default_poll_interval_secs from /config, then to its own EVALUATOR_POLLING_INTERVAL_SECS env var. Terminal evaluator-side error:
The server treats any other 2xx body as a protocol error and records a terminal error for the session.

Writing an evaluator with the SDK

You don’t have to implement the HTTP contract by hand. The agenteye-evaluator Python package gives you a typed FastAPI wrapper that handles auth, routing, and the request/response shapes for you. Failproof AI Observability also ships a working reference evaluator that scores helpfulness, tool_efficiency, and factuality from the shape of the transcript. Copy it as a starting point and swap in your own logic: an LLM judge, a rule engine, whatever fits your quality bar. Minimum viable evaluator:
The app instance runs under any ASGI server, so uvicorn module:app starts it. For evaluators that need to defer expensive work, return JobPending instead and register a @app.job_lookup handler; the Observability server polls GET /evaluate/{job_id} until you return a terminal status or the EVALUATOR_MAX_POLL_DURATION_SECS cap (default 1 h) elapses. The full API reference, async pattern, and event schema are documented in the agenteye-evaluator SDK’s README.

Running your evaluator

The evaluator is your service — Failproof AI Observability does not ship a default evaluator, so you build and run it wherever you run your own services. It runs under any ASGI server (for example uvicorn my_evaluator:app); serve the /health, /config, and /evaluate routes from the HTTP contract, then point the server at it (see Configuring the server). Once the evaluator is reachable, GET /health returns {"status":"ok"}. After an agent runs end-to-end, GET /evaluations on the server returns a row with status: "done" and the scores your evaluator produced.

Configuring the server

Set on the server process: To turn on automatic scoring, set both EVALUATOR_ENDPOINT and EVALUATOR_TOKEN on the server, then restart it to pick up the change. With EVALUATOR_ENDPOINT unset the pipeline stays a no-op. The tuning knobs above are optional; set the corresponding environment variables on the server only if you need to override the defaults.

API reference

Filtering by score range: score_filters

GET /evaluations accepts an optional score_filters parameter that narrows results by numeric values inside the scores object. The parameter is a comma-separated list of key:min..max entries; either bound may be omitted. Multiple entries combine with logical AND. Rows where the named key is absent or non-numeric are excluded. A request may carry at most 20 filter entries; exceeding that returns HTTP 400. Examples:
Each /evaluations response object has these fields:

Permissions

The bootstrap admin (ADMIN_KEY, ADMIN_EMAIL) automatically receives these.

Viewing results

  • /sessions/<id>: events timeline + a right rail showing the session’s scores and any error from the dispatch attempt. If your key has evaluations:trigger, a re-evaluate button appears next to the export button, useful for sessions that never emitted agent_end, or for refreshing scores after deploying a new evaluator. The dashboard polls for the new result and updates the right rail when it lands.
  • /sessions: filterable session grid; the score column shows each session’s evaluation status and scores at a glance.
  • /dashboards: saved eval-health views (see Dashboards below).
The Sessions grid with per-session evaluation status pills and colour-coded score badges (helpfulness, factuality, tool_efficiency, safety, coherence) The sessions grid shows each run’s evaluation status and scores at a glance; red/amber/green badges make low scores jump out.

Dashboards

The Dashboards page (/dashboards) lets you save a combination of evaluation filters as a named, reusable view and watch how that slice of evaluations is doing at a glance. Dashboards are shared across your whole organization; everyone with dashboards:read sees the same set. Each dashboard pins:
  • Filters: the same controls as the sessions page: environment, status, agent, a rolling time window, and score-range filters (key:min..max).
  • A display configuration: which score keys to feature, the green/amber/red health thresholds, which panels to show, and whether to collapse to the latest evaluation per session.
Each card shows the number of matching sessions, a done/error/timeout breakdown, the average of each featured score, and a small trend sparkline. Opening a dashboard shows the full-size panels; “open in sessions” drops you into the sessions page pre-filtered to exactly that slice. Metrics are computed server-side over the whole matching set (via GET /evaluations/aggregate), so the numbers are exact rather than sampled. An eval-health dashboard with average-score bars per evaluator dimension, a tool ok-vs-error breakdown, top tools, and an events-per-hour trend Permissions: viewing needs both dashboards:read and evaluations:read; creating and editing needs dashboards:write; deleting needs dashboards:delete. The bootstrap admin receives all of these automatically.

Troubleshooting

Sessions exist but no evaluations are created. Confirm EVALUATOR_ENDPOINT is set on the server process, that the server and evaluator share the same EVALUATOR_TOKEN value, and that the evaluator’s /health endpoint is reachable from the server. With EVALUATOR_ENDPOINT unset the pipeline is a no-op. In-flight evaluations pile up. Query GET /evaluation-jobs to see the in-flight queue. Inspect attempt_count, next_attempt_at, and last_error on each row. Common causes: evaluator service unreachable or returning 5xx (retried with backoff), wrong EVALUATOR_TOKEN (401 is terminal), or an async evaluator that returns pending indefinitely (see below). Sessions completed but no terminal evaluation. Query GET /evaluation-jobs?status=polling; the result may still be in flight. If a job is stuck in pending, the server is having trouble reaching the evaluator; check that the evaluator is up and that EVALUATOR_TOKEN matches. HTTP 401 from evaluator: invalid bearer token. The EVALUATOR_TOKEN on the server does not match the value the evaluator service is configured with. They must be identical. Async evaluator returns pending forever. The server polls GET /evaluate/{job_id} until the evaluator returns done or error, or until EVALUATOR_MAX_POLL_DURATION_SECS (default 1 h) elapses. After the cap the evaluation is recorded as timeout and removed from the in-flight queue. Raise EVALUATOR_MAX_POLL_DURATION_SECS if your evaluator legitimately needs longer than the default.

Next steps

  • Evaluator agent skill: have a coding agent design your dimensions against real sessions and build this service for you.
  • Python SDK: emit the agent_end events that trigger scoring.
  • API keys: the evaluations:read and evaluations:trigger permissions.
  • Audits: Observability’s other automated quality feature, for policy-based review.