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
-
An answer from the agent, in a table, like:
Segment Severity Last week Baseline Change Contribution reason = card_declined, region = EU high 412 238 +73 % 64 % reason = out_of_stock medium 98 140 -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.
-
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
- Which metric, exactly? Failed orders as a count, or the failed percentage? The agent reads the app's model (
analysis_describelists the fields you can use) and asks. - 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.
- Which window and baseline? Last week against the week before is the default here.
- The budget. Each analysis counts against a daily budget for your workspace;
analysis_describeshows how much is used and left. The agent tells you before it spends one. - 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
| Piece | Name | What it is |
|---|---|---|
| Nothing new, at first | The ad hoc analysis runs on the existing app's model and is visible to whoever ran it | |
| App version | Test KPI app v4 | Adds a declared analysis with scope: shared to bda.manifest.json |
| Optional | A run of the cause agent | A 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"}
]
kindisdetectorexplain; theconfigis the engine's own configuration, which the agent takes from the run it just made rather than writing by hand.bindlets 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 asuserwhen 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
- 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.
- Ask the same question again; the agent should report the job was reused or joined and the budget figure did not move.
- 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 isscope: shared). - Change the window in the panel to "last month" (the
bind); the panel re-runs with that window. - On the Runs page, each analysis appears as a run with its status.
How to switch it off
- Remove the entry from
analysesin 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
causeagent, 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
causeagent, and are labelled as such. - A declared analysis with
scope: sharedgives 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.
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.