Zum Inhalt springen

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:

EndpunktZweck
GET /rest/saiku/api/ai/cubesListet 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/queryFührt eine typisierte Anfrage aus. Standardmäßig Records-Format als Antwort; Matrix bei ?format=matrix.

Long-Tail-Endpunkte (auf den verlinkten Seiten dokumentiert):

EndpunktZweck
GET /rest/saiku/api/ai/members/searchSubstring-Lookup von Members auf einem Level.
POST /rest/saiku/api/ai/scenario/whatifWrite-back-What-if-Simulation.
POST /rest/saiku/api/ai/query/execute-asyncReicht zur Hintergrundausführung ein (status / result abfragen).
POST /rest/saiku/api/ai/anomalyFührt die Abfrage aus und markiert dann Anomalien entlang einer Zeitachse.
POST /rest/saiku/api/ai/forecastProjiziert zukünftige Punkte (ETS / ARIMA / Prophet).
POST /rest/saiku/api/ai/askNatü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/Sales

Kodieren 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/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 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, %) oder null

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/whatif und pii-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.