- An external scorer POSTs each sampled trace to your own HTTP endpoint and uses its verdict.
- A code scorer runs a script you write (Python or JavaScript) inside the engine itself - no endpoint to host.
custom-evaluators.)
Code scorers
Scorers → New scorer → Code scorer. The script defines one function:async function handler(...),
await trace.getSpans({ spanType: ["llm"] })). A returned score below the scorer’s alert
threshold (default 0.5) raises a signal; every scored check is recorded in the scorer’s event
history either way, and None/null skips the trace entirely.
trace.get_spans() returns the trace’s own span subtree (root first, start-time ordered). Each
span carries span_id, parent_span_id, name, input, output, error, model,
latency_ms, input_tokens, output_tokens, tool_calls, metadata, started_at, and a
derived type: llm (a model is recorded), tool (tool calls recorded), retrieval
(metadata.kind == "retrieval"), or span (anything else).
Dry run in the dialog executes the script against a synthetic two-span sample (one root, one
llm span) and shows the score it returns.
External scorers
Request (engine → your endpoint,POST, JSON, schemaVersion: 2):
trace (the v1 keys -
input/output/error/toolCalls - are exactly where they always were, so v1 endpoints keep
working untouched), and the trace’s span subtree under spans with the same per-span fields and
derived type values code scorers see (llm/tool/retrieval/span).
Response your endpoint must return:
matches is the only field that decides anything. reason and score are both optional and purely informational - recorded and shown alongside the resulting signal, but neither affects whether one is raised. A minimal example endpoint (Flask):
Setup and behavior
Build it from the dashboard: Scorers → New scorer → External scorer. The dialog documents the request/response contract in place, and Dry run sends a synthetic v2 payload to your URL so you can confirm it responds correctly before saving.Distinct from
monitor_profiles.channels’ "webhook:<url>" notification targets - those fire-and-forget a message after a signal is raised and never consume a response. A custom evaluator’s response is the detection result, awaited synchronously (up to the 8-second timeout)./agent-monitoring/custom-evaluators[/:id] (code scorers pass {"kind": "code", "language": "python"|"javascript", "script": "...", "alertBelow": 0.5}), plus POST /agent-monitoring/custom-evaluators/dry-run (transient - {"url": ...} for external, {"kind": "code", "language": ..., "script": ...} for code - the live response back out, nothing persisted).
