Skip to main content
The dashboard includes an optional AI assistant, a chat panel docked to the right edge of the dashboard that answers natural-language questions about your agents (“how is quality trending in prod this week?”, “which sessions errored today?”, “summarize this session”) and, when the user permits each action, drafts and saves SQL queries and dashboards on their behalf. It cites clickable links straight to the relevant sessions, queries, and dashboards, and it is page-aware: ask about “this session” while viewing one and it knows what you mean. The dock shows as a thin 44px vertical rail by default: a ›_ prompt glyph plus a colored health dot. Click the rail (or press ⌘J / Ctrl+J) to expand it to the full chat panel. The expanded panel is resizable between 320 and 640 pixels by dragging its left edge; your preferred width is remembered across reloads. It runs as a small internal agent container (on the Claude Agent SDK) that only the dashboard can reach. It is disabled by default and stays hidden until you configure an LLM endpoint.

What it can and cannot do

  • Reads the operational data the asking user can see. Events, evaluations, sessions, the evaluation-job queue, saved queries, and saved dashboards, scoped per-request to the user’s read permissions. Read tools execute immediately.
  • Writes are gated by per-action approval. It can author saved queries (create_saved_query, update_saved_query), run draft SQL against the read-only role to validate it (run_query), and assemble dashboards from those queries (create_dashboard, update_dashboard, add_query_to_dashboard). Each write pauses for an in-chat Approve / Reject / ask a question prompt; the SDK does not call the tool until the operator clicks Approve. Deletion is never available to the assistant; destructive operations stay with operators.
  • Drafted SQL goes through the same sql_guard validation and read-only roles as user-written SQL (SELECT/WITH only, no multi-statement). Execution is routed by which tables the query touches: queries that reference the analytics tables (events, evaluations, sessions) run as the organization’s read-only ClickHouse user (scoped to that org by a row policy, with a 10s execution cap and a 100k row cap) while queries that touch only relational tables run on a read-only Postgres role (10s, 10k rows). The assistant cannot widen the data surface; it can only author over the queries surface the operator already has.
  • It uses a dedicated assistant key (see below) seeded with a fixed permission set; even if the model misbehaves it cannot exceed those scopes.
  • Each dashboard user needs the agent:use permission to see and use the assistant. Tools are filtered per-request to match the user’s own data permissions, so an events:read user gets event tools but no dashboards:write tools.

Page-aware AI dock: composer on /queries, chat elsewhere

The right-side assistant dock is page-aware. The model picker, conversation history, model health dot, and chat input are unchanged, but the empty-state template chips, the placeholder text, and which backend endpoint a user’s message hits all switch automatically based on the current route. The dock becomes “the AI helper for whichever page you’re standing on”. Two backends, picked per page (with per-chip overrides).
RoutePage-default backendWhy
/queries, /queries/newPOST /api/agent/compose-sql (no tool loop)The user is starting fresh; ≤1s first-token SQL streamed straight into the editor
/queries/<id> (existing)POST /api/agent/chat (full tool-loop assistant), page defaultFree-typed messages should let the user ask anything (“explain this”, “what does this do?”); refactor chips opt back into compose-sql via per-chip kind
every other pagePOST /api/agent/chat (full tool-loop assistant)Read tools + approval-gated write tools
Chips on /queries/<id> carry an explicit kind so a single page can mix the two flows seamlessly. The default chip set is two chat chips (explain the query on screen, what does this query do?) plus five compose-sql chips (parameterize by date range, add a status='error' filter, etc.). Free-typed messages fall through to the page default (chat), so a question like “why is this so slow?” gets a prose answer, while clicking the parameterize by date range chip routes through the compose endpoint and edits the SQL. When the composer runs in edit mode (it sees a non-empty currentSql because the user is on /queries/<id> or /queries/new with proposed SQL already loaded), its system prompt switches from “compose a new query” to “modify the provided SQL minimally: preserve table choice, column names, join structure, aliases, indentation”. The model is shown a separate set of before/after worked examples (parameterize, add filter, convert to hourly buckets), so a chip-clicked refactor produces a minimal diff against the editor’s SQL, not a rewrite from scratch. Click a compose chip (or type freely on /queries/new) → the SQL streams into the assistant message as a fenced ```sql block. The moment the stream finalizes, if Monaco is mounted on the current route, the editor automatically lights up in diff view (original on the left, proposed on the right, an ▾ AI proposed an edit cue at the top, and Accept / Reject pills below). The user does not need to find or click an Insert into editor button to see the diff. The Insert button is still rendered beneath the SQL block as a manual re-trigger (useful after a Reject or when the user has navigated away and back), and it remains the only path when the user is on a non-editor page (e.g. the saved-queries list); there it stashes the SQL in sessionStorage and navigates to /queries/new, where the freshly-mounted editor reads the stash on mount and opens the same diff view. If the proposed SQL is byte-identical to what’s already in the editor (a no-op edit), the auto-open is skipped; we don’t pop an empty diff. The Insert into editor button is also a no-op in that case. When the user accepts a suggestion on /queries/new, the toolbar’s primary action reads save instead of create; the SQL was handed to them by the assistant; the mental model is “finalize this”, not “write from scratch”. The label flips once the dock inserts SQL and stays as save until page navigation. On /queries/<id> the button has always read save; nothing changes there. Outside /queries, the dock works exactly as before: full chat with tool-approval cards, page-context awareness, citations. Permissions / gating. The compose endpoint gates on the per-user queries:run permission (read-equivalent; the user still has to click Accept and Run, and Run goes through the existing sql_guard + references_ch_tables routing on the Rust server). The chat endpoint gates on agent:use. Both still require an LLM connection configured on the agent container; if none is configured, the dock surfaces a “the assistant isn’t configured on this deployment” banner on either path. Refusals. The composer refuses any request it cannot satisfy with a read-only analytics query and emits -- REFUSE: <one-sentence reason> instead of SQL. It refuses requests that would write data or reach tables outside the analytics views (api_keys, users, dashboards, saved_queries, evaluation_jobs), and it refuses pure prose requests (“explain this”, “what does this do?”) on the compose path; those belong to the chat path and produce a prose answer there. The dock renders the refusal string as an inline red error chip in the assistant message; nothing is inserted. Model selection. Shared with the chat path. The model picker in the dock header applies to both endpoints (the compose call passes the picked model through to resolveModel() on the agent service). When AGENTEYE_AGENT_MODELS lists multiple models, operators can mix a Haiku-class option for the composer with a Sonnet-class option for the chat; the user picks per-conversation. Per-page templates. Each page has its own template (headline, body copy, placeholder text, and suggestion chips) so the dock adapts to the page you are standing on. The chips offered on a given route map to the same intents the composer is tuned for, so clicking a suggestion produces the edit you expect. Disabling it. Same as the chat path: the dock + composer are both gated by the agent container and its LLM connection. If you want chat-only behaviour for a particular user, remove the queries:run permission (which also disables the editor’s Run button); if you want composer-only behaviour, remove agent:use from that user’s roles, then re-add queries:run separately so they can still execute author-written SQL.

Enabling it

The agent service ships in the Docker Compose file and the Kubernetes manifests. To turn the assistant on, provide (1) an LLM endpoint and (2) the assistant’s dedicated data key.

1. Choose an LLM connection

Pick one of these and set the corresponding variables on the agent service: a) Anthropic directly
ANTHROPIC_API_KEY=sk-ant-...
b) Through Portkey (recommended; model-catalog slug, key only)
PORTKEY_API_KEY=<your-portkey-key>
AGENTEYE_AGENT_MODEL=@<your-anthropic-integration-slug>/claude-sonnet-4-6
This is the simplest path: in Portkey, set up an Anthropic integration (Model Catalog); it gets a slug. Name the model as @<slug>/<model> and the slug carries the provider + credential routing, so no virtual key is needed, just your Portkey API key. The agent sends only x-portkey-api-key and points at the Portkey gateway; Portkey resolves the rest. (A plain model name fails with “x-portkey-config or x-portkey-provider header is required”; the @slug/ prefix is what makes key-only work.) For a self-hosted gateway set PORTKEY_BASE_URL. Prefer per-request routing instead of a slug? Set PORTKEY_VIRTUAL_KEY=<vk> (or PORTKEY_CONFIG=<id>) with a plain AGENTEYE_AGENT_MODEL. c) Any other Anthropic-compatible gateway (LiteLLM, self-hosted, …)
ANTHROPIC_BASE_URL=https://your-gateway
# Newline-delimited "Name: Value" header lines (NOT JSON):
ANTHROPIC_CUSTOM_HEADERS=x-my-header: value
d) Amazon Bedrock / Google Vertex
CLAUDE_CODE_USE_BEDROCK=1   # + standard AWS credentials in the environment
# or
CLAUDE_CODE_USE_VERTEX=1    # + standard GCP credentials in the environment
Optionally pin the default model with AGENTEYE_AGENT_MODEL (default claude-sonnet-4-6). To let users choose among several models, set AGENTEYE_AGENT_MODELS to a comma-separated allowlist (e.g. @anthropic-prod/claude-opus-4-7,@anthropic-prod/claude-sonnet-4-6); a model picker then appears in the chat header, and each user’s choice is remembered. The agent only ever calls a model on this allowlist.

2. Provide the assistant key

Pick any random secret and give it to the agent as AGENTEYE_API_KEY and to the server as AGENT_API_KEY (the same value). On startup the server seeds it as a dedicated key named dashboard-assistant with this fixed permission set: events:read, evaluations:read, dashboards:read, dashboards:write, queries:read, queries:write, queries:run. The write permissions are only ever exercised through approval-gated tools (see “What it can and cannot do” above). There is no manual key-minting step and no admin key involved. The permission set is fixed in the server, and the seeded key is protected: it cannot be disabled or regenerated through the keys API; rotate it by changing the value and restarting the server. Do not reuse the admin/dashboard key.
SECRET="$(openssl rand -base64 32)"
# on the agent service:
AGENTEYE_API_KEY="$SECRET"
# on the server service:
AGENT_API_KEY="$SECRET"
On Kubernetes this is wired for you: put AGENTEYE_API_KEY in the agenteye-agent secret and the server Deployment already reads that same value as AGENT_API_KEY.

3. Set the shared dashboard↔agent token

Set the same AGENTEYE_AGENT_TOKEN on both the dashboard and agent services. The dashboard presents it when calling the internal agent service; the agent rejects calls without it.

4. Grant users access

Give the relevant dashboard operators the agent:use permission (see enterprise-docs/api-keys.md). Users without it never see the assistant. Once an LLM endpoint and the read-only key are set, restart the server (to seed the read-only key) and the agent service. The assistant dock appears on the right edge for any agent:use user, collapsed by default; click the rail or press ⌘J / Ctrl+J to expand.

Environment variable reference

Set on the agent service:
VariablePurpose
PORTKEY_API_KEYRoute through Portkey (the agent builds the gateway connection from this)
PORTKEY_VIRTUAL_KEYPortkey virtual key for your Anthropic credentials (optional if the key has a default config)
PORTKEY_CONFIG / PORTKEY_BASE_URLNamed Portkey config / self-hosted Portkey gateway URL (optional)
PORTKEY_PROVIDERPortkey provider slug — a third routing option alongside PORTKEY_VIRTUAL_KEY / PORTKEY_CONFIG (used only when neither of those is set)
ANTHROPIC_API_KEYDirect Anthropic access (alternative to a gateway / Bedrock / Vertex)
ANTHROPIC_AUTH_TOKENBearer token for a gateway that authenticates via Authorization: Bearer instead of x-api-key (optional)
ANTHROPIC_BASE_URLEndpoint for a non-Portkey gateway
ANTHROPIC_CUSTOM_HEADERSExtra headers for a non-Portkey gateway: newline-delimited Name: Value lines (not JSON)
CLAUDE_CODE_USE_BEDROCK / CLAUDE_CODE_USE_VERTEXRoute via Bedrock / Vertex
AGENTEYE_AGENT_MODELDefault model id (default claude-sonnet-4-6)
AGENTEYE_AGENT_MODELSComma-separated allowlist of models the user can pick from in the chat header. Leave unset for a single fixed model. The default above must be one of these (else it’s added).
AGENTEYE_AGENT_MAX_CONCURRENCYMax concurrent chats per pod (default 4); excess requests get 429
AGENTEYE_API_KEYAssistant’s data key. Set the same value as the server’s AGENT_API_KEY, which seeds it with a fixed scoped permission set on startup (see step 2).
AGENTEYE_AGENT_TOKENShared secret with the dashboard
AGENTEYE_SERVER_URLAgentEye server URL (default http://server:8080)
AGENTEYE_AGENT_ALLOW_NO_ORGMulti-tenancy. Off by default (fail-closed): the assistant rejects a /chat request that carries no organization context with 400, because every tool it runs is scoped to one org. The dashboard always sends that context once it is org-aware, so you normally leave this unset. Set to 1 only during a transitional rollout where a not-yet-org-aware dashboard is talking to an org-aware agent, so the assistant falls back to the default org instead of refusing. Clear it once the dashboard upgrade lands.
AGENTEYE_AGENT_MAX_STEPSMax tool-use steps per answer (default 8)
AGENTEYE_AGENT_TIMEOUT_MSOverall /chat request timeout (all model turns + tool steps), in milliseconds (default 90000); the SQL tool has its own 10s cap
AGENTEYE_AGENT_SELF_TELEMETRY1 to record the assistant’s own runs into AgentEye
AGENTEYE_TELEMETRY_API_KEYSeparate events:add-only key for self-instrumentation
AGENTEYE_AGENT_ENVEnvironment tag applied to the assistant’s own self-telemetry (default prod)
Set on the dashboard service:
VariablePurpose
AGENTEYE_AGENT_URLWhere the dashboard reaches the agent service. The bundled Kubernetes manifests and Compose file set this to http://agent:9100. Leave it unset to hide the assistant entirely.
AGENTEYE_AGENT_TOKENMust match the agent’s token

Telemetry & seeing what users ask

Prompt content stays inside your own systems by default. Three layers:
  1. Conversation store: every prompt and answer is saved in your AgentEye database (per user, private), and reloadable from the assistant’s history switcher. This is the durable record of what users ask.
  2. Product analytics: the dashboard records metadata only (how often the assistant is used, tool counts, latency) to your analytics. Prompt text is never included on this path.
  3. Self-instrumentation (optional): set AGENTEYE_AGENT_SELF_TELEMETRY=1 (plus an events:add-only AGENTEYE_TELEMETRY_API_KEY) and the assistant records its own runs into AgentEye as a dashboard-assistant agent. You then watch user prompts and the assistant’s reasoning in the very same sessions/events views you use for everything else. Note: those events are visible to anyone with events:read; if that’s too broad, leave this off.

Disabling it

Any of these disables the assistant (the dock rail disappears):
  • Unset AGENTEYE_AGENT_URL on the dashboard, or
  • Leave the LLM endpoint unconfigured on the agent (no ANTHROPIC_API_KEY / gateway / Bedrock / Vertex), or
  • Don’t deploy the agent service at all.

Security summary

  • No silent writes: the assistant’s write tools (create_saved_query, update_saved_query, create_dashboard, update_dashboard, add_query_to_dashboard) cannot execute without an explicit operator click on the in-chat Approve button; the SDK’s pre-call gate blocks the tool until an approval reaches the agent over a back-channel. There is no setting that disables this gate.
  • Fixed, narrow data scope: the assistant authenticates to the server with a dedicated key whose permission set is fixed in the server (events:read, evaluations:read, dashboards:read, dashboards:write, queries:read, queries:write, queries:run). The only writes it can author are saved queries and dashboards; the server rejects anything outside that scope regardless of what the model attempts.
  • No deletion surface: the key carries no delete permission and no delete tool is exposed. Operators delete through the dashboard UI, never the assistant.
  • Internal-only: the agent has no public route; only the dashboard can call it, and only with the shared token. (In Kubernetes, a NetworkPolicy restricts the agent to reaching just the AgentEye server and the LLM endpoint.)
  • Per-user scoping: only agent:use users get the assistant, and it is given only the tools matching each user’s read permissions.
  • No raw HTML / no link exfiltration: answers render as sanitized markdown; external links are defanged.
See enterprise-docs/troubleshooting.md for common issues.