Agent Skills
Agent Skills são arquivos markdown com frontmatter YAML que vivem
em saiku-home/skills/. O launcher os escaneia preguiçosamente em cada
requisição e injeta o catálogo no system prompt do LLM, para que
qualquer turno de AI Ask — MDX ou
Ossie — possa rotear para um workflow autorado por
admin em vez de improvisar.
Recurso pequeno, valor desproporcional: permite que operadores codifiquem as perguntas canônicas do seu time (“o rollup executivo deste trimestre”, “o cohort de churn”, “o relatório de comp de lojas”) sem ter que manter mais uma ferramenta. A skill vive no repo, versiona com o modelo semântico e recebe code review como tudo o mais.
Distribuído no saiku v4.7 como saiku#1426.
Dois caminhos de invocação
Formato de arquivo
---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 | Obrigatório | Tipo | Notas |
|---|---|---|---|
name | sim | string | kebab-case, [a-z][a-z0-9-]{0,63}. Usado como o slug da slash. |
description | sim | string | Uma linha ou block scalar. Mostrado em /ai/skills e no prompt do LLM. |
cube | não | string | Ref connection/catalog/schema/cubeName. Escopa a skill. |
Chaves top-level desconhecidas são rejeitadas. Um typo
(descripton) aparece como um erro estruturado UNKNOWN_FIELD — nunca
como uma skill silenciosa sem nome.
Corpo
Tudo após a cerca de fechamento --- é o corpo. Markdown é recomendado
mas não obrigatório; o corpo é tratado como texto opaco e colado
verbatim no prompt do LLM em uma correspondência de slash-command.
Mantenha-o enxuto — o LLM tem que ler o arquivo inteiro.
Slash command
-
O usuário (ou o widget DimSum em seu nome) posta um ask começando com
/:Terminal window 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 -
O serviço parseia a slash e busca
weekly-foodmart-rollupno catálogo. -
Correspondência → o ask enviado ao LLM é:
Skill: weekly-foodmart-rollup## Steps1. Query total [Measures].[Store Sales] ……User follow-up: for Q4 instead of this week -
O LLM roda os passos, aplica o follow-up (“Q4 instead of this week”) ao filtro de tempo e emite um
AiQueryRequestcomo de costume. -
Sem correspondência → o ask viaja inalterado. O LLM o vê como um prompt comum com uma slash inicial. Nada quebra.
Linguagem natural
O catálogo cai no system prompt do 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…O modelo roteia por conta própria. Se o usuário pergunta “how did
stores compare to last year?”, o matcher de linguagem natural escolhe
/store-comp-report; o usuário nunca vê a decisão de roteamento.
Superfície REST
Todos os três endpoints ficam sob o path base padrão do AI Ask.
GET /rest/saiku/api/ai/skills
O catálogo — resumos 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
Mesmo formato, mais um array errors[] listando cada arquivo que
falhou ao parsear neste scan. Cada entrada carrega um código de máquina
estável para que operadores consertem frontmatter ruim sem ler logs de
servidor.
{ "skills": [ /* … */ ], "errors": [ { "path": "broken.md", "code": "MISSING_FRONTMATTER", "message": "expected leading `---` YAML frontmatter block" } ]}Códigos de erro estáveis:
| Código | Quando |
|---|---|
EMPTY_SKILL | O arquivo está vazio ou só whitespace. |
MISSING_FRONTMATTER | Sem cerca inicial ---. |
EMPTY_BODY | Frontmatter parseou mas sem markdown após a cerca de fechamento. |
MALFORMED_YAML | O parser YAML rejeitou o frontmatter. |
MISSING_FIELD | Campo obrigatório (name / description) não presente. |
BLANK_FIELD | Campo obrigatório presente mas vazio / só-whitespace. |
TYPE_MISMATCH | Campo presente mas tipo errado (name: 42). |
INVALID_NAME | name não casa com [a-z][a-z0-9-]{0,63}. |
UNKNOWN_FIELD | Frontmatter contém um campo que não está no schema. |
DUPLICATE_NAME | Dois arquivos de skill declararam o mesmo name. |
IO_ERROR | Não foi possível ler o arquivo (nível de filesystem). |
GET /rest/saiku/api/ai/skills/{name}
Corpo completo de uma skill — o markdown cru. Prático para um slash-menu de UI que preview o workflow antes que o usuário aperte enviar.
POST /rest/saiku/api/ai/skills/refresh
Força um rescan (ignora a checagem de assinatura de mtime). Retorna as contagens frescas para que operadores possam conferir o reload.
curl -sS -u admin:admin -X POST \ http://localhost:8080/saiku/api/ai/skills/refresh{ "skills": 4, "errors": 0 }Exemplo incluído
Instalações Saiku novas preparam um exemplo funcional no primeiro boot — veja weekly-foodmart-rollup.md. Operadores adicionam os seus ao lado; o exemplo semeado é idempotente (só cai quando o arquivo alvo ainda não existe).
Como o scan funciona
- Subdiretórios são caminhados recursivamente.
- Arquivos não-
.mdsão ignorados (umREADME.txtperdido não vai travar o scan). - Arquivos quebrados não derrubam o catálogo — uma
ParseExceptionem um arquivo deixa os outros intactos. - Duplicatas em
nameproduzem um erroDUPLICATE_NAMEno segundo arquivo a carregar; o primeiro vence.
Asks Ossie também os pegam
O catálogo de skills é um único store por-launcher servido em
/rest/saiku/api/ai/skills. Qualquer skill cujo field cube: nomeia
uma ref Ossie (ex.: pharma/Pharma/Pharma/Sales) é naturalmente
escopada por essa ref quando roteada através de
/ai/ossie/ask. MDX e Ossie
compartilham a mesma primitiva.