Skip to main content

data-querying

General guidance on querying data sources, using existing scripts vs ad-hoc queries, filtering patterns, and generating charts for the analytics app.

Informations de source

Dépôt
BuilderIO/agent-native
Dernière activité de la source
19 octobre 2026 à 18:55
Langue détectée de SKILL.md
anglais
Étoiles
7 449
Forks
681

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
2 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
data-querying
description
General guidance on querying data sources, using existing scripts vs ad-hoc queries, filtering patterns, and generating charts for the analytics app.
# Data Querying The analytics app connects to multiple data sources. This skill covers general patterns for querying data effectively. ## Approach 0. **Use retrieved references first** — data questions may start with a small set of relevant data-dictionary entries and saved dashboard panels in `<resource scope="analytics-catalog">`. Treat them as definitions and query examples, never live results. If they do not fit, call `find-data` before querying; use data-source status when provider availability matters. 1. **Route named account health deliberately** — for a customer/org health, QBR, renewal, contract-utilization, risk, or adoption request, read `account-health` before writing SQL. It adds identity-lock and metric-definition checks that an ordinary lookup does not need. 2. **Read the relevant provider skill first** — check `.agents/skills/<provider>/SKILL.md` for table names, column mappings, auth, and gotchas. For BigQuery, read `.agents/skills/bigquery/SKILL.md` and use `search-bigquery-schema` before guessing table or column names. 3. **Clarify if ambiguous** — if the metric definition, date range, or grain is unclear and a wrong guess would change the numbers, use the `ask-question` clarifying tool (multiple-choice) before querying. Ask at most once per turn; skip it when the dictionary or the user already answered. 4. **Use existing actions or connected provider MCP tools** — call the provider action/tool with structured arguments, then filter or aggregate the returned records in your answer 5. **Write ad-hoc scripts** — if no existing script covers the question, create one in `actions/` 6. **Present data in chat** — don't just say "check the dashboard" — actually query, get the data, and present it. Only present numbers you actually retrieved; never report a value you did not query. For a question naming a dbt metric, use `tool-search` to load `query-dbt-semantic-metric` and pass its exact dbt name. Use dbt as the metric- definition and grain source; Sigma and Amplitude are examples or cross-checks. If no dbt connection is available, use the source-selection path below. If the metric is not defined or its deployment is not ready, report that state and use a verified warehouse path only when it answers the same definition. Use the metric's returned dimensions and `queryableGranularities` for a breakdown. Pass exact dimension names in `groupBy`; grouped MetricFlow queries require an inclusive `timeRange` no longer than 366 days. Use `metric_time` and an explicit grain for a time series. The action validates dimensions and grains against dbt metadata, and returns the executed `querySpec`; preserve it when interpreting rows or continuing with the returned `queryId` and `nextPage`. MetricFlow names grouped time columns `metric_time__<GRAIN>` in the returned rows; preserve that key when presenting the result. When activity could mean Builder product events or Agent-Native app opens, keep the populations separate and ask which one the user intends if the source is ambiguous. ## Metric semantics and source status - Keep event families separate unless the dbt metric or exact saved query defines their union. A UI click and a submitted message are different events even if a dashboard labels both as activity. - Verify population filters and exclusions from model/metric SQL or tests. Similar aggregate totals do not prove that a model excludes staff, internal users, or any other group. - A bounded date or partition scan supports conclusions only for the scanned range. State that range and any bytes or query limit; an empty result in those partitions does not establish absence elsewhere. - For a source inventory, call `data-source-status` and distinguish configured credentials from a healthy workspace connection. A provider appearing in the tool catalog is not evidence that this workspace has connected it. For events recorded by the analytics template itself via its `/track` endpoint, use `pnpm action query-agent-native-analytics --sql "SELECT ... FROM analytics_events ..."`. This includes pageviews, site/app traffic, template usage, app usage, and event counts collected by this analytics app. Pageviews and traffic can also live in GA4, BigQuery/warehouse tables, Mixpanel, PostHog, Amplitude, or another configured provider, so choose the source from the user's wording, connected-source status, existing dashboards, data dictionary, and user/org resources. Ask one concise clarification only when multiple configured sources are plausible and the choice would change the numbers; otherwise name the source you used. Do not use `db-query` for data-source analysis; `db-query` is only for internal app tables and will confuse analytics questions. The shipped `agent-native-templates-first-party` SQL dashboard is the template engagement dashboard for the first-party collector source. For first-party counts, active-user, and retention questions, prefer the compact tenant-scoped daily event and user-day rollups. Use `analytics_events` only for a bounded recent drill-down with an explicit date/time range. For the Builder.io production organization after the BigQuery cutover, these logical tables are served by partitioned BigQuery data and views; the source still does not require an end user's separate warehouse connection. The Builder.io BigQuery path translates a restricted PostgreSQL-compatible SQL subset. Extract an event property with `properties::jsonb ->> 'key'`; bare JSON operators are not translated. Avoid `LATERAL`, `unnest`, `generate_series`, `FILTER (WHERE ...)`, `DISTINCT ON`, and `ILIKE`, which the BigQuery path rejects. Use `||` explicitly for string concatenation. Rewrite `FILTER (WHERE ...)` as a conditional aggregate such as `SUM(CASE WHEN ... THEN value ELSE 0 END)`, `ILIKE` as `LOWER(left) LIKE LOWER(right)`, and `DISTINCT ON` as `ROW_NUMBER()` in a CTE with an outer `WHERE` filter. After an unsupported-syntax error, make one corrected attempt and do not repeat unchanged SQL. Follow the first-party query's typed recovery instruction: schema errors call for `find-data` or `search-bigquery-schema`; permission failures stop; billed-byte failures need a narrower scan; only timeout, rate-limit, and temporary backend failures permit one unchanged retry. Use the separate BigQuery action for GoogleSQL-only SQL. First-party reads exclude test identities (QA/E2E accounts): ingest never stores their events or replays, and the read scope filters `user_id` (events, replays) and `user_key` (user-days). Pass `includeTestIdentities: true` to `query-agent-native-analytics` only to debug those accounts. Daily event rollups and BigQuery-source panels that query the raw warehouse table directly are clean from ingest onward but are not filtered at read time. The one exception is the legacy `@app_events` table, which keeps a test identity's `$exception` rows marked `JSON_VALUE(data, '$.test_identity') = 'true'`; exclude those from any metric over it. When the user rules out BigQuery, skip the health check below, which queries BigQuery on a BigQuery-backed scope. Call `get-first-party-analytics-backend` to see which tables Postgres serves, then pass `backend: "postgres"` to `query-agent-native-analytics`. If it fails with `first_party_backend_not_postgres`, this scope reads those tables only from BigQuery: say so and let the user decide. Otherwise, before a large or historical first-party query, call `get-first-party-analytics-health`. Keep Neon as the default while its status is `healthy` or `monitor`; a `recommend_bigquery` result means the app has observed 1M+ events, repeated slow queries, or a timeout/30-second query. Treat that status as a compatibility name for an external-backend recommendation, not as a requirement to use BigQuery. The health result lists the supported options: - BigQuery for warehouse SQL and complete historical analysis. - Amplitude for product analytics, funnels, and retention. A `recommend_bigquery` result is advice, not a stop: answer the bounded question first. Use the setup link only for a historical or high-volume request with no suitable backend configured, via its returned link or `data-source-status --key <provider>`, to guide the user through the existing Data Sources walkthrough. Connecting a query backend alone does not move the collector or copy existing Neon events. For the Builder.io production organization, the hidden `migrate-first-party-analytics-to-bigquery` action is the explicit state machine: prepare dual-write, backfill through bounded, newest-first UTC time shards with per-shard leases, then cut over with confirmation. The worker excludes `http.response` by default and uses BigQuery `insertId` values for retry-safe writes. After cutover, `/track` writes and event queries use BigQuery; public-key metadata, derived exception issues, and session-replay data remain in SQL. After cutover, an org admin can point charts at a scheduled warehouse events model with `configure-first-party-events-model`; run `check` first, which dry-runs as the app and saves nothing. With a model set, a first-party query reads `analytics_events` from the model only when its WHERE filters `event_name` to named events (`= 'x'` or `IN (...)`, ANDed, no OR or NOT), because the model has no `http.response` rows; its result carries `eventsSource` and `sourceGeneration` (the model build it read). Unfiltered queries, `SELECT *`, and queries that name `public_key_id` or `context`, read `http.response`, or read `properties` other than through `JSON_VALUE`, `JSON_EXTRACT_SCALAR`, `JSON_VALUE_ARRAY`, or `JSON_EXTRACT_STRING_ARRAY` stay on the raw table, as do alerts and onboarding journeys; `eventsSource.modelSkipped` says why. A model result trails the newest events by up to one rebuild, so for the last few minutes give the model's age from `get-analytics-source-generation` (a metadata read, no query) rather than calling the count complete. Example pageviews query for a local calendar day: ```sql SELECT COUNT(*) AS pageviews FROM analytics_events WHERE event_name = 'pageview' AND timestamp >= '<start-utc>' AND timestamp < '<end-utc>' ``` Convert the user's requested local date/timezone to UTC before querying. For example, May 1, 2026 in America/New_York is `2026-05-01T04:00:00Z` through `2026-05-02T04:00:00Z`. ### LLM observability events Agent runs are `analytics_events` rows with `event_name = '$ai_generation'`. Useful `properties`: - Identity: `$ai_trace_id`/`run_id`, `$ai_session_id`/`thread_id`, `$ai_model`/`model`, `$ai_provider`/`provider`. - Usage: `$ai_input_tokens`/`input_tokens`, `$ai_output_tokens`/`output_tokens`, `cache_read_tokens`, `cache_write_tokens`. - Cost: `$ai_total_cost_usd`/`cost_usd`, `cost_cents_x100`. - Time: `duration_ms` is the full run in milliseconds; `$ai_latency` is model time in seconds (run minus tool time). - Tools: `tool_calls`, `successful_tools`, `failed_tools`, `tools`, `tools_truncated`. The bounded `tools` array holds names, relative start times, durations, statuses, and coarse error classes, never args or results; failed runs and interrupted tools stay queryable. - Delegation: `delegated`, `delegation_protocol`, `caller_app`, `delegation_task_id`, `a2a_task_id`, `parent_run_id`, `parent_turn_id`. Agent Teams child runs use `delegation_protocol = 'agent-team'`, keep their own `run_id`, and link to the launching run through `parent_run_id`. - Errors: `status`, `$ai_error` (terminal code, cause, retryable, and a fixed code-derived message), `$ai_error_type`. Run failure messages are omitted from telemetry. ## Inline Charts In Chat For an in-chat answer, **emit a live `/chart` embed** — never `generate-chart`. The embed mounts a live `SqlChart` that re-queries when its source changes, and it doesn't choke on rigid JSON params the way the PNG action does. Reach for `generate-chart` only when you're building a dashboard artifact that needs a persisted report image. If `generate-chart` returns an error in any chat-answering flow, the recovery is to switch to the live embed, not to retry with reformatted params. **How it renders.** The core chat markdown renderer turns any fenced block tagged `embed` into a sandboxed, same-origin iframe. Emit: ````markdown ```embed src: /chart?panel=<base64url-encoded panel JSON> title: Daily pageviews height: 320 ``` ```` Fence keys: `src` (required, same-origin path), `title`, and either `height` (px) or `aspect` (`16/9`, `4/3`, `1/1`, `21/9`, `3/2`, `2/1`; default `16/9`). A cross-origin `src` renders an "Embed blocked" notice instead of a chart. **This fence is the only supported syntax.** Never write a bare line like `` `/chart type=bar title="..." labels=[...] data=[...] color=#...` `` in chat text — that pattern comes from confusing `generate-chart`'s tool parameters (`title`, `labels`, `data`, `type`) with markdown; those are arguments to a tool call, not something to type into a chat message. The chat renderer has a best-effort compatibility fallback that tries to recover a chart from that exact shorthand shape, but it is not the contract: it rejects mismatched lengths, negative values, and malformed input (falling back to plain text), and it does not re-query live data the way the embed does. If you catch yourself typing `label` or `data` followed by `=` in a chat reply, stop and build the ` ```embed ` fence above instead. **Panel JSON.** The `/chart` route decodes `panel` into a `SqlPanel` (`app/pages/adhoc/sql-dashboard/types.ts`): - `sql` — required, non-empty. - `source` — required, one of `bigquery`, `ga4`, `amplitude`, `first-party`, `demo`, `prometheus`. `program` is deliberately **not** embeddable. - `chartType` — required, one of `line`, `area`, `bar`, `metric`, `table`, `pie`. Dashboard-layout types (`section`, `heatmap`, `callout`, `extension`) are rejected. - `id` (defaults `"embed"`), `title` (rendered above the chart), `width` (dashboard-only, ignored here), `config` (passed through unvalidated — `xKey`/`yKeys`, `colors`, `yFormatter`, `rightYKeys`/`rightYFormatter` for a dual-axis line/area/bar chart, `columns`, `stacked`, `legend`, …). An unknown `source`/`chartType` or blank `sql` renders an error card, not a chart. **Encoding.** JSON-stringify the panel, base64-encode it, then make it URL-safe: `+` → `-`, `/` → `_`, strip `=` padding. No further URL-encoding is needed. Keep the SQL short — it rides in a query string; if it's long, save it as a dashboard panel and link to the dashboard instead. Full details (per-field validation, `config` keys, a verified round-trip example, and how this differs from `generate-chart`) are in `references/inline-chart-embeds.md` — read it with `docs-search --slug "skill-data-querying--references-inline-chart-embeds"`. ## Script Patterns ### Reusing Existing Actions ```bash # Jira tickets pnpm action jira-search --jql="summary ~ SSO" --fields=key,summary,status # HubSpot deals pnpm action hubspot-deals --query="The Knot" --limit=10 --properties=dealname,amount,dealstage # HubSpot + Gong account/deal deep dive pnpm action account-deep-dive --query="The Knot" --days=180 --gongLimit=10 --transcriptLimit=5 # HubSpot contacts or companies pnpm action hubspot-records --objectType=companies --query=builder.io --properties=name,domain,lifecyclestage # Gong call evidence (quotes, counts); ask run-gong-native-insight first for "what did they say" pnpm action gong-calls --company="The Knot" --days=180 --includeTranscripts=true --transcriptLimit=5 ``` The first-class actions above are convenience shortcuts for the common cases, not the limit of what you can do. Many providers (GitHub, Amplitude, PostHog, Mixpanel, Apollo, Common Room, Twitter/X, Notion, Pylon, GA4, plus any endpoint/filter a shortcut can't express) have **no bespoke action** — reach them through the shared provider API escape-hatch pattern: `provider-api-catalog` / `provider-api-docs` to learn the endpoint, then `provider-api-request` (or `providerFetch` inside `run-code`) against the provider's real HTTP API. Ask the provider to do the work first: use its own search or semantic tool, or a server-side filter on a bounded window (for Gong, `run-gong-native-insight`). Only for exhaustive coverage, exact counts, or an absence claim ("how many", "any/none across all …") stage the raw API response with `stageAs`/`saveToFile` and reduce it with `query-staged-dataset` or `run-code`, rather than stretching a capped shortcut action or paging by hand to filter locally. ### Writing Ad-Hoc Scripts When no existing script covers the question: 1. Create a new script in `actions/` that imports the relevant server lib 2. Run it via `pnpm action <name>` 3. For one-off queries, you can delete the script after 4. For reusable queries, keep the script ```ts // scripts/my-query.ts import { runQuery } from "../server/lib/bigquery.js"; import { output } from "./helpers.js"; export default async function main(args: string[]) { const results = await runQuery("SELECT ..."); output(results); } ``` ## Cross-Referencing Sources For answers that span multiple sources, follow the `cross-source-analysis` skill: plan which source owns each fact, fetch per source, stitch identities on BOTH a stable id AND email (ids can be reassigned), de-duplicate, and cite per-source provenance. For complete answers, combine data from multiple sources: - **BigQuery** for analytics events, signups, pageviews - **First-party Analytics** (`query-agent-native-analytics`) for events collected through `/track` - **HubSpot** for CRM data — `hubspot-records` for contacts/companies/tickets/general lookup; `hubspot-deals` and `hubspot-metrics` for pipeline and revenue analysis - **Gong** for sales-call evidence — ask `run-gong-native-insight` first for what was said, risks, objections, or next steps; use `gong-calls` with `includeTranscripts=true` for quotes, counts, or when native operations are not connected - **Jira** for engineering metrics — tickets, sprints - **GitHub** for code metrics — PRs, reviews - **Agent-Native Analytics Monitoring -> Errors** for first-party captured client/server issues; use `list-error-issues` and `get-error-issue` for grouped details - **Sentry** for external error rates and trends when connected - **Grafana** for infrastructure metrics
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub