Espaces d'agent
Les espaces d’agent (Agent Spaces) sont des personas nommés, créés par l’admin, qui délimitent une question d’IA. Là où les compétences d’agent codifient des workflows individuels, un espace codifie un point de vue : system prompt, liste d’autorisation de cubes, liste d’autorisation de compétences, prompts suggérés — imposés côté serveur pour que, quoi que l’appelant envoie, le LLM voie la voix du persona
- les cubes du persona + les compétences du persona.
Persistés en JSON sous saiku-home/agent-spaces/. Le launcher scanne
paresseusement sur signature mtime (même modèle que les compétences — pas de
thread watcher).
Livré dans saiku v4.7 comme saiku#1440.
Ce qu’un espace impose
Format de fichier
{ "id": "foodmart-sales-analyst", "name": "FoodMart Sales Analyst", "description": "Weekly and monthly sales rollups over the FoodMart Sales cube.", "systemPrompt": "You are the FoodMart Sales Analyst. Prefer weekly and monthly time grain unless the user asks otherwise. Lead with the top three lines by absolute value. Flag any figure that swings by more than 20% versus the prior period. Be analytical, brief, and numbers-first.", "cubeAllowlist": [ {"connectionName": "unknown_foodmart", "catalog": "FoodMart", "schema": "FoodMart", "cubeName": "Sales"} ], "skillAllowlist": ["weekly-foodmart-rollup"], "suggestedPrompts": [ "How did Store Sales track last week vs the prior week?", "Break down Store Sales by Product Family for Q4.", "/weekly-foodmart-rollup" ]}| Champ | Requis | Type | Notes |
|---|---|---|---|
id | oui | string | kebab-case, [a-z][a-z0-9-]{0,63}. Segment de chemin dans /ai/spaces/{id}/ask. |
name | oui | string | Nom d’affichage pour le catalogue et la sidebar. |
description | non | string | Résumé d’une ligne affiché dans le sélecteur d’espace. |
systemPrompt | non | string | Préfixé au SYSTEM_PROMPT intégré à chaque question. |
cubeAllowlist | oui | AiCubeRef[] | Au moins une entrée. Les références en dehors de cette liste retournent 403 FORBIDDEN. |
skillAllowlist | non | string[] | Filtre le routage de slash + le catalogue du LLM. Vide = toutes les compétences autorisées. |
suggestedPrompts | non | string[] | Questions de démarrage rapide en forme libre que l’UI peut rendre. |
Les clés de premier niveau inconnues sont rejetées pour qu’une faute de
frappe (sytemPrompt) fasse surface sous forme d’UNKNOWN_FIELD plutôt que
d’être silencieusement abandonnée.
Application de la liste d’autorisation de cubes
-
L’appelant envoie un POST à
/ai/spaces/{id}/askavec un champcubeoptionnel :Fenêtre de terminal curl -sS -X POST -H 'Content-Type: application/json' \-u admin:admin \http://localhost:8080/saiku/api/ai/spaces/foodmart-sales-analyst/ask \-d '{"question": "How did Store Sales track last week?"}' -
Si
cubeest omis, la première entrée autorisée de l’espace est utilisée. -
Si
cubeest fourni, il doit correspondre à une entrée de la liste d’autorisation de l’espace sur les quatre coordonnées (connectionName,catalog,schema,cubeName), sinon l’appel retourne :HTTP 403 Forbidden{"degraded": true,"reason": "FORBIDDEN: cube OtherCube is not in space 'foodmart-sales-analyst' allowlist"}
C’est délibéré : une UI qui remet au serveur une référence de cube périmée devrait être corrigée, pas réinterprétée silencieusement vers le défaut.
Injection du system prompt
Le systemPrompt de l’espace est préfixé au SYSTEM_PROMPT intégré côté
fournisseur. Le message système complet assemblé que voit le LLM :
You are a Mondrian OLAP analyst assistant scoped to a single cube. …[…the built-in tool-choice rails…]
Agent space persona:You are the FoodMart Sales Analyst. Prefer weekly and monthly timegrain unless the user asks otherwise. Lead with the top three lines byabsolute value. Flag any figure that swings by more than 20% versus theprior period. Be analytical, brief, and numbers-first.
Cube schema:{ … the AiSchema JSON … }
Cube ref to echo: { … }Les champs history et question de l’utilisateur chevauchent tout cela
en dessous, comme des messages ordinaires — rien qu’ils puissent injecter ne
réécrit le persona.
Filtre de compétences
Le catalogue de compétences est filtré vers le
skillAllowlist de l’espace avant d’atteindre le LLM :
- Le
skillAllowlistest vide → toutes les compétences passent. - Le
skillAllowlistnomme des compétences spécifiques → seules celles-ci apparaissent dans le system prompt du LLM ET seules celles-ci s’expansent par slash.
Une question comme /some-other-skill for Q4 dans un espace qui n’autorise
pas some-other-skill retombe comme une question brute — le LLM voit le
message littéralement, sans expansion. La décision de routage est
unilatérale (déni par liste d’autorisation), jamais partielle.
Prompts suggérés
Chaque espace porte une liste suggestedPrompts — 3 à 6 questions de
démarrage rapide rédigées que l’UI fait apparaître sous forme de puces :
"suggestedPrompts": [ "How did Store Sales track last week vs the prior week?", "Break down Store Sales by Product Family for Q4.", "Which Product Department is up the most month-over-month?", "/weekly-foodmart-rollup"]Les entrées de slash-command sont légales et encouragées — un prompt
suggéré qui commence par / invoque directement la compétence nommée.
Surface REST
GET /rest/saiku/api/ai/spaces
Catalogue de résumés {id, name, description, suggestedPrompts}.
curl -sS -u admin:admin \ http://localhost:8080/saiku/api/ai/spaces | jq{ "spaces": [ { "id": "foodmart-sales-analyst", "name": "FoodMart Sales Analyst", "description": "Weekly and monthly sales rollups over the FoodMart Sales cube.", "suggestedPrompts": ["How did Store Sales track last week?", "…"] } ]}GET /rest/saiku/api/ai/spaces?errors=true
Idem, plus un tableau errors[]. Codes d’erreur stables :
| Code | Quand |
|---|---|
EMPTY_SPACE | Le fichier est vide ou entièrement composé d’espaces. |
MALFORMED_JSON | Le parseur JSON a rejeté le corps. |
MISSING_FIELD | Champ requis (id, name) non présent. |
BLANK_FIELD | Champ requis présent mais vide / composé uniquement d’espaces. |
TYPE_MISMATCH | Champ présent mais du mauvais type. |
INVALID_ID | id ne correspond pas à [a-z][a-z0-9-]{0,63}. |
EMPTY_ALLOWLIST | cubeAllowlist présent mais vide — l’espace serait inutilisable. |
INVALID_CUBE_REF | Entrée de la liste d’autorisation à laquelle manque une coordonnée (connectionName, etc.). |
UNKNOWN_FIELD | Le frontmatter contient un champ absent du schéma. |
DUPLICATE_ID | Deux fichiers ont déclaré le même id. |
IO_ERROR | Impossible de lire le fichier (niveau système de fichiers). |
GET /rest/saiku/api/ai/spaces/{id}
Enregistrement complet — inclut le systemPrompt et le cubeAllowlist que
le résumé omet. Utilisé par l’UI admin lors de l’édition d’un persona.
POST /rest/saiku/api/ai/spaces/{id}/ask
Question délimitée par un espace. La forme du corps reflète
/ai/ask mais le champ cube est optionnel :
curl -sS -X POST -H 'Content-Type: application/json' \ -u admin:admin \ http://localhost:8080/saiku/api/ai/spaces/foodmart-sales-analyst/ask \ -d '{"question": "Break down Store Sales by Product Family for Q4"}'L’enveloppe de réponse est le standard
AiAskApi.AskResponse — model, request (l’
AiQueryRequest que le modèle a émise) et degraded/reason en cas
d’erreur.
POST /rest/saiku/api/ai/spaces/{id}/ask/stream
Variante en streaming de la question délimitée par un espace. Émet le même
schéma de Server-Sent Events que
/ai/ask/stream
(model → intent → chunk → final) avec la portée du persona
appliquée — le client voit des événements de fil identiques qu’il frappe
/ai/ask/stream ou ce miroir délimité par un espace. Les cas d’espace
introuvable et de cube hors liste d’autorisation font surface sous forme
d’événement error suivi d’un final dégradé, pour que le lecteur gère les
échecs de portée et les échecs de fournisseur de la même façon.
curl -sS -N -X POST -H 'Content-Type: application/json' \ -u admin:admin \ http://localhost:8080/saiku/api/ai/spaces/foodmart-sales-analyst/ask/stream \ -d '{"question": "Break down Store Sales by Product Family for Q4"}'POST /rest/saiku/api/ai/spaces/refresh
Force un rescan (contourne la vérification de signature mtime).
Rédiger des espaces dans le panneau admin
Les espaces n’ont plus à être rédigés à la main en JSON. Admin → Agent spaces est un éditeur complet : écrivez le system prompt, cochez les cubes dans la liste d’autorisation (adossée à la découverte de cubes en direct, pour que vous ne puissiez autoriser que des cubes qui existent), listez les compétences et les prompts suggérés, et sauvegardez. Supprimer un espace retire son fichier JSON.
L’éditeur est adossé à une surface CRUD réservée à l’admin. Ces endpoints
nécessitent ROLE_ADMIN et retournent le persona complet (system prompt
- liste d’autorisation de cubes inclus, contrairement au catalogue public caviardé ci-dessus) :
| Méthode + chemin | Objectif |
|---|---|
GET /rest/saiku/admin/agent-spaces | Lister chaque persona en entier, pour l’édition. |
GET /rest/saiku/admin/agent-spaces/errors | Erreurs d’analyse pour les fichiers malformés. |
PUT /rest/saiku/admin/agent-spaces/{id} | Créer ou remplacer un persona. L’id est validé (kebab-case, protégé contre le path-traversal) avant que le fichier JSON ne soit écrit. |
DELETE /rest/saiku/admin/agent-spaces/{id} | Supprimer un persona et son fichier. |
Embarquer un espace
<saiku-embed kind="ai" space="foodmart-sales-analyst"> insère un assistant
délimité par un persona dans n’importe quelle page. La liste d’autorisation
de cubes et le system prompt sont imposés côté serveur exactement comme ils
le sont pour la question REST, pour qu’un assistant embarqué ne puisse pas
être détourné de son persona. Voir le guide d’embed.
Exemples fournis
Les installations fraîches de Saiku déposent deux personas fonctionnels :
- FoodMart Sales Analyst — analytique, concis, les chiffres d’abord.
weekly-foodmart-rollupdans sa liste d’autorisation de compétences pour que/weekly-foodmart-rollupsoit disponible comme slash command. - FoodMart Finance Ops — prudent, précis, axé sur la marge.
skillAllowlistvide = toutes les compétences autorisées.
Les deux se délimitent au cube FoodMart Sales — une démo fraîche a des personas prêts à cliquer sans aucune rédaction d’opérateur.
Non-objectifs pour la v1
- Portée par utilisateur ou par rôle. Les espaces sont par launcher en v1 ; les workspaces multi-tenant peuvent superposer une portée en mappant les répertoires de workspace sur des racines de registre par workspace — reporté.
- Surcharge de portée de données par espace. Les requêtes embarquées imposent déjà des filtres forcés au niveau ligne (appliquer-ou-échouer-en- fermé — voir le guide d’embed), mais épingler des filtres RLS spécifiquement sur les questions délimitées par un espace est encore suivi avec le travail Ossie RLS dans saiku#1393.