Doing it well
- Score the behaviors that hurt in production. LLM judge scorers run judge criteria (faithfulness, tool accuracy, goal completion) against sampled live traces and whole sessions; patterns catch the deterministic failures (errors, empty responses, PII, latency) at zero LLM cost.
- Tune the sample rate to your volume. Every judged trace is a real LLM call - start at
sample_rate=1.0while traffic is small and you’re validating criteria, then dial down as volume grows. Deterministic patterns stay at 100%: they’re free. - Let users vote. End-user feedback (
client.feedback.report) is the cheapest evaluation signal you’ll ever get - a downvote raises a signal directly, and real-world outcomes measure how often your judges agreed with reality. - Feed findings back into offline tests. Low-scoring traces (and suspiciously perfect ones) become dataset cases - the online/offline loop is what makes both sides better.
client.monitor.signals.
In the dashboard the machinery splits into two surfaces:
- Monitor is the charts - health strip, healthy-rate and signal-volume trends, and per-scorer live judge score charts with their alert thresholds drawn in (click a card for the scored traces).
- Review is where a human passes verdicts on individual signals: a worst-first queue with the occurrence text and judge rationale as evidence, and Confirm / Fixed / Ignore / Wrong judgement verdict actions (Wrong judgement records the correction that feeds Judge Calibration and Tune judge), plus Add to dataset to turn the failure into an eval case. The classic filterable signal table lives under Review → Review signals.
client.monitor.patterns.builder(...).publish(), then reference it by id, or rely on the built-in checks and any patterns your workspace has defined in the dashboard.
This works the same way for an agent built natively in AgentX and for an external agent traced entirely through the Python SDK.
Checking is automatic
Every ingested root trace is checked the moment it arrives - there is no per-call opt-in and no dashboard switch to flip first. Six independent passes run against each trace: pattern and built-in detection, LLM judge scorers, scorer groups, custom/code/external scorers, automation rules, and topic classification. Each pass is opt-in by existing: create a scorer (or enable a template) and it sees every subsequent trace. There is no global sample rate: sampling lives on the scorers that spend LLM budget (each judge scorer’s own rate, the Topics classification rate), so free deterministic checks always see all traffic. The first call totracer.trace(...) for a given agent name auto-creates a reference agent in your workspace - see Tracing if you haven’t wired this up yet - and project-wide defaults live under Platform Settings → Monitoring defaults.
The monitor and pattern_ids kwargs on tracer.trace(...) are modifiers on that default, not the trigger:
monitor=Falseopts that trace out of all six ingest-time passes - pattern/built-in detection, LLM judge scorers, scorer groups, custom/code/external scorers, automation rules, and topic classification. Use it for traffic that is already judged elsewhere, so a second pass doesn’t double the bill.source="eval-run"traces are skipped automatically, even without the flag: the run’s own evaluator already judges each case. An explicitmonitor=Trueoverrides the skip - that is someone deliberately pointing checks at eval traffic.monitor=Truewithpattern_idsrestricts pattern detection to exactly those patterns: the built-in checks and each pattern’s own sample rate are bypassed, and only the named patterns run. Leavingmonitorunset (ormonitor=Truewithoutpattern_ids) keeps the full default sweep.
pattern_ids fully defines what pattern detection checks when provided: only
those named patterns run, the built-in checks are skipped. This mirrors how
scorer_id fully defines an evaluation’s grading config rather than layering
on top of a default.What gets checked
Three separate streams classify a monitored trace: Operational outcomes - facts the trace itself recorded, always on, never configurable, and never raising triage signals (they feed the KPI failure metrics and Top failing instead):
Latency is a distribution metric (Overview’s p95 card, straight from the traces), not a failure
classification.
Scorers - judgments you opt in to from the Scorers page, all off by default. Six built-in
template scorers ship with the engine, all zero-LLM-cost; your own template patterns, LLM judge
scorers, and custom endpoint scorers layer on top:
Human feedback - your users’ votes via
client.feedback.report(trace_id, "down"), raising a
Negative user feedback signal directly (see User Feedback).
Metric semantics: operational outcomes and template/pattern hits classify the RUN (they move
failureRate and the run-outcome breakdown); judge, code, and external scorer verdicts are
evaluator events - they raise signals and keep per-scorer histories without reclassifying the
run. See what moves which metric.Scorer administration as code
Everything the Scorers page does is scriptable viaclient.monitor.scorers - enable the
shipped templates, deploy code/external scorers, and read their check history:
client.monitor.judge_scorers: one scorer = one
judge rubric + an offline profile (dataset-run grading; the scorer’s id is exactly the
scorer_id a run takes - evaluation_settings_id remains a working alias) + an optional online profile (live-traffic scoring).
Strictly one online profile per scorer.
client.evaluations.settings, client.monitor.online_evaluators)
keep working unchanged as views onto the same entity - each emits a default-hidden
DeprecationWarning on first use pointing here.
Projects and trace read-back are first-class too: client.projects.create(name) returns an
isolated project with its own apiKey (the per-run isolation pattern integration suites use),
and client.traces.get(trace_id) / client.traces.list(cursor=...) read what the tracer wrote.
Pattern scorers you author are rules matched against the response text or the trace: keyword/contains, regex, or an LLM-judged semantic rubric. Build them via the dashboard (Scorers → New scorer → Pattern scorer - see Patterns for the condition builder, AND/OR/NOR combination, and match targets) or with client.monitor.patterns.builder(...), shown below.
The “Tool/action failure” check reads the
success: false a recorded tool
call carries. The easiest way to produce that is
tracer.trace_tool_call(...), which captures an escaping exception as a
failed call automatically - see Tracing.client.monitor.patterns
builder() parameters
publish() returns a pattern with .id, which you pass in pattern_ids at trace time.
On the hosted platform, creating a pattern requires a Business or Enterprise
plan (the same entitlement gate as the dashboard’s Patterns UI). Self-host has
no plan gates.
Where signals show up
Matches are deduped by pattern and (usually) by agent, so a recurring issue accumulates occurrences on one signal instead of creating a new row every time. Each signal links back to the trace and tool calls that produced it, so a reviewer can open the exact exchange from the dashboard (Observe), or read it straight from the SDK.client.monitor.signals
Read-only: a signal is the system’s output from checking traces against patterns, not something you create directly.
list() parameters
list()/get() both return a signal with .id, .type, .severity, .polarity, .status, .summary, .pattern_key, .occurrence_count, .occurrences, .recommended_actions, .root_cause, and more, matching the fields shown in the dashboard’s triage queue.
Reading production metrics
The numbers behind the dashboard’s Overview and Monitor charts are readable as plain dicts, for wiring into your own dashboards and alerts:kpis() and calibration() take a window of "24h", "7d" (default), or "30d".
metrics() filters
metrics() accepts a window from "1h" to "90d" (default "1d") plus optional filters
that scope every number the way the dashboard’s filter chips do:
The response includes platform attribution -
frameworks window totals plus per-bucket
byFramework - which is the data behind the Platforms chart.
Two related project-wide switches: client.monitor.topics(window) reads the
Topics view (LLM-classified themes of sampled traffic - empty until enabled),
and client.monitor.set_topics(True, sample_rate=0.2) turns classification on (each classified
trace costs one judge call, hence the sample rate).
Human review and automation rules
The review loop and the routing rules the dashboard offers are scriptable too:client.monitor.review_queue-list(),queue(trace_id),label(item_id, "good"|"bad", corrected_score=...),dismiss(item_id). Labels feed judge calibration and become judge-tuning evidence. See Review queue.client.monitor.rules-create(name, action, filter=..., sample_rate=..., action_config=...)whereactionis"review"(sample matches into the review queue),"dataset"(append matches as dataset cases), or"webhook"(POST the matching trace to your URL); pluslist(),update(),delete().action_configis required for"dataset"({"datasetId": ...}) and"webhook"({"url": ...}) - the engine returns 400 without it. See Rules.client.monitor.alert_rules- KPI alert rules:create(name, metric=, operator=, threshold=, window_minutes=, channels=[...], severity=, cooldown_minutes=, agent_id=)wheremetricis one offailureRate,toolFailureRate,p95LatencyMs,estimatedCostUsd,judgeFailures,traceCount, andchannelsare built by theslack(url),teams(url),pagerduty(key),email(address),webhook(url)helpers inagentx.monitor.alert_rules; pluslist(),get(),update(),delete(),events(rule_id)(delivery history),test(rule_id)(a test page to every channel),preview(metric, window_minutes)(the live value), andrun_sweep(). See Alert rules.
client.monitor.profile
Get/update one agent’s monitoring profile: enable/disable, detection category opt-outs, and approval policy. The coverage_mode/sample_rate pair still round-trips for wire compatibility but gates nothing - detection runs on all ingested traffic, and sampling lives on each scorer’s own rate. Legacy threshold_overrides fields also still round-trip; latency is a KPI metric now (Overview’s p95 card, straight from the traces) rather than a detection threshold.
get() returns None when the agent has never been configured (still on platform defaults). update() upserts and only changes the fields you pass:
Self-host extras
A self-hosted instance adds three Monitor surfaces the hosted platform doesn’t have yet, all reachable from this same SDK:- Live LLM judge scoring - a real LLM judge scoring a sample of live traffic continuously, per trace or per whole session (
scope="session", judged automatically once the conversation goes idle). Configure it as the online profile of a judge scorer (client.monitor.judge_scorers, or the legacyclient.monitor.online_evaluatorsview). See LLM judge scorers. - Scorer groups (
client.monitor.scorer_groups) - scorers of any kind composed into one weighted 0-10 score with must-pass gates, usable as a dataset run’s grader and against live traffic, per trace or per whole session. Session helpers live onclient.monitor.sessions(scores(),run_sweep(),spans(),coherence_check()). See Scorer groups. - Outcome reports and user feedback (
client.outcomes.report(...)/client.feedback.report(...)) - record what actually happened after the fact (a reopened ticket, an end user’s thumbs-down) against a trace; negative feedback raises a signal directly, and both streams feed the dashboard’s Judge Calibration card. See Outcomes & Judge Calibration.
Tracing
Send the traces this feature monitors
Patterns
The condition builder, match targets, and where signals go

