Guided mode v0.4¶
Guided mode is for analysts who know their business but do not know PyMC. Guided mode uses
tasks, not screens. You select what you want to learn and answer questions about your
columns. You tell what you already believe. Then you start a run, read a report in plain
language and move the what-if sliders. Each answer goes into the same ModelSpec that Expert
mode edits. Thus "View as YAML" always shows the real spec, and edits in Expert mode come back
to Guided mode.
This page tells about the concepts and the contracts in mapi_spec. The server, the web app
and the client use the same contracts. The
HTTP API reference gives the endpoints.
Status
The server and the runner serve all the contracts on this page. The data path includes the templates, role detection, drafts, the ingest presets in the sandbox and the persona lock. From a spreadsheet to a Guided draft shows the data path. The modelling path includes business priors, lift tests, the scorecard, the what-if grids and the forecast and counterfactual views. Guided modelling shows the modelling path. The Guided screens of the web app use all of it. Refer to Your first analysis in Guided mode.
The persona lock¶
Each user selects Guided or Expert in the header, and the app keeps the selection. Before a
user selects a mode, a self-service workspace starts in Guided mode. All other workspaces
start in Expert mode. A project admin can lock a project to Guided mode
(Project.guided_locked). In the web app, use Settings > Mode > Lock
… to Guided mode. With the API, use
PATCH /v1/tenants/{tenant_id}/projects/{project_id}. The audit log records the change as
project.guided_lock. After the lock, members below admin see only Guided screens.
The API applies the same limit when these members write specs
(mapi_spec.guided.guided_lock_issues). A stored spec must be a Guided spec. Its
metadata.labels.guided names a template, and model.use is the plugin of that template.
The fields that the template marks advanced or hidden keep their values. A revision keeps
the values of its parent, and a new spec keeps the values of the template. Members can edit
answers, basic settings, business priors and lift tests. All other changes get
403 FORBIDDEN, with one issue for each JSON pointer.
The lock has no effect on admins, static validation, runs or scoring. Thus integrations that call endpoints also have no limit.
1. "What do you want to learn?" - templates¶
A template (GuidedTemplate) is data. It contains these items:
- a title and a description in plain words
- the questions
- the built-in plugin that it uses
- a
ModelSpectemplate with{{placeholder}}strings - default settings
- the scorecard checks that apply
x-ui-levelhints for each field.
| Template | Plugin | For | What-if | Forecast | Business priors |
|---|---|---|---|---|---|
geo_mmm - Geo marketing mix |
mmm.bayesian (mmm-framework) |
The contribution of each channel in each market; budget changes between channels | yes | yes | yes, and lift tests |
mediated_mmm - Marketing mix with an intermediate measure v0.12 |
mmm.nested (mmm-framework) |
The direct and indirect effect of each channel through visits, searches or awareness (national data) | yes | yes | lift tests only |
multi_kpi_mmm - Marketing mix for related products v0.12 |
mmm.multivariate (mmm-framework) |
The effect of each channel on two or more results, and one budget plan for all of them (national data) | yes | yes | — |
campaign_impact - Effect of a campaign v0.13 |
causal.impact |
The effect of a campaign, a launch or a price change on one series, with optional control series | — | — | — |
geo_lift - Geo-lift test v0.13 |
causal.synthetic_control |
The effect of a campaign in test markets, against a mix of the other markets. The report gives a lift test for the MMM. | — | — | — |
weekly_forecast - Weekly forecast |
ts.hsgp_trend |
One series, some weeks or months ahead, with a range | — | yes | — |
store_demand - Demand by store |
tabular.regressor |
Units for each store and week from prices and promotions (low/median/high) | — | yes | — |
customer_choice - Customer choice |
choice.multinomial_logit |
The product that customers select, and how price changes the selection | presets | — | — |
Customer choice: a conditional logit with household effects
customer_choice uses the built-in choice.multinomial_logit. The model has utilities
for each alternative from price and promotion. It has a product preference for each
household: a random-intercept logit, fitted at its posterior mode. It also has
Bayesian-bootstrap draws over households. Thus shares, counterfactual deltas and sums over
trips keep their uncertainty. If a product has no price on a trip, the product was not on
the shelf. New households draw their preferences from the fitted spread. Choice values
must name products in the same way as the price columns (price_cola goes with the choice
cola). Refer to
Built-in models.
All templates fit with fit.mode: evaluate_then_refit. First, the platform fits the model
without the latest periods, to measure its forecast error. Then it fits the model again on all
the data. Endpoints need this fit mode.
2. Questions instead of a role mapper¶
The user does not map columns to dims and roles. The user answers questions, for example
"Which column is the result you want to explain?" or "Which columns are media spend (one per
channel)?". A question (GuidedQuestion) tells what its answer becomes:
- a dim (
date→ the time dim,geo→ markets), with the policy of the dim - a variable with a role (
kpi→ the target) - one variable along a new dim that the platform makes from some columns (
spend→ onespendvariable alongchannel, with one label for each column:tv_spendbecomes channeltv) - some separate variables (
controls), or a choice or a number (frequency, holdout length).
Role detection¶
The browser makes a profile of the upload on the local computer. Thus no dangerous data goes
to the servers for a preview. The browser sends ColumnProfiles. Each profile has the name,
the type, the row and missing counts, the distinct values, the range and the date frequency
that the browser found. POST /v1/guided/detect-roles (or the pure function
mapi_spec.guided.suggest_roles) ranks the columns for each question and fills in the
answers:
- The function splits names into words (
SearchSpend→search spend). It compares the words with the keywords of each question. - Types must agree. A date question needs dates. A spend question needs numbers, and numbers that are never negative are better.
- Columns that look like labels (few values that repeat) are good for dim questions. Columns that are almost empty lose points.
- Dates in sequence and trip numbers that increase are good for ordered dims. Spend with negative values and columns that do not change lose points.
Each candidate has a confidence between 0 and 1 and a reason_plain, for example "named
like a revenue column; never negative". The function fills in an answer when the confidence
is 0.5 or more. A column answers a maximum of one question. The function reads the data
frequency from the selected date column. If a necessary question has no candidate, the
function returns a warning.
Detection also works on a stored dataset version (dataset instead of columns). The
ingest run of the version wrote columns.json. This file is the profile that the sandbox made
of each uploaded column (for MFF uploads, of the pivoted variables). For versions without this
file, the platform makes the profile from the stored schema and summary. The API reads only
that JSON.
Drafts and "View as YAML"¶
POST /v1/guided/drafts (mapi_spec.guided.build_draft) changes GuidedAnswers into a
GuidedDraft. The draft contains these items:
- an
IngestMappingfor the upload. Column names become safe dataset names:Revenue (EUR)→revenue_eur. - the
ModelSpecdocument. Each placeholder gets a value from the answers, the template defaults or lookups (for example, the season length from the frequency). Thecalibrationblock contains the business priors and lift tests. - the same spec as YAML.
The server does a static validation of the draft against the catalog. It uses the schema that
the mapping gives (mapi_spec.guided.draft_schema). It returns the findings in issues. The
server stores nothing. Upload the data as a dataset version with the mapping. Then save the
spec with POST /v1/specs. Incorrect answers come back as GUIDED_ANSWERS_INVALID, with a
pointer to /answers/<question>.
File formats and presets¶
Only a sandboxed ingest run parses uploads. You can give an explicit column mapping. Or the
IngestMapping.preset field can name a file contract that marketing teams already use:
| Format or preset | Contract |
|---|---|
csv, parquet |
A column mapping: dims, vars and, for wide tables, wide (WideVarMapping: one variable from one column for each label) |
mff (format or preset) |
The MFF long table below |
meridian |
The CoordToColumns wide table of Google Meridian (MeridianColumns): time, geo, kpi, controls, and parallel media/media_spend (and reach/frequency) lists for each channel |
zarr_zip, netcdf |
Labelled datasets that describe themselves |
The MFF ("model feature format") long table has one row for each period, key combination
and variable. mmm-framework uses the same MFF convention (mapi_spec.ingest.MFF_COLUMNS):
| Column | Meaning |
|---|---|
Period |
The date. Weekly data must be on the same day of the week. |
Geography, Product |
Panel levels. Each column becomes a dim when it has more than one level. A blank label (or National, Total, All) is the total. The ingest spreads a variable that has values only in the total (national TV) across the levels. Geographies get equal parts. Products get parts by the share of the KPI (MffOptions.allocation). mmm-framework does the same. |
Outlet, Campaign, Creative |
Breakdowns. The ingest adds together rows that are different only in these columns. MffOptions.split keeps the column as a dim. |
VariableName |
The variable (Sales, TV_Spend, ...) |
VariableValue |
The value |
You can also pivot MFF tables and then map them like a CSV file (the variables become
columns). MffOptions.combine joins key columns into one dim. The ingest applies limits on
rows, columns, cell length and decompression. It explains each refusal. Refer to
File formats and presets.
3. Business priors and lift tests¶
Users tell what they already believe in business units (BusinessPriors):
calibration:
business_priors:
channels:
tv: {roi: [1.5, 4.0], credibility: 0.8} # every euro returns 1.5-4 euros, 80% sure
search: {roi: [2.0, 6.0], credibility: 0.9}
social: {contribution_share: [0.02, 0.10]} # 2-10% of revenue
baseline_share: {share: [0.5, 0.8]}
lift_tests:
- {id: tv-holdout-2026q2, channel: tv, geo: de, start: 2026-04-06, end: 2026-05-03,
spend_delta: -120000, kpi_delta: -260000, ci: [-380000, -140000], ci_level: 0.9}
- Credibility is the probability that the true value is in the range. A range
[low, high]at credibility c becomes a log-normal (ROI) or a logit-normal (share) distribution. The central c interval of this distribution is exactly that range (lognormal_from_range). - Shares must fit in the KPI. The smallest shares (channels plus baseline) cannot add up to
more than 100% (
BUSINESS_PRIORS_INVALID). - You must give a minimum of one belief, but any one is sufficient.
baseline_sharealone (nochannels) makes only the baseline narrower. Channels without a belief keep the default priors of the model. - Lift tests are experiment results that the model uses as calibration data. A lift test has extra (or removed) spend on one channel, optionally in one geo, over a window. It also has the incremental KPI that the spend caused, with a standard error or a confidence interval. The model uses each lift test as an extra observation of the incremental effect of the channel.
- Only models that declare them (
Capabilities.calibration) accept them. For other models, static validation reportsCALIBRATION_UNSUPPORTED.
To change beliefs into priors in model space, the platform needs the scale of the data and the
transforms of the model. Thus this change occurs in the sandbox as a translate_priors run
(POST /v1/guided/prior-translations). Its result (PriorTranslationResult) contains the
priors to write at /model/settings/priors (apply_prior_translation). It also contains a
prior-predictive preview in business units. The preview shows the ROI and
contribution-share distributions for each channel, the baseline share and a KPI fan. Thus
users see the effect of their beliefs before the fit. Guided modelling
gives the math, the lift-test likelihood and the checks that each layer does.
4. Run, then the sanity scorecard¶
Each fit gets a Scorecard of checks in plain language. Each check is pass, warn or
fail. It has the measured value, the threshold, a message_plain, a suggested action and a
link back to this page. overall is the worst item. Templates select their checks and can make
thresholds stricter. The catalog is mapi_spec.scorecard.SCORECARD_CHECKS:
Convergence¶
Holdout error¶
holdout.hit_rate is the check of the customer_choice template. For each household, the
check uses the trip in the holdout. It compares the most probable product of the model with
the product that the household bought (accuracy, the hit rate). It also compares the model
with a guess that is always the most popular product of the holdout trips (accuracy_naive).
This baseline is strict, because it knows the shares of the holdout. accuracy_lift is the
difference in percentage points. The template also reports two more values in the metrics of
the model. These are the log_score (the mean log probability of the bought product, where
higher is better) and the multi-class brier score (0 is perfect).
Plausibility¶
Data¶
| Check | Metric | Thresholds | What to do |
|---|---|---|---|
data.missing (Few missing values) |
missing_fraction |
warn above 5%, fail above 30% | Fill the gaps in the source data, or remove the series that have gaps. The check counts only the variables that contain observations. Refer to Guided modelling. |
data.history (Enough history) |
history_years |
warn below 2 years, fail below 1 year (the weekly_forecast template: below 0.5) |
Add more history if you have it. |
data.spend_variation (Spend changes enough) |
spend_cv_min |
warn below 10%, fail below 2% | Add a lift test for the channel, or give its ROI in business priors. |
Guided modelling tells how the runner measures each check. The scorecard also controls promotion to an endpoint. Refer to Integrations.
5. What-if sliders¶
Sliders give answers immediately, because each MMM fit calculates response grids for each
draw before you use them (ResponseGridView, ui/grids/response_curves.json, format
mapi.response-grid/v1). The grid has values for each thinned posterior draw, channel and
spend multiplier. It contains the total contribution of the channel over the fit window and
the spend at each grid point. It also contains the actual spend and the baseline.
A WhatIfScenario sets a multiplier for each channel (1 = the actual spend). The web app
and mapi_spec.whatif.evaluate_scenario use the same interpolation contract:
- A channel without a multiplier keeps
1. The contract refuses multipliers outside the grid (SCENARIO_OUT_OF_RANGE). It never extrapolates the grid. - For each draw and channel, the contract interpolates the contribution linearly between the two grid points around the multiplier. It does the same for spend.
- For each draw, KPI = baseline + the contributions of the channels. The change is the
difference from the KPI with all multipliers at
1. The contract calculates means and quantiles after the sum. Thus the uncertainty band stays joint across channels.
The web app calculates the slider answers in the browser with a TypeScript port of this
contract. Its unit tests compare the port with golden cases that the spec tests export
(packages/spec/tests/snapshots/whatif_golden.json).
Run exact changes the scenario into a counterfactual score request, with one multiply
intervention for each changed channel. Then it starts a real score run
(POST /v1/guided/requests:compile, then score).
Forecasts and counterfactuals¶
Guided forecasts (forecast_request) need a horizon. They also need a policy for covariates
that have no future values from the user (ScoreRequest.future_covariates). The policies are
carry_last (the default in Guided mode), seasonal_naive, zero, or supplied with a
scenario dataset. Intervention presets make counterfactuals with one click:
- channel off (
ChannelOff): the spend of that channel is zero. - budget × k (
BudgetScale): the platform multiplies the spend of all channels, or of the listed channels, by k.
The presets apply over the fit window or over a forecast horizon. By default, responses are expected values with the mean and a 90% interval (Q2).