认证
Authentication2所有受保护端点需要在 Header 中携带 Supabase JWT:
Authorization: Bearer
后端通过 get_current_user 校验 JWT 并解析用户所属组织,数据按 org_id 行级隔离。
Supabase Auth 注册/登录后调用,创建或返回用户资料。
{\n "display_name": "Ada"\n}{\n "profile": {\n "id": "user_123",\n "org_id": "00000000-0000-0000-0000-000000000001",\n "display_name": "Ada",\n "role": "analyst"\n },\n "created": true\n}返回已认证用户、组织、角色、邮箱及头像 URL。
基础路径与限制
Base Path & Limits基础路径:/api(完整 URL 取决于部署环境,本地开发为 http://localhost:8000/api)
数据隔离:所有资源按 org_id 隔离,跨组织访问返回 403。
并发限制:每个项目同一时间只允许一个活跃模型运行,重复发起返回 409。
AI 速率限制:每个组织每 10 秒最多一次 LLM 生成;缓存命中不受限。
POST /api/agent/upload {"demo":true} 无需认证,但仅返回示例数据。项目管理
Projects6返回当前组织的所有项目,包含 Champion run 摘要(如有)。
{\n "name": "Q1 Brand MMM",\n "description": "Weekly revenue model",\n "kpi_type": "revenue",\n "time_granularity": "weekly",\n "geo_level": false,\n "model_type": "ridge"\n}{\n "project": {\n "id": "project_123",\n "name": "Q1 Brand MMM",\n "status": "draft",\n "kpi_type": "revenue",\n "time_granularity": "weekly",\n "geo_level": false,\n "model_type": "ridge"\n }\n}返回已认证用户、组织、角色、邮箱及头像 URL。
{
"user": {
"id": "user_123",
"org_id": "00000000-0000-0000-0000-000000000001",
"email": "ada@brand.com",
"role": "analyst",
"display_name": "Ada",
"avatar_url": null
}
}所有字段可选:
{\n "name": "Q1 Brand MMM - Final",\n "kpi_type": "revenue",\n "time_granularity": "weekly",\n "geo_level": true,\n "model_type": "pymc",\n "config": { "calendar": "us" }\n}获取单个项目详情,包含状态、配置和 Champion run 摘要。
{
"project": {
"id": "project_123",
"name": "Q1 Brand MMM",
"status": "active",
"kpi_type": "revenue",
"time_granularity": "weekly",
"geo_level": false,
"model_type": "ridge",
"champion_run_id": "run_456"
}
}创建内置电商 demo 项目,自动上传演示数据、映射字段并启动 Ridge 运行。
数据上传与映射
Data Upload8支持 .csv / .xlsx / .xls,最大 50 MB。
Content-Type: multipart/form-data
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file | file | 是 | .csv / .xlsx / .xls |
| 参数 | 可选值 | 默认 | 说明 |
|---|---|---|---|
strategy | replace / append / new_root | replace | 数据集版本策略 |
{\n "dataset": {\n "id": "dataset_123",\n "project_id": "project_123",\n "file_name": "weekly_marketing.csv",\n "row_count": 104,\n "col_count": 9,\n "version": 1,\n "is_active": true,\n "upload_status": "validated"\n },\n "analysis": {\n "row_count": 104,\n "col_count": 9,\n "suggested_mappings": [\n { "column": "week_start", "type": "date" },\n { "column": "revenue_usd", "type": "kpi" }\n ]\n },\n "quality": { "overall_status": "pass", "findings": [], "stats": {} }\n}删除项目及其关联的数据集和运行记录。操作不可逆。
{
"deleted": true,
"project_id": "project_123"
}返回项目的所有数据集版本,标记当前活跃版本。
{
"datasets": [
{
"id": "dataset_123",
"file_name": "weekly_marketing.csv",
"row_count": 104,
"version": 1,
"is_active": true,
"upload_status": "validated"
}
]
}返回当前活跃数据集及其分析摘要。
{
"dataset": {
"id": "dataset_123",
"file_name": "weekly_marketing.csv",
"row_count": 104,
"col_count": 9,
"version": 1,
"is_active": true
}
}将指定数据集版本设为活跃。后续模型运行将使用此版本。
{
"dataset": { "id": "dataset_124", "is_active": true },
"message": "Dataset version activated."
}{\n "mappings": [\n { "source_column": "week_start", "target_type": "date" },\n { "source_column": "revenue_usd", "target_type": "kpi" },\n { "source_column": "google_spend", "target_type": "media_spend", "channel_name": "Google" },\n { "source_column": "holiday_flag", "target_type": "control" }\n ]\n}target_type 可选值:date / kpi / media_spend / control / geo / ignore
返回当前字段映射配置。
{
"mappings": [
{ "source_column": "week_start", "target_type": "date" },
{ "source_column": "revenue_usd", "target_type": "kpi" },
{ "source_column": "google_spend", "target_type": "media_spend", "channel_name": "Google" },
{ "source_column": "holiday_flag", "target_type": "control" }
]
}返回列统计、时间序列、花费占比、相关矩阵和分布。
建模分析
Modeling6{\n "model_type": "ridge",\n "n_chains": 4,\n "n_iterations": 2000,\n "config": {\n "adstock_type": "geometric",\n "priors": {},\n "project_settings": {\n "kpi_type": "revenue",\n "time_granularity": "weekly",\n "geo_level": false\n }\n }\n}{\n "run_id": "run_123",\n "status": "pending",\n "dispatch_mode": "subprocess",\n "message": "Model run started. Poll the status endpoint for progress."\n}n_chains ≥ 2 且 n_iterations ≥ 100。关键数据质量问题返回 422。返回数据质量报告,包含总体状态和具体 findings。
{
"overall_status": "pass",
"findings": [
{
"severity": "warning",
"code": "low_variation",
"message": "Channel tv_spend has low variation (CV=0.08)",
"column": "tv_spend"
}
],
"stats": {
"row_count": 104,
"col_count": 9,
"missing_pct": 0.2,
"duplicate_periods": 0
}
}granularity 参数可选 daily / weekly / monthly。{\n "id": "run_123",\n "project_id": "project_123",\n "status": "sampling",\n "progress_pct": 45,\n "current_step": "sampling",\n "started_at": "2026-07-02T08:00:00Z",\n "completed_at": null,\n "duration_seconds": null,\n "r_squared": null,\n "mape": null,\n "max_rhat": null,\n "min_ess": null,\n "error_message": null,\n "model_type": "pymc",\n "engine": null,\n "run_config": {}\n}状态流转:pending → preprocessing → sampling → completed / failed
返回项目的所有模型运行记录,按时间倒序。
{
"runs": [
{
"id": "run_123",
"status": "completed",
"model_type": "ridge",
"r_squared": 0.92,
"mape": 4.8,
"is_champion": true,
"started_at": "2026-07-01T08:00:00Z",
"duration_seconds": 25
}
]
}返回 Champion run(如有),否则返回最近一次完成的运行。
{\n "engines": ["ridge", "pymc"],\n "n_chains": 4,\n "n_iterations": 2000,\n "config": {}\n}可选引擎:
ridgepymcstanmeridianrobynnevergradsparkxsparkx-expertlgbm
meridian 和 robyn 为环境依赖引擎,可能未在所有部署中启用。结果查询
Results6返回运行元数据和渠道摘要。
{\n "channels": [\n {\n "channel_name": "Google",\n "roi_mean": 3.2,\n "roi_ci_low": 2.4,\n "roi_ci_high": 4.1,\n "contribution_pct": 28.5,\n "adstock_halflife_weeks": 1.8,\n "saturation_pct": 64.0,\n "current_spend": 12000,\n "marginal_roi": 1.7,\n "health_score": 82\n }\n ]\n}取消正在进行的模型运行。已完成的运行不受影响。
{
"run_id": "run_123",
"status": "cancelled",
"message": "Model run cancelled."
}pending / preprocessing / sampling 状态可取消。返回时间对齐的 actual / predicted / residual 序列(如可用)。
返回 baseline 和各渠道贡献序列(如可用)。
返回模型诊断指标:R-squared、MAPE、预测 vs 实际序列、残差。
{
"r_squared": 0.92,
"mape": 4.8,
"n_observations": 104,
"predictions": [
{ "date": "2026-01-01", "actual": 128500, "predicted": 126800, "residual": 1700 }
]
}预算优化
Optimization6支持两种优化目标:给定预算最大化收入、给定收入目标最小化花费。
固定预算模式:
{\n "objective": "maximize_revenue",\n "total_budget": 250000,\n "budget_constraints": {\n "Google": { "min": 50000, "max": 120000 },\n "Meta": { "min": 30000, "max": 100000 }\n }\n}目标收入模式:
{\n "objective": "target_revenue",\n "target_revenue": 1000000,\n "budget_constraints": {\n "Google": { "min": 50000, "max": 150000 }\n }\n}{\n "scenario": {\n "id": "scenario_123",\n "run_id": "run_123",\n "total_budget": 250000,\n "current_allocation": {},\n "optimal_allocation": {},\n "current_revenue": 850000,\n "predicted_revenue": 930000,\n "revenue_uplift_pct": 9.4,\n "recommendations": []\n }\n}对比两个运行的渠道 ROI、贡献度和诊断指标差异。
{
"run_a": { "id": "run_123", "model_type": "ridge", "r_squared": 0.92 },
"run_b": { "id": "run_456", "model_type": "pymc", "r_squared": 0.94 },
"channel_diff": [
{ "channel": "Google", "roi_a": 3.2, "roi_b": 3.5, "roi_diff": 0.3 }
]
}{\n "allocations": {\n "Google": 90000,\n "Meta": 70000,\n "TikTok": 30000\n }\n}{\n "name": "Q3 constrained plan",\n "allocations": { "Google": 90000, "Meta": 70000 },\n "notes": "Finance-approved cap"\n}返回该运行的所有优化场景。
{
"scenarios": [
{
"id": "scenario_123",
"name": "Maximize Q3 Revenue",
"total_budget": 250000,
"predicted_revenue": 930000,
"revenue_uplift_pct": 9.4
}
]
}返回用户保存的 what-if 场景列表。
{
"scenarios": [
{
"id": "scenario_125",
"name": "Q3 constrained plan",
"allocations": { "Google": 90000, "Meta": 70000 },
"notes": "Finance-approved cap"
}
]
}AI 洞察
AI Insights3Claude 驱动的自然语言解读,支持中英双语。每个 section 可独立生成或重新生成。
{\n "section": "executive_summary",\n "force_regenerate": false,\n "locale": "zh",\n "custom_prompt": null\n}可选 section:
executive_summary— 执行摘要channel_deep_dive— 渠道深度解读risk_warnings— 风险提醒action_plan— 行动计划analyst_commentary— 分析师评论budget_rationale— 预算依据
{\n "interpretation": {\n "id": "interp_123",\n "run_id": "run_123",\n "section": "executive_summary",\n "content_md": "## Executive Summary\\n...",\n "content_structured": null,\n "model_used": "claude-sonnet-4-20250514"\n },\n "cached": false\n}force_regenerate=true 强制重新生成。删除指定的保存场景。删除后不可恢复。
{
"deleted": true,
"scenario_id": "scenario_125"
}{\n "locale": "zh",\n "messages": [\n { "role": "user", "content": "Which channel should I cut first?" }\n ]\n}数据科学诊断
Diagnostics8面向数据科学家的模型审计与方法论透明度视图。
拟合质量、运行元数据、R-squared、MAPE、R-hat、ESS 及参数摘要。
返回该运行所有已生成的 AI 洞察 section。
{
"interpretations": [
{
"id": "interp_123",
"section": "executive_summary",
"content_md": "## Executive Summary\n...",
"model_used": "claude-sonnet-4-20250514"
},
{
"id": "interp_124",
"section": "risk_warnings",
"content_md": "## Risk Warnings\n..."
}
]
}返回 trace / forest / pair / posterior predictive 检查(仅 Bayesian 运行)。
返回 posterior NetCDF 文件的签名 URL(如可用)。
返回 Bayesian 运行的收敛指标:R-hat、ESS、divergences。非 Bayesian 运行返回 null。
{
"max_rhat": 1.01,
"min_ess": 320,
"n_divergences": 2,
"n_chains": 4,
"n_iterations": 2000,
"converged": true
}R-hat ≥ 1.1 的运行不可用于决策。滚动起点验证,测试样本外稳定性。
与项目 Champion 对比,检测模型或数据漂移。
基于模型类型、拟合、不确定性和饱和度的透明告警。
报告下载
Reports2返回二进制 .pptx 文件。
Content-Disposition: MMM_Report_{project_name}_{run_id}.pptx
返回自包含 HTML 报告,可直接在浏览器打开。
Agent 与 Demo Pipeline
Agent & Demo4AutoMMM 的对话式 pipeline 接口,支持 demo 模式和文件上传模式。
Demo 模式(无需认证):
{\n "demo": true,\n "mode": "instant",\n "locale": "zh"\n}文件模式(multipart):
| 字段 | 类型 | 说明 |
|---|---|---|
file | file | CSV / XLSX / XLS |
mode | string | instant 或 expert |
locale | string | zh 或 en |
{\n "task_id": "task_123",\n "status": "running",\n "messages": []\n}返回模型稳健性评估:CI 宽度、预测精度、集中度风险。
{
"avg_ci_width": 0.8,
"prediction_accuracy": 95.2,
"concentration_risk": "low",
"channel_stability": [
{ "channel": "Google", "ci_width": 0.6, "stable": true },
{ "channel": "TikTok", "ci_width": 1.8, "stable": false }
]
}轮询 AutoMMM pipeline 进度和消息流。offset 参数用于增量拉取新消息。
{
"task_id": "task_123",
"status": "running",
"progress_pct": 60,
"current_step": "roi_analysis",
"messages": [
{ "step": "data_quality", "status": "completed", "message": "Data quality check passed" },
{ "step": "eda", "status": "completed", "message": "EDA completed, 5 channels detected" },
{ "step": "roi_analysis", "status": "running", "message": "Computing channel ROI..." }
]
}与 AutoMMM pipeline 进行对话式追问。基于当前 pipeline 结果上下文回答。
{
"task_id": "task_123",
"message": "TikTok 的 ROI 为什么这么高?"
}{
"reply": "TikTok ROI 为 4.2,高于 Google(3.2)。主要原因是 TikTok 当前花费较低,处于响应曲线陡峭区间...",
"context": { "run_id": "run_123" }
}错误码
Error CodesSparkX 使用标准 HTTP 状态码,错误响应包含 JSON detail 字段。
| 状态码 | 含义 | 常见原因 |
|---|---|---|
400 | 请求错误 | 无效策略、不支持的引擎、参数格式错误 |
401 | 未授权 | 缺少或无效的 JWT |
403 | 禁止访问 | 资源属于其他组织 |
404 | 未找到 | 项目、数据集、运行、诊断或报告不存在 |
409 | 冲突 | 该项目已有活跃模型运行 |
413 | 文件过大 | 上传文件超过 50 MB |
422 | 验证失败 | 数据质量问题、模型配置无效 |
429 | 速率限制 | AI 生成调用过于频繁 |
500 | 服务器错误 | 存储、解析或后端异常 |
502 | 上游 AI 故障 | LLM 提供方失败 |
503 | 服务不可用 | LLM 未配置 |
{
"detail": {
"code": "data_quality_failed",
"message": "Data quality check failed (2 critical issues)",
"findings": []
}
}{
"detail": "Project not found"
}代码示例
Code ExamplescURL — 上传 CSV
curl -X POST "http://localhost:8000/api/datasets/project_123/upload?strategy=replace" \ -H "Authorization: Bearer $TOKEN" \ -F "file=@weekly_marketing.csv"
cURL — 启动运行并轮询
curl -X POST "http://localhost:8000/api/modeling/project_123/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "model_type": "ridge", "n_chains": 4, "n_iterations": 2000 }'
curl "http://localhost:8000/api/modeling/run/run_123/status" \
-H "Authorization: Bearer $TOKEN"Python
import time, requests
BASE = "http://localhost:8000"
TOKEN = "YOUR_SUPABASE_JWT"
H = {"Authorization": f"Bearer {TOKEN}"}
project = requests.post(f"{BASE}/api/projects/",
headers={**H, "Content-Type": "application/json"},
json={"name": "API MMM", "kpi_type": "revenue",
"time_granularity": "weekly", "geo_level": False,
"model_type": "ridge"}).json()["project"]
with open("weekly_marketing.csv", "rb") as f:
upload = requests.post(f"{BASE}/api/datasets/{project['id']}/upload",
headers=H, files={"file": f}).json()
run = requests.post(f"{BASE}/api/modeling/{project['id']}/run",
headers={**H, "Content-Type": "application/json"},
json={"model_type": "ridge", "n_chains": 4,
"n_iterations": 2000, "config": {}}).json()
run_id = run["run_id"]
while True:
st = requests.get(f"{BASE}/api/modeling/run/{run_id}/status",
headers=H).json()
print(st["status"], st.get("progress_pct"), st.get("current_step"))
if st["status"] in ("completed", "failed"):
break
time.sleep(5)
summary = requests.get(f"{BASE}/api/results/run/{run_id}/summary",
headers=H).json()
print(summary)JavaScript
const BASE = "http://localhost:8000";
const token = "YOUR_SUPABASE_JWT";
async function api(path, options = {}) {
const headers = new Headers(options.headers || {});
headers.set("Authorization", "Bearer " + token);
if (!(options.body instanceof FormData))
headers.set("Content-Type", "application/json");
const res = await fetch(BASE + path, { ...options, headers });
if (!res.ok) throw new Error(await res.text());
return res.json();
}
const { project } = await api("/api/projects/", {
method: "POST",
body: JSON.stringify({
name: "JS MMM", kpi_type: "revenue",
time_granularity: "weekly", geo_level: false, model_type: "ridge"
})
});
const form = new FormData();
form.append("file", fileInput.files[0]);
await api("/api/datasets/" + project.id + "/upload?strategy=replace", {
method: "POST", body: form
});
const { run_id } = await api("/api/modeling/" + project.id + "/run", {
method: "POST",
body: JSON.stringify({
model_type: "ridge", n_chains: 4, n_iterations: 2000, config: {}
})
});
const status = await api("/api/modeling/run/" + run_id + "/status");
console.log(status);