Agent Spaces
Agent Spaces son personas nombradas y escritas por admins que limitan una ask de IA. Donde las Agent Skills codifican flujos de trabajo individuales, un espacio codifica un punto de vista: system prompt, lista de cubos permitidos, lista de skills permitidas, prompts sugeridos — aplicado en el servidor para que, sin importar lo que envíe el llamador, el LLM vea la voz de la persona + los cubos de la persona + las skills de la persona.
Persistido como JSON bajo saiku-home/agent-spaces/. El launcher
escanea de forma perezosa por firma de mtime (mismo modelo que las skills
— sin hilo vigilante).
Entregado en saiku v4.7 como saiku#1440.
Qué aplica un espacio
Formato de archivo
{ "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" ]}| Campo | Requerido | Tipo | Notas |
|---|---|---|---|
id | sí | string | kebab-case, [a-z][a-z0-9-]{0,63}. Segmento de ruta en /ai/spaces/{id}/ask. |
name | sí | string | Nombre para mostrar en el catálogo y la barra lateral. |
description | no | string | Resumen de una línea mostrado en el selector de espacios. |
systemPrompt | no | string | Antepuesto al SYSTEM_PROMPT incorporado en cada ask. |
cubeAllowlist | sí | AiCubeRef[] | Al menos una entrada. Las refs fuera de esta lista devuelven 403 FORBIDDEN. |
skillAllowlist | no | string[] | Filtra el slash-routing + catálogo del LLM. Vacío = todas las skills permitidas. |
suggestedPrompts | no | string[] | Preguntas de inicio rápido de forma libre que la UI puede renderizar. |
Las claves de nivel superior desconocidas son rechazadas para que un
error tipográfico (sytemPrompt) aflore como UNKNOWN_FIELD en lugar de
descartarse silenciosamente.
Aplicación de cubos
-
El llamador hace POST a
/ai/spaces/{id}/askcon un campo opcionalcube:Ventana 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
cubese omite, se usa la primera entrada permitida del espacio. -
Si
cubese suministra, debe coincidir con una entrada de la lista permitida del espacio en las cuatro coordenadas (connectionName,catalog,schema,cubeName) o la llamada devuelve:HTTP 403 Forbidden{"degraded": true,"reason": "FORBIDDEN: cube OtherCube is not in space 'foodmart-sales-analyst' allowlist"}
Esto es deliberado: una UI que le entrega al servidor una ref de cubo obsoleta debería ser corregida, no reinterpretada silenciosamente hacia la por defecto.
Inyección de system prompt
El systemPrompt del espacio se antepone al SYSTEM_PROMPT incorporado
en el lado del proveedor. El mensaje de sistema completo ensamblado que
el LLM ve:
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: { … }Los campos history y question del usuario cabalgan por debajo de todo
eso como mensajes regulares — nada que puedan inyectar reescribe la
persona.
Filtro de skills
El catálogo de skills se filtra a la skillAllowlist del
espacio antes de que llegue al LLM:
- La
skillAllowlistestá vacía → todas las skills fluyen. - La
skillAllowlistnombra skills específicas → solo esas aparecen en el system prompt del LLM Y solo esas se expanden con slash.
Una ask como /some-other-skill for Q4 en un espacio que no permite
some-other-skill cae como una ask cruda — el LLM ve el mensaje
literalmente, sin expansión. La decisión de enrutamiento es unilateral
(denegación por lista permitida), nunca parcial.
Prompts sugeridos
Cada espacio lleva una lista suggestedPrompts — 3-6 preguntas de inicio
rápido escritas que la UI expone como chips:
"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"]Las entradas de slash-command son legales y fomentadas — un prompt
sugerido que empieza con / invoca la skill nombrada directamente.
Superficie REST
GET /rest/saiku/api/ai/spaces
Catálogo de resúmenes {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
Lo mismo, más un array errors[]. Códigos de error estables:
| Código | Cuándo |
|---|---|
EMPTY_SPACE | El archivo está vacío o es todo espacios en blanco. |
MALFORMED_JSON | El parser JSON rechazó el cuerpo. |
MISSING_FIELD | Campo requerido (id, name) no presente. |
BLANK_FIELD | Campo requerido presente pero vacío / solo espacios en blanco. |
TYPE_MISMATCH | Campo presente pero de tipo incorrecto. |
INVALID_ID | id no coincide con [a-z][a-z0-9-]{0,63}. |
EMPTY_ALLOWLIST | cubeAllowlist presente pero vacía — el espacio sería inutilizable. |
INVALID_CUBE_REF | A una entrada de la lista permitida le falta una coordenada (connectionName, etc.). |
UNKNOWN_FIELD | El frontmatter contiene un campo que no está en el schema. |
DUPLICATE_ID | Dos archivos declararon el mismo id. |
IO_ERROR | No se pudo leer el archivo (a nivel de sistema de archivos). |
GET /rest/saiku/api/ai/spaces/{id}
Registro completo — incluye systemPrompt y cubeAllowlist que el
resumen omite. Usado por la UI de admin al editar una persona.
POST /rest/saiku/api/ai/spaces/{id}/ask
Ask limitada al espacio. La forma del cuerpo refleja
/ai/ask pero el campo cube es opcional:
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"}'El sobre de respuesta es el estándar AiAskApi.AskResponse
— model, request (el AiQueryRequest que el modelo emitió), y
degraded/reason en caso de error.
POST /rest/saiku/api/ai/spaces/{id}/ask/stream
Variante de streaming de la ask limitada al espacio. Emite el mismo
schema de Server-Sent Events que /ai/ask/stream
(model → intent → chunk → final) con el alcance de persona
aplicado — el cliente ve eventos de cable idénticos tanto si golpea
/ai/ask/stream como este espejo limitado al espacio. Los desenlaces de
espacio-no-encontrado y de cubo fuera de la lista permitida afloran como
un evento error seguido de un final degradado, así que el lector
maneja los fallos de alcance y los fallos de proveedor de la misma forma.
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
Fuerza un reescaneo (se salta la comprobación de firma de mtime).
Escribir espacios en el panel de admin
Los espacios ya no tienen que ser JSON escrito a mano. Admin → Agent spaces es un editor completo: escriba el system prompt, marque los cubos en la lista permitida (respaldado por descubrimiento de cubos en vivo, así que solo puede permitir cubos que existen), liste las skills y los prompts sugeridos, y guarde. Eliminar un espacio quita su archivo JSON.
El editor está respaldado por una superficie CRUD limitada a admin. Estos
endpoints requieren ROLE_ADMIN y devuelven la persona completa
(system prompt + lista de cubos permitidos incluidos, a diferencia del
catálogo público redactado de arriba):
| Método + ruta | Propósito |
|---|---|
GET /rest/saiku/admin/agent-spaces | Lista cada persona en su totalidad, para editar. |
GET /rest/saiku/admin/agent-spaces/errors | Errores de parseo para archivos malformados. |
PUT /rest/saiku/admin/agent-spaces/{id} | Crea o reemplaza una persona. El id se valida (kebab-case, protegido contra path-traversal) antes de escribir el archivo JSON. |
DELETE /rest/saiku/admin/agent-spaces/{id} | Elimina una persona y su archivo. |
Incrustar un espacio
<saiku-embed kind="ai" space="foodmart-sales-analyst"> coloca un
asistente limitado a la persona en cualquier página. La lista de cubos
permitidos y el system prompt se aplican en el servidor exactamente como
para la ask REST, así que un asistente incrustado no puede ser desviado
de su persona. Vea la guía de embed.
Ejemplos incluidos
Las instalaciones nuevas de Saiku preparan dos personas funcionales:
- FoodMart Sales Analyst — analítica, breve, orientada a números primero.
weekly-foodmart-rollupen su lista de skills permitidas así que/weekly-foodmart-rollupestá disponible como slash command. - FoodMart Finance Ops — cautelosa, precisa, orientada al margen.
skillAllowlistvacía = todas las skills permitidas.
Ambas limitan al cubo FoodMart Sales — una demo nueva tiene personas listas para pulsar sin ninguna escritura por parte del operador.
No-objetivos para v1
- Alcance por usuario o por rol. Los espacios son por launcher en v1; los workspaces multi-tenant pueden superponer el alcance mapeando directorios de workspace sobre raíces de registro por workspace — diferido.
- Sobrescritura de alcance de datos por espacio. Las consultas incrustadas ya aplican filtros forzados a nivel de fila (aplicar-o-fallar-cerrado — vea la guía de embed), pero fijar filtros RLS sobre asks limitadas a espacio específicamente sigue siendo rastreado con el trabajo de RLS de Ossie en saiku#1393.