AI Query API — cubos OLAP
A AI Query API permite que um agente consulte um cubo OLAP Mondrian sem nunca ver ou escrever MDX. O agente busca um schema autodescritivo, preenche uma requisição JSON contra ele, o servidor valida cada nome, executa e retorna registros tipados. Se um nome está errado, o erro diz ao agente exatamente o que consertar — sem prompt engineering necessário.
Esta é a contraparte OLAP da
AI Query API para modelos Ossie (a superfície de
SQL/YAML semântico). Mesma disciplina, mesmas proteções; esta trabalha
contra cubos Mondrian em vez de YAML Ossie. Assentada sobre ela está a
camada de linguagem natural /ai/ask — o endpoint ask
traduz uma pergunta em inglês simples exatamente para o corpo de
requisição documentado aqui.
Orientação rápida
Três endpoints cobrem ~90% do uso por agentes:
| Endpoint | Propósito |
|---|---|
GET /rest/saiku/api/ai/cubes | Lista todo cubo que o chamador pode consultar. |
GET /rest/saiku/api/ai/schema/{cubeId} | Schema autodescritivo — measures, dimensions, hierarchies, levels, sample members, synonyms, e o JSON Schema da requisição. |
POST /rest/saiku/api/ai/query | Executa uma requisição tipada. Resposta em formato records por default; matrix em ?format=matrix. |
Endpoints de cauda longa (documentados nas páginas linkadas):
| Endpoint | Propósito |
|---|---|
GET /rest/saiku/api/ai/members/search | Lookup por substring de members em um level. |
POST /rest/saiku/api/ai/scenario/whatif | Simulação de what-if com write-back. |
POST /rest/saiku/api/ai/query/execute-async | Submete para execução em background (consulte status / result). |
POST /rest/saiku/api/ai/anomaly | Executa a query e então sinaliza anomalias ao longo de um eixo temporal. |
POST /rest/saiku/api/ai/forecast | Projeta pontos futuros (ETS / ARIMA / Prophet). |
POST /rest/saiku/api/ai/ask | Pergunta em linguagem natural. Precisa de uma chave de LLM. |
O cubeId em todos os lugares é o quádruplo
connection/catalog/schema/cubeName unido com /. Todos os endpoints
exigem uma sessão autenticada; endpoints POST exigem o par
cookie/header de CSRF.
Passo 1 — listar os cubos
GET /rest/saiku/api/ai/cubes[ { "connectionName": "unknown_foodmart", "catalog": "FoodMart", "schema": "FoodMart", "cubeName": "Sales", "cubeCaption": "Sales", "defaultMeasure": "Unit Sales", "measureCount": 8 }]O quádruplo connectionName/catalog/schema/cubeName é o
identificador de cubo usado em todos os outros lugares.
Passo 2 — buscar o schema tipado
GET /rest/saiku/api/ai/schema/unknown_foodmart/FoodMart/FoodMart/SalesNão faça URL-encode das barras — o template do path aceita a forma
multi-segmento connection/catalog/schema/cubeName diretamente. A
resposta é densa — é isso que torna a API autodescritiva:
{ "cubeId": "unknown_foodmart/FoodMart/FoodMart/Sales", "measures": { "store sales": { "name": "Store Sales", "uniqueName": "[Measures].[Store Sales]", "description": "Net retail revenue in USD across all transactions.", "synonyms": ["revenue", "turnover", "top-line"], // accepted as `name` on input "unit": "USD", "aggregationKind": "sum" } // …8 measures total… }, "measureAliases": { "revenue": "store sales", "turnover": "store sales" }, "dimensions": { "time": { "name": "Time", "uniqueName": "[Time]", "hierarchies": { "time by": { "name": "Time By", "levels": { "quarter": { "name": "Quarter", "synonyms": ["quarterly", "qtr"], // accepted as `level` on input "sampleMembers": [ { "caption": "Q1", "uniqueName": "[Time].[Time By].[Quarter].&[Q1]" } ] } } } } } }}Synonyms e aliases de nome de exibição são aceitos como input em
qualquer lugar onde o name canônico é — um agente pode dizer
"revenue" e o servidor o resolve para Store Sales.
Passo 3 — executar uma query
“Show Store Sales and Unit Sales by Product Family, top 3 by Store Sales.”
POST /rest/saiku/api/ai/queryContent-Type: application/json{ "cube": "unknown_foodmart/FoodMart/FoodMart/Sales", "measures": [{ "name": "Store Sales" }, { "name": "Unit Sales" }], "rows": [{ "dimension": "Product", "hierarchy": "Products", "level": "Product Family" }], "order": [{ "by": "Store Sales", "direction": "desc" }], "limit": 3}cube aceita tanto a forma de objeto de 4 segmentos quanto a string
compacta "connection/catalog/schema/cube".
Resposta (200):
{ "status": "SUCCESS", "format": "records", "metadata": { "generatedMdx": "SELECT NON EMPTY {[Measures].[Store Sales], [Measures].[Unit Sales]} ON COLUMNS, NON EMPTY TopCount([Product].[Products].[Product Family].Members, 3, [Measures].[Store Sales]) ON ROWS FROM [Sales]", "freshness": { "computedAtMillis": 1715798421042, "cached": false } }, "data": [ { "Product Family": "Food", "Store Sales": { "value": 409035.59, "formatted": "409,035.59", "unit": null }, "Unit Sales": { "value": 191940.0, "formatted": "191,940", "unit": null } } ], "totalRows": 3, "runtimeMs": 421}Cada linha é um objeto autodescritivo indexado pelo caption legível da coluna. Toda célula numérica é um envelope tipado:
value— número parseado (para matemática / ordenação / gráficos)formatted— a string de exibição pré-formatada do Mondrian (para UI)unit— farejada da string formatada (USD,GBP,EUR,JPY,%) ounull
generatedMdx é ecoado para debug; agentes tipicamente o ignoram.
Formato matrix
Clientes indexados por posição optam por sair de records com
?format=matrix — a resposta carrega matrix em vez de data, cada
linha indexada pelo índice da coluna como string, células ainda no
envelope tipado {value, formatted, unit}.
Privacidade: k-anonimato
Quando ai.kAnonymity está definido (default 5; 0 desabilita), o
servidor mascara valores de measure de células pequenas antes que o
resultado cruze a fronteira da AI — qualquer linha cuja measure de
contagem no resultado cai abaixo de k tem suas células de measure
mascaradas com suppressed: true. Aplica-se a records e matrix, e aos
endpoints derivados /ai/anomaly + /ai/forecast.
Passo 4 — validação ensina o agente
Forneça um nome que não resolve e o servidor retorna 400 com um corpo do qual o agente pode se autocorrigir — sem prompting de retry necessário:
{ "status": "VALIDATION_ERROR", "error": "Unknown measure 'Made Up Measure'", "field": "measures[].name", "available": ["Unit Sales", "Store Cost", "Store Sales", "Profit", "Customer Count"]}O agente lê field (o que estava errado), lê available[] (os valores
legais), conserta e tenta de novo. Duas camadas rodam: um validador
de formato (JSON Schema — fields faltando, tipos errados, violações
de enum, com paths de field indexados por array como filters[0].op) e
um validador semântico (resolução do cubo — nomes que são válidos
de formato mas não existem). O contrato é idêntico de qualquer forma:
leia field, leia available[], conserte, tente de novo.
Para onde ir a seguir
- AI Ask API — a camada de linguagem natural que
produz o corpo de requisição acima, mais os endpoints companheiros
members/search,scenario/whatifepii-suggestions. - Agent Spaces — personas que escopam esta superfície a um conjunto de cubos em allowlist.
- Agent Skills — workflows autorados por admin, descobríveis de cada ask.
- Servidor MCP — a mesma superfície tipada para hosts de
agentes externos (
list_cubes,describe_cube,run_query, …). - AI Query API para modelos Ossie — a contraparte em SQL/YAML semântico.
- AI inference API — uma superfície diferente: o designer de cubos em design-time (profile → propose → render), não execução de query.