AI Query API — OLAP-Cubes
Die AI Query API lässt einen Agenten einen Mondrian-OLAP-Cube abfragen, ohne je MDX zu sehen oder zu schreiben. Der Agent holt ein selbstbeschreibendes Schema, füllt eine JSON-Anfrage dagegen aus, der Server validiert jeden Namen, führt aus und gibt typisierte Records zurück. Ist ein Name falsch, sagt der Fehler dem Agenten genau, was zu korrigieren ist — kein Prompt-Engineering erforderlich.
Dies ist das OLAP-Gegenstück zur
AI Query API für Ossie-Modelle (der
SQL-/Semantik-YAML-Oberfläche). Dieselbe Disziplin, dieselben
Leitplanken; diese hier arbeitet gegen Mondrian-Cubes statt Ossie-YAML.
Darüber sitzt die natürlichsprachliche /ai/ask-Ebene —
der Ask-Endpunkt übersetzt eine natürlichsprachliche Frage in genau den
Request-Body, der hier dokumentiert ist.
Schnelle Orientierung
Drei Endpunkte decken ~90 % der Agenten-Nutzung ab:
| Endpunkt | Zweck |
|---|---|
GET /rest/saiku/api/ai/cubes | Listet jeden Cube auf, den der Aufrufer abfragen kann. |
GET /rest/saiku/api/ai/schema/{cubeId} | Selbstbeschreibendes Schema — Measures, Dimensionen, Hierarchien, Levels, Sample-Members, Synonyme und das Request-JSON-Schema. |
POST /rest/saiku/api/ai/query | Führt eine typisierte Anfrage aus. Standardmäßig Records-Format als Antwort; Matrix bei ?format=matrix. |
Long-Tail-Endpunkte (auf den verlinkten Seiten dokumentiert):
| Endpunkt | Zweck |
|---|---|
GET /rest/saiku/api/ai/members/search | Substring-Lookup von Members auf einem Level. |
POST /rest/saiku/api/ai/scenario/whatif | Write-back-What-if-Simulation. |
POST /rest/saiku/api/ai/query/execute-async | Reicht zur Hintergrundausführung ein (status / result abfragen). |
POST /rest/saiku/api/ai/anomaly | Führt die Abfrage aus und markiert dann Anomalien entlang einer Zeitachse. |
POST /rest/saiku/api/ai/forecast | Projiziert zukünftige Punkte (ETS / ARIMA / Prophet). |
POST /rest/saiku/api/ai/ask | Natürlichsprachliche Anfrage. Benötigt einen LLM-Schlüssel. |
Die cubeId ist überall das mit / verbundene
connection/catalog/schema/cubeName-Quadrupel. Alle Endpunkte
erfordern eine authentifizierte Session; POST-Endpunkte erfordern das
CSRF-Cookie/Header-Paar.
Schritt 1 — die Cubes auflisten
GET /rest/saiku/api/ai/cubes[ { "connectionName": "unknown_foodmart", "catalog": "FoodMart", "schema": "FoodMart", "cubeName": "Sales", "cubeCaption": "Sales", "defaultMeasure": "Unit Sales", "measureCount": 8 }]Das connectionName/catalog/schema/cubeName-Quadrupel ist der
Cube-Bezeichner, der überall sonst verwendet wird.
Schritt 2 — das typisierte Schema holen
GET /rest/saiku/api/ai/schema/unknown_foodmart/FoodMart/FoodMart/SalesKodieren Sie die Schrägstriche nicht URL-encoded — die Pfadvorlage
akzeptiert die mehrsegmentige connection/catalog/schema/cubeName-Form
direkt. Die Antwort ist dicht — genau das macht die API
selbstbeschreibend:
{ "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]" } ] } } } } } }}Synonyme und Anzeigenamen-Aliase werden überall dort als Eingabe
akzeptiert, wo der kanonische name steht — ein Agent kann "revenue"
sagen, und der Server löst es zu Store Sales auf.
Schritt 3 — eine Abfrage ausführen
„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 akzeptiert entweder die 4-segmentige Objektform oder den
kompakten "connection/catalog/schema/cube"-String.
Antwort (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}Jede Zeile ist ein selbstbeschreibendes Objekt, verschlüsselt nach der menschenlesbaren Spaltenüberschrift. Jede numerische Zelle ist ein typisierter Umschlag:
value— geparste Zahl (für Mathe / Sortieren / Charting)formatted— Mondrians vorformatierter Anzeigestring (für die UI)unit— aus dem formatierten String erschnüffelt (USD,GBP,EUR,JPY,%) odernull
generatedMdx wird zum Debuggen zurückgegeben; Agenten ignorieren es
typischerweise.
Matrix-Format
Positionsindizierte Clients wählen Records mit ?format=matrix ab —
die Antwort trägt matrix statt data, jede Zeile verschlüsselt nach
dem Spaltenindex als String, die Zellen weiterhin der typisierte
{value, formatted, unit}-Umschlag.
Datenschutz: K-Anonymität
Wenn ai.kAnonymity gesetzt ist (Standard 5; 0 deaktiviert),
maskiert der Server Measure-Werte kleiner Zellen, bevor das Ergebnis die
AI-Grenze überschreitet — jede Zeile, deren In-Result-Count-Measure unter
k fällt, hat ihre Measure-Zellen mit suppressed: true maskiert. Gilt
für Records und Matrix sowie für die abgeleiteten /ai/anomaly- +
/ai/forecast-Endpunkte.
Schritt 4 — Validierung lehrt den Agenten
Geben Sie einen Namen an, der nicht auflöst, und der Server gibt 400 mit einem Body zurück, aus dem sich der Agent selbst korrigieren kann — kein Retry-Prompting nötig:
{ "status": "VALIDATION_ERROR", "error": "Unknown measure 'Made Up Measure'", "field": "measures[].name", "available": ["Unit Sales", "Store Cost", "Store Sales", "Profit", "Customer Count"]}Der Agent liest field (was falsch war), liest available[] (die
zulässigen Werte), korrigiert und versucht es erneut. Zwei Ebenen laufen:
ein Shape-Validator (JSON Schema — fehlende Felder, falsche Typen,
Enum-Verletzungen, mit array-indizierten Feldpfaden wie filters[0].op)
und ein semantischer Validator (Cube-Auflösung — Namen, die
shape-valide sind, aber nicht existieren). Der Vertrag ist in beiden
Fällen identisch: field lesen, available[] lesen, korrigieren,
erneut versuchen.
Wohin als Nächstes
- AI Ask API — die natürlichsprachliche Ebene, die den
obigen Request-Body erzeugt, plus die Companion-Endpunkte
members/search,scenario/whatifundpii-suggestions. - Agent Spaces — Personas, die diese Oberfläche auf eine Allowlist-Cube-Menge beschränken.
- Agent Skills — von Admins verfasste Workflows, die aus jeder Anfrage auffindbar sind.
- MCP-Server — dieselbe typisierte Oberfläche für externe
Agent-Hosts (
list_cubes,describe_cube,run_query, …). - AI Query API für Ossie-Modelle — das SQL-/Semantik-YAML-Gegenstück.
- AI Inference API — eine andere Oberfläche: der Cube-Designer zur Design-Zeit (Profil → Vorschlag → Rendern), nicht die Abfrageausführung.