Skip to main content

What changed, and why: Detect and Explain

Detect and Explain is already built into Studio; you do not write it, you ask it. Detect finds the parts of a metric (combinations of up to four dimensions, such as reason and region) that moved unusually in a window. Explain ranks what drove a change against a baseline. This example runs both on the failed-orders metric of the KPI app's model, reports the findings exactly, and then makes the same analysis part of the app so anyone who opens it sees it.

What you will have at the end​

  1. An answer from the agent, in a table, like:

    SegmentSeverityLast weekBaselineChangeContribution
    reason = card_declined, region = EUhigh412238+73 %64 %
    reason = out_of_stockmedium98140-30 %-15 %

    with, for each finding, the ranked drivers the engine returned. Every number is quoted as returned; check the totals against your own weekly report.

  2. A declared analysis in Test KPI app, "Failed orders: what changed last week", listed in the app's analysis panel for every viewer, showing one shared answer.

What the agent will ask you before it builds​

  1. Which metric, exactly? Failed orders as a count, or the failed percentage? The agent reads the app's model (analysis_describe lists the fields you can use) and asks.
  2. Which dimensions may it slice by? Up to four. The agent proposes, for example, reason, region, channel and payment method, and asks which matter to you.
  3. Which window and baseline? Last week against the week before is the default here.
  4. The budget. Each analysis counts against a daily budget for your workspace; analysis_describe shows how much is used and left. The agent tells you before it spends one.
  5. The rule about numbers. The agent should say this unprompted: it will report the findings and drivers exactly as returned, name the segment, the percentage change and the contribution, and never add a cause the engine did not give. If you want a narrative "why", there is a separate agent for that (below), and it should ask before running it.

The pieces it creates​

PieceNameWhat it is
Nothing new, at firstThe ad hoc analysis runs on the existing app's model and is visible to whoever ran it
App versionTest KPI app v4Adds a declared analysis with scope: shared to bda.manifest.json
OptionalA run of the cause agentA narrative explanation, only if you ask for it

Steps, in order​

1. Describe, then run (agent)​

analysis_describe(app_id)
# -> enabled? the declared analyses (none yet), the intent fields, the budget used and left

analysis_run(app_id, intent={
"kpi": "failed_orders",
"window": "last_week",
"dimensions": ["reason", "region", "channel", "payment_method"],
"mode": "detect_and_explain",
"direction": "both",
"baseline": "P7D"
})
# -> a job id

analysis_result(job_id, wait_s=60)
# call again until it says succeeded or failed; findings and drivers come back as tables

The analysis needs an app, because the app fixes the model and the calendar (its timezone and week start). That is why this example runs on Test KPI app rather than on a bare model. To analyse a model that has no app, the agent would first create a minimal app bound to it.

The job runs as you, with a 30-minute budget. The same question asked twice joins the running job or reuses a recent answer, so asking again does not spend the budget twice; the agent should note when that happened.

2. Report the numbers (agent)​

The agent turns the two tables into a plain-words report, and this is where you hold it to the rule: each finding names the segment (which values of which dimensions), its severity, the current and baseline values, the % change and its contribution to the overall move; each driver is listed under its finding, in the order returned. Nothing is rounded into a story. "Card declines in the EU rose 73 % and account for 64 % of the increase" is a report; "because a gateway rule changed" is not, unless a driver says so.

Check the totals against your weekly report. If they do not match, the most likely reasons are the definition of the metric or the calendar (which day the week starts), both of which the app's manifest fixes.

3. Ask again, and see it reused (agent)​

Ask the same question again. The agent should tell you the job was joined or the answer reused, and report the budget used and left from analysis_describe. This is how you know repeated views are cheap.

4. Declare the analysis in the app (agent)​

To make it part of the app, the agent adds a declared analysis to bda.manifest.json in a new version:

"analyses": [
{"id": "failed_orders_last_week",
"kind": "detect",
"title": "Failed orders: what changed last week",
"config": { "...": "the engine config the agent copies from the run above (metric, dimensions, sensitivity)" },
"bind": {"window": "last_week", "filters": []},
"scope": "shared"}
]
  • kind is detect or explain; the config is the engine's own configuration, which the agent takes from the run it just made rather than writing by hand.
  • bind lets the viewer's window and filters apply when the panel runs it.
  • scope: "shared" means one answer for every viewer of the app, instead of a private job per person. Use it for a team view; leave it as user when each viewer should get their own.
  • At most 16 analyses per app.

The app builds no panel. Studio's host page draws the analysis panel and lists the declared analyses in it; the app's code does not call anything (there is no network in the sandbox, and there is no bda call for analyses yet). The agent uploads, validates and publishes the version.

Your call: this version adds no import, so the agent may publish it once you say yes. If the app is shared with the team, they see the panel entry the moment it is published.

5. Optional: a narrative "why" (agent, only if you ask)​

For a written explanation with evidence, Studio has a registered agent called cause. The agent can run it for you:

agent_run("cause", input_json={
"question": "Why did failed orders rise last week?",
"subject": {"model": "m_retail_demo", "metric": "failed_orders",
"window": {"from": "2026-09-15", "through": "2026-09-21"},
"baseline": {"shift": "P7D"}}})
agent_result(...)

It uses Detect and Explain as one of its tools and writes a narrative. It costs more than a bare analysis (it is an agent run, with steps and model calls), so the agent should ask before starting it, and you should read its text as a model's text: quoted numbers are checkable, causes are hypotheses.

What if you want this every week?​

You might ask "run this in a weekly workflow and email it". The agent should tell you plainly: workflows cannot run Detect and Explain yet. The workflow step kinds detect and explain exist but are cut from this version: they validate, but every run reports them as not runnable. Do not let it build a workflow around them. What to do instead:

  • Open the app's analysis panel (the declared analysis, one shared answer), or ask the app's chat.
  • Run it over MCP when you want it, as above.
  • For a weekly email, build the digest workflow and let its screenshot step capture the app with the analysis panel open; the numbers arrive, and the analysis runs when someone opens the panel.

Scheduled detection on a metric ("patterns") is a separate part of Bicycle and is not covered here.

How to test it​

  1. After step 1, the agent's report should have at least one finding with a segment, a % change and a contribution, and the totals should match your weekly report.
  2. Ask the same question again; the agent should report the job was reused or joined and the budget figure did not move.
  3. Open Test KPI app, open the analysis panel in the host page: "Failed orders: what changed last week" is listed. Open it: the same findings. Ask a colleague who has the app to open it; they see the same answer (that is scope: shared).
  4. Change the window in the panel to "last month" (the bind); the panel re-runs with that window.
  5. On the Runs page, each analysis appears as a run with its status.

How to switch it off​

  • Remove the entry from analyses in a new app version and publish it (Your call); the panel entry disappears. Or unpublish the app.
  • Ad hoc analyses leave nothing behind but their results, which expire from the cache on their own.
  • If you ran the cause agent, its run is on the Runs page; there is nothing to switch off.

What you learned​

  • Detect and Explain is asked, not built: describe, run with an intent, poll for the result.
  • Report numbers exactly; causes come only from drivers or from the cause agent, and are labelled as such.
  • A declared analysis with scope: shared gives every viewer one answer in the host's panel; the app draws nothing.
  • Workflows cannot run it yet; use the panel, chat or MCP, and a screenshot workflow for a cadence.

Read more: Detect and Explain, the app manifest for the analyses and calendar fields, Runs and traces.

Next: Chat that answers from your functions.

Copy for your agent
On my "Test KPI app" on preview: what changed in failed orders last week, and why? Then make that
analysis available to anyone who opens the app.

How to work with me:
- Use analysis_describe(app_id) first, then analysis_run(app_id, intent={kpi, window "last_week",
up to 4 dimensions, mode "detect_and_explain", direction "both"}), and poll analysis_result until
it succeeds. Ask me which metric and which dimensions before you run it, and tell me the budget
used and left.
- Report findings and drivers with the numbers exactly as returned: segment, % change,
contribution. Do not invent causes. Tell me if a second ask was reused or joined.
- Add a declared analysis to the app's manifest in a new version, with scope "shared", and explain
that the host's panel shows it and the app builds no panel UI. Ask me before publishing.
- If I ask to run this weekly in a workflow, tell me the workflow detect/explain kinds are not
runnable yet and offer the panel, chat, MCP, or a workflow that emails a screenshot of the app
instead. Do not build a workflow with a detect node.
- Offer the "cause" agent for a narrative, and run it only if I say yes.
- Only create things titled "Test ...". Never change an existing app.