AI Agents & AnomalyGuard
AnomalyGuard exposes a stable REST API and anomalyguard CLI so AI agents can discover filters, pull anomalies and aggregates, read comments context, and reuse LLM summaries — without opening the web UI.
This tutorial focuses on CLI-first agent integration (same endpoints as the API; one command ≈ one HTTP call).
Why agents use the CLI
| Benefit | Detail |
|---|---|
| Structured data | Filters, anomalies, aggregates, series, comments — JSON payloads agents can reason over |
| Saved business views | Reuse pinned / shared Filters instead of inventing ad-hoc queries |
| Optional prose | Filter summarization endpoints for ready-made briefings |
--json |
Machine-readable wrapper for reliable tool parsing |
| API keys | Unattended auth — no interactive login in the agent runtime |
Typical agent jobs: morning leadership brief, regional escalation triage, “explain this spike”, ticket drafting, or feeding another LLM with grounded anomaly context.
Prerequisites
- CLI matching your AnomalyGuard version — download from the UI or
/api/cli/download/…(see How to use the CLI). - API key with access to the data views the agent may read — create under API Keys (prefer User role + least-privilege data views).
- At least one saved filter (and ideally one with aggregation / summarization enabled) that the key can access.
export ANOMALYGUARD_BASE_URL="https://anomalyguard.contoso.com"
export ANOMALYGUARD_API_KEY="<agent-api-key>"
anomalyguard ping --json
anomalyguard access whoami --json
Never put the API key in the agent system prompt. Inject it only as an environment variable (or a secret store) available to the tool runner.
How an agent should call the CLI
Give the agent a shell / tool that can run anomalyguard … --json. Prefer:
- Always pass
--json. - Check
statusCode(expect200). - Parse
bodyas JSON (it is a string containing the API payload). - Prefer saved filter IDs over free-form queries when a business view already exists.
- Cap context: use Top N on filters, or take only the first N rows from results before stuffing the LLM context window.
CLI JSON envelope:
{
"statusCode": 200,
"reasonPhrase": "OK",
"body": "<raw response body as string>"
}
Example parse with jq:
anomalyguard filters list-little --json | jq '.body | fromjson'
Recommended agent workflow
ping / whoami
→ list filters (or pinned)
→ pick filter by name / comment
→ get summarization (if any)
→ OR anomalies / aggregates by-filter
→ optional: series-data / similar for one anomaly
→ answer user / open ticket / notify
| Step | Command |
|---|---|
| Health | anomalyguard ping --json |
| Identity & access | anomalyguard access whoami --json |
| Data views | anomalyguard access me-dataviews --json |
| Discover filters | anomalyguard filters list-little --json or filters pinned --json |
| Filter definition | anomalyguard filters get {id} --json |
| Anomalies | anomalyguard anomalies by-filter {id} --json |
| Aggregates | anomalyguard anomalies aggregates-by-filter {id} --json |
| Stored summary | anomalyguard filters summarization {id} --json |
| Refresh summary | anomalyguard filters refresh-summarization {id} --json |
| Series chart data | anomalyguard anomalies series-data {anomalyId} --json |
| Similar anomalies | anomalyguard anomalies similar {anomalyId} --json |
End-to-end sample: morning brief agent
Goal. User asks: “What should leadership care about in sales this morning?”
The agent uses a pinned enterprise filter, prefers the stored AI summary, and falls back to top aggregates.
1. Bootstrap (ops / one-time)
export ANOMALYGUARD_BASE_URL="https://anomalyguard.contoso.com"
export ANOMALYGUARD_API_KEY="<agent-api-key>"
# Confirm CLI ↔ server
anomalyguard --version
anomalyguard ping --json
Create or reuse a filter (UI or CLI) such as Enterprise sales — last 7 days, enable aggregation + summarization, pin it. Note its id (example: 12).
2. Script the agent can run as a tool
#!/usr/bin/env bash
# tools/ag_morning_brief.sh — agent tool: morning brief for one filter
set -euo pipefail
FILTER_ID="${1:?Usage: $0 <filterId>}"
echo "== identity =="
anomalyguard access whoami --json | jq '{statusCode, body: (.body | fromjson)}'
echo "== filter =="
anomalyguard filters get "$FILTER_ID" --json | jq '.body | fromjson | {id, name, comment, timeRange, isAggregated, isSummarized}'
echo "== summarization (may be empty) =="
anomalyguard filters summarization "$FILTER_ID" --json | jq '.body | fromjson' || true
echo "== top aggregates =="
anomalyguard anomalies aggregates-by-filter "$FILTER_ID" --json \
| jq '.body | fromjson | .[0:10] | map({start: .StartDateId, types: .AnomalyTypes, chg: .nChange, chgPct: .nChangePerc, share: .nSharePerc, common: .CommonCategories, different: .DifferentCategories})'
echo "== top anomalies (fallback detail) =="
anomalyguard anomalies by-filter "$FILTER_ID" --json \
| jq '.body | fromjson | .[0:5] | map({id, date: .DateId, type: .AnomalyType, changePercent: .ChangePecent, share: .CategoryPercentageShare, categories: .Categories})'
Field names in body follow the API models; adjust the jq paths if your deployment returns slightly different casing. When unsure, dump one full object: jq '.body | fromjson | .[0]'.
3. Example agent system instructions
Paste something like this into the agent (Cursor, LangGraph, custom runner, etc.):
You are an AnomalyGuard analyst agent. You do not invent anomalies.
You only use data returned by the `anomalyguard` CLI (or REST API).
Environment already has ANOMALYGUARD_BASE_URL and ANOMALYGUARD_API_KEY.
Tools:
- Run shell: anomalyguard <args> --json
- Prefer filters list-little / pinned to find the right filter by name or comment.
- For leadership questions, prefer filters summarization {id}.
If missing or stale, use anomalies aggregates-by-filter {id}, then by-filter.
- For “why did X move?”, take one anomaly id and call series-data / similar.
- Always cite filter id and key metrics (change %, share %, categories, date).
- If a matched comment says an event is expected, mark it as explained — do not escalate.
- Never print the API key. Never call admin-only commands unless explicitly allowed.
4. Example user turn → agent actions
User: Brief me on filter “Enterprise sales — last 7 days”.
Agent runs:
anomalyguard filters list-little --json
# → finds id 12
anomalyguard filters summarization 12 --json
# → if present, use as primary narrative
anomalyguard anomalies aggregates-by-filter 12 --json
# → verify top groups match the summary; drill if needed
Agent answers with: overview from summarization, 3–5 top aggregates (common/different), next steps, and which findings are already explained by comments.
5. Optional: refresh summary then notify
If the agent must force a fresh LLM brief (and global LLM setup is configured):
anomalyguard filters refresh-summarization 12 --json \
| jq -r '.body | fromjson | .summarization // .Summarization // .'
Post the text to Slack / Teams / email with your own notifier. AnomalyGuard stores the summary on the filter; it does not send chat messages by itself.
6. Optional: Python helper for tool output
import json, os, subprocess
def ag(*args: str) -> dict:
env = os.environ.copy()
proc = subprocess.run(
["anomalyguard", *args, "--json"],
check=True,
capture_output=True,
text=True,
env=env,
)
envelope = json.loads(proc.stdout)
if envelope.get("statusCode") != 200:
raise RuntimeError(envelope)
body = envelope.get("body")
return json.loads(body) if isinstance(body, str) else body
filters = ag("filters", "list-little")
summary = ag("filters", "summarization", "12")
aggregates = ag("anomalies", "aggregates-by-filter", "12")
Discovering filters without hard-coding IDs
# Compact catalog for the agent
anomalyguard filters list-little --json | jq '.body | fromjson'
# What appears on Home for this identity
anomalyguard filters pinned --json | jq '.body | fromjson | map({id, name, comment})'
Match on name / comment (Home header text) so users can say “the Slovakia filter” without knowing numeric IDs.
Security checklist
- Scope the API key to only the data views the agent needs (API Keys).
- Prefer User keys for read/investigate agents; use Admin keys only for admin automation.
- Keep secrets in the runtime env / vault — not in prompts, git, or chat logs.
- Rotate keys with regenerate / revoke when an agent is decommissioned.
- Log filter IDs and anomaly IDs in agent traces; avoid logging full series payloads if they contain sensitive volumes.
- Keep a reliable backup of the external backend PostgreSQL database (filters, anomalies, comments, settings, and agent-consumed history live there — API/CLI access does not replace DB backup).