Saltearse al contenido

API AI Query — cubos OLAP

La API AI Query permite a un agente consultar un cubo OLAP Mondrian sin ver ni escribir jamás MDX. El agente obtiene un schema autodescriptivo, rellena una petición JSON contra él, el servidor valida cada nombre, ejecuta, y devuelve registros tipados. Si un nombre está mal, el error le dice al agente exactamente qué corregir — sin ingeniería de prompts requerida.

Esta es la contraparte OLAP de la API AI Query para modelos Ossie (la superficie SQL/YAML-semántico). La misma disciplina, las mismas barandillas; esta trabaja contra cubos Mondrian en lugar de YAML de Ossie. Sobre ella se sienta la capa de lenguaje natural /ai/ask — el endpoint de ask traduce una pregunta en inglés llano exactamente al cuerpo de petición documentado aquí.

Orientación rápida

Tres endpoints cubren ~90% del uso de los agentes:

EndpointPropósito
GET /rest/saiku/api/ai/cubesLista cada cubo que el llamador puede consultar.
GET /rest/saiku/api/ai/schema/{cubeId}Schema autodescriptivo — medidas, dimensiones, jerarquías, niveles, miembros de muestra, sinónimos, y el JSON Schema de la petición.
POST /rest/saiku/api/ai/queryEjecuta una petición tipada. Respuesta en formato de registros por defecto; matrix en ?format=matrix.

Endpoints de cola larga (documentados en las páginas enlazadas):

EndpointPropósito
GET /rest/saiku/api/ai/members/searchBúsqueda por subcadena de miembros en un nivel.
POST /rest/saiku/api/ai/scenario/whatifSimulación what-if con write-back.
POST /rest/saiku/api/ai/query/execute-asyncEnvía para ejecución en segundo plano (consulte status / result).
POST /rest/saiku/api/ai/anomalyEjecuta la consulta y luego marca anomalías a lo largo de un eje temporal.
POST /rest/saiku/api/ai/forecastProyecta puntos futuros (ETS / ARIMA / Prophet).
POST /rest/saiku/api/ai/askPregunta en lenguaje natural. Necesita una clave de LLM.

El cubeId en todas partes es el cuádruple connection/catalog/schema/cubeName unido con /. Todos los endpoints requieren una sesión autenticada; los endpoints POST requieren el par cookie/cabecera CSRF.

Paso 1 — listar los cubos

GET /rest/saiku/api/ai/cubes
[
{
"connectionName": "unknown_foodmart",
"catalog": "FoodMart",
"schema": "FoodMart",
"cubeName": "Sales",
"cubeCaption": "Sales",
"defaultMeasure": "Unit Sales",
"measureCount": 8
}
]

El cuádruple connectionName/catalog/schema/cubeName es el identificador de cubo usado en todo lo demás.

Paso 2 — obtener el schema tipado

GET /rest/saiku/api/ai/schema/unknown_foodmart/FoodMart/FoodMart/Sales

No codifique en URL las barras — la plantilla de ruta acepta la forma multi-segmento connection/catalog/schema/cubeName directamente. La respuesta es densa — esto es lo que hace la API autodescriptiva:

{
"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]" }
]
}
}
}
}
}
}
}

Los sinónimos y alias de nombre para mostrar se aceptan como entrada en cualquier sitio donde se acepte el name canónico — un agente puede decir "revenue" y el servidor lo resuelve a Store Sales.

Paso 3 — ejecutar una consulta

“Muestra Store Sales y Unit Sales por Product Family, top 3 por Store Sales.”

POST /rest/saiku/api/ai/query
Content-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 acepta o bien la forma de objeto de 4 segmentos o bien la cadena compacta "connection/catalog/schema/cube".

Respuesta (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 fila es un objeto autodescriptivo indexado por el caption de columna legible por humanos. Cada celda numérica es un sobre tipado:

  • value — número parseado (para matemáticas / ordenación / graficado)
  • formatted — la cadena de visualización pre-formateada de Mondrian (para la UI)
  • unit — inferida de la cadena formateada (USD, GBP, EUR, JPY, %) o null

generatedMdx se devuelve para depuración; los agentes normalmente lo ignoran.

Formato matrix

Los clientes indexados por posición se salen de los registros con ?format=matrix — la respuesta lleva matrix en lugar de data, cada fila indexada por el índice de columna como cadena, las celdas siguen siendo el sobre tipado {value, formatted, unit}.

Privacidad: k-anonimato

Cuando ai.kAnonymity está definido (por defecto 5; 0 lo deshabilita), el servidor enmascara los valores de medida de celdas pequeñas antes de que el resultado cruce la frontera de la IA — cualquier fila cuya medida de recuento en el resultado cae por debajo de k tiene sus celdas de medida enmascaradas con suppressed: true. Aplica a registros y matrix, y a los endpoints derivados /ai/anomaly + /ai/forecast.

Paso 4 — la validación enseña al agente

Suministre un nombre que no resuelve y el servidor devuelve 400 con un cuerpo del que el agente puede autocorregirse — sin necesidad de reintentar mediante prompts:

{
"status": "VALIDATION_ERROR",
"error": "Unknown measure 'Made Up Measure'",
"field": "measures[].name",
"available": ["Unit Sales", "Store Cost", "Store Sales", "Profit", "Customer Count"]
}

El agente lee field (qué estuvo mal), lee available[] (los valores legales), corrige y reintenta. Corren dos capas: un validador de forma (JSON Schema — campos faltantes, tipos incorrectos, violaciones de enum, con rutas de campo indexadas por array como filters[0].op) y un validador semántico (resolución del cubo — nombres que son válidos en forma pero no existen). El contrato es idéntico en cualquier caso: lea field, lea available[], corrija, reintente.

A dónde ir después

  • API AI Ask — la capa de lenguaje natural que produce el cuerpo de petición de arriba, más los endpoints compañeros members/search, scenario/whatif y pii-suggestions.
  • Agent Spaces — personas que limitan esta superficie a un conjunto de cubos en lista de permitidos.
  • Agent Skills — flujos de trabajo escritos por admins, descubribles desde cada ask.
  • Servidor MCP — la misma superficie tipada para hosts de agentes externos (list_cubes, describe_cube, run_query, …).
  • API AI Query para modelos Ossie — la contraparte SQL/YAML-semántico.
  • API de inferencia de IA — una superficie diferente: el diseñador de cubos en tiempo de diseño (profile → propose → render), no la ejecución de consultas.