Pular para o conteúdo

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

saiku-home/skills/weekly-foodmart-rollup.md
---
name: weekly-foodmart-rollup
description: |
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

CampoObrigatórioTipoNotas
namesimstringkebab-case, [a-z][a-z0-9-]{0,63}. Usado como o slug da slash.
descriptionsimstringUma linha ou block scalar. Mostrado em /ai/skills e no prompt do LLM.
cubenãostringRef 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

  1. 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
  2. O serviço parseia a slash e busca weekly-foodmart-rollup no catálogo.

  3. Correspondência → o ask enviado ao LLM é:

    Skill: weekly-foodmart-rollup
    ## Steps
    1. Query total [Measures].[Store Sales] …
    User follow-up: for Q4 instead of this week
  4. O LLM roda os passos, aplica o follow-up (“Q4 instead of this week”) ao filtro de tempo e emite um AiQueryRequest como de costume.

  5. 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 question
closely matches one of these — or when the message starts with
`/<skill-name>` — use the skill's steps to structure your response
instead 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}.

Terminal window
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ódigoQuando
EMPTY_SKILLO arquivo está vazio ou só whitespace.
MISSING_FRONTMATTERSem cerca inicial ---.
EMPTY_BODYFrontmatter parseou mas sem markdown após a cerca de fechamento.
MALFORMED_YAMLO parser YAML rejeitou o frontmatter.
MISSING_FIELDCampo obrigatório (name / description) não presente.
BLANK_FIELDCampo obrigatório presente mas vazio / só-whitespace.
TYPE_MISMATCHCampo presente mas tipo errado (name: 42).
INVALID_NAMEname não casa com [a-z][a-z0-9-]{0,63}.
UNKNOWN_FIELDFrontmatter contém um campo que não está no schema.
DUPLICATE_NAMEDois arquivos de skill declararam o mesmo name.
IO_ERRORNã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.

Terminal window
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-.md são ignorados (um README.txt perdido não vai travar o scan).
  • Arquivos quebrados não derrubam o catálogo — uma ParseException em um arquivo deixa os outros intactos.
  • Duplicatas em name produzem um erro DUPLICATE_NAME no 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.

Para onde ir a seguir