Agent Skills
Agent Skills son archivos markdown con frontmatter YAML que viven en
saiku-home/skills/. El launcher los escanea de forma perezosa en cada
petición e inyecta el catálogo en el system prompt del LLM, de modo que
cualquier turno de AI Ask — MDX u Ossie
— puede enrutar a un flujo de trabajo escrito por un admin en lugar de ir
por libre.
Funcionalidad pequeña, valor desproporcionado: permite a los operadores codificar las preguntas canónicas de su equipo (“el rollup ejecutivo de este trimestre”, “la cohorte de churn”, “el informe de comparación de tiendas”) sin ser dueños de otra herramienta más. La skill vive en el repo, se versiona con el modelo semántico, y recibe revisión de código como todo lo demás.
Entregado en saiku v4.7 como saiku#1426.
Dos rutas de invocación
Formato de archivo
---name: weekly-foodmart-rollupdescription: | Weekly revenue rollup for the FoodMart Sales cube: total Store Sales by Product Family for the last 7 days, compared to the prior 7 days. Flags any family with a >20% swing.cube: unknown_foodmart/FoodMart/FoodMart/Sales---
## Steps
1. Query total `[Measures].[Store Sales]` and `[Measures].[Unit Sales]` broken down by `[Product].[Product Family]` for the last 7 days (`[Time].[Weekly].[Week].&[latest]`).
2. Query the same shape for the prior 7 days (`[Time].[Weekly].[Week].&[latest - 1]`).
3. Present the result as a two-column table with a `Δ vs prior` percentage column derived per family.
4. Highlight any Product Family whose `Δ vs prior` swings by more than 20% in either direction.Frontmatter
| Campo | Requerido | Tipo | Notas |
|---|---|---|---|
name | sí | string | kebab-case, [a-z][a-z0-9-]{0,63}. Usado como el slug del slash. |
description | sí | string | Una línea o escalar de bloque. Mostrado en /ai/skills y el prompt del LLM. |
cube | no | string | Ref connection/catalog/schema/cubeName. Limita la skill. |
Las claves de nivel superior desconocidas son rechazadas. Un error
tipográfico (descripton) aflora como un error estructurado
UNKNOWN_FIELD — nunca como una skill silenciosa sin nombre.
Cuerpo
Todo lo que sigue a la valla --- de cierre es el cuerpo. Se recomienda
markdown pero no es obligatorio; el cuerpo se trata como texto opaco y se
pega literalmente en el prompt del LLM ante una coincidencia de
slash-command. Manténgalo ceñido — el LLM tiene que leer el archivo
entero.
Slash command
-
El usuario (o el widget DimSum en su nombre) publica una ask que empieza con
/:Ventana de terminal curl -sS -X POST -H 'Content-Type: application/json' \-u admin:admin \http://localhost:8080/saiku/api/ai/ask \-d @- <<'EOF'{"question": "/weekly-foodmart-rollup for Q4 instead of this week","cube": {"connectionName":"unknown_foodmart","catalog":"FoodMart","schema":"FoodMart","cubeName":"Sales"}}EOF -
El servicio parsea el slash y busca
weekly-foodmart-rollupen el catálogo. -
Coincidencia → la ask enviada al LLM es:
Skill: weekly-foodmart-rollup## Steps1. Query total [Measures].[Store Sales] ……User follow-up: for Q4 instead of this week -
El LLM ejecuta los pasos, aplica el seguimiento (“Q4 instead of this week”) al filtro de tiempo, y emite un
AiQueryRequestcomo de costumbre. -
Fallo → la ask viaja sin cambios. El LLM la ve como un prompt ordinario con un slash inicial. Nada se rompe.
Lenguaje natural
El catálogo aterriza en el system prompt del LLM como:
Available skills (admin-authored workflows). When the user's questionclosely matches one of these — or when the message starts with`/<skill-name>` — use the skill's steps to structure your responseinstead of freewheeling:- /weekly-foodmart-rollup: Weekly revenue rollup for the FoodMart Sales cube…- /store-comp-report: Store-level comp vs same period last year…- /churn-cohort: 30/60/90 day churn cohort split by acquisition channel…El modelo enruta por sí mismo. Si el usuario pregunta “how did stores
compare to last year?”, el emparejador de lenguaje natural elige
/store-comp-report; el usuario nunca ve la decisión de enrutamiento.
Superficie REST
Los tres endpoints se sitúan bajo la ruta base estándar de AI Ask.
GET /rest/saiku/api/ai/skills
El catálogo — resúmenes compactos {name, description, cube}.
curl -sS -u admin:admin \ http://localhost:8080/saiku/api/ai/skills | jq{ "skills": [ { "name": "weekly-foodmart-rollup", "description": "Weekly revenue rollup for the FoodMart Sales cube: total Store Sales …", "cube": "unknown_foodmart/FoodMart/FoodMart/Sales" } ]}GET /rest/saiku/api/ai/skills?errors=true
La misma forma, más un array errors[] listando cada archivo que falló
al parsear en este escaneo. Cada entrada lleva un código de máquina
estable para que los operadores arreglen frontmatter malo sin leer los
logs del servidor.
{ "skills": [ /* … */ ], "errors": [ { "path": "broken.md", "code": "MISSING_FRONTMATTER", "message": "expected leading `---` YAML frontmatter block" } ]}Códigos de error estables:
| Código | Cuándo |
|---|---|
EMPTY_SKILL | El archivo está vacío o es todo espacios en blanco. |
MISSING_FRONTMATTER | No hay valla --- inicial. |
EMPTY_BODY | El frontmatter parseó pero no hay markdown tras la valla de cierre. |
MALFORMED_YAML | El parser YAML rechazó el frontmatter. |
MISSING_FIELD | Campo requerido (name / description) no presente. |
BLANK_FIELD | Campo requerido presente pero vacío / solo espacios en blanco. |
TYPE_MISMATCH | Campo presente pero de tipo incorrecto (name: 42). |
INVALID_NAME | name no coincide con [a-z][a-z0-9-]{0,63}. |
UNKNOWN_FIELD | El frontmatter contiene un campo que no está en el schema. |
DUPLICATE_NAME | Dos archivos de skill declararon el mismo name. |
IO_ERROR | No se pudo leer el archivo (a nivel de sistema de archivos). |
GET /rest/saiku/api/ai/skills/{name}
El cuerpo completo de una skill — el markdown crudo. Útil para un slash-menu de UI que previsualiza el flujo de trabajo antes de que el usuario le dé a enviar.
POST /rest/saiku/api/ai/skills/refresh
Fuerza un reescaneo (se salta la comprobación de firma de mtime). Devuelve los recuentos frescos para que los operadores puedan comprobar la recarga a ojo.
curl -sS -u admin:admin -X POST \ http://localhost:8080/saiku/api/ai/skills/refresh{ "skills": 4, "errors": 0 }Ejemplo incluido
Las instalaciones nuevas de Saiku preparan un ejemplo funcional en el primer arranque — vea weekly-foodmart-rollup.md. Los operadores añaden las suyas al lado; la sembrada es idempotente (solo aterriza cuando el archivo destino no existe ya).
Cómo funciona el escaneo
- Los subdirectorios se recorren recursivamente.
- Los archivos que no son
.mdse ignoran (unREADME.txtperdido no hará caer el escaneo). - Los archivos rotos no derriban el catálogo — una
ParseExceptionen un archivo deja los demás intactos. - Los duplicados en
nameproducen un errorDUPLICATE_NAMEen el segundo archivo en cargarse; el primero gana.
Las asks de Ossie también las recogen
El catálogo de skills es un único almacén por launcher servido en
/rest/saiku/api/ai/skills. Cualquier skill cuyo campo cube: nombre
una ref Ossie (p. ej. pharma/Pharma/Pharma/Sales) queda naturalmente
limitada por esa ref cuando se enruta a través de
/ai/ossie/ask. MDX y Ossie
comparten la misma primitiva.