Pular para o conteúdo
astorlm
Idioma: Português
← Mapa

Nível 8

Skills sob demanda

Uma skill é um manual para um tipo de trabalho. O agente sempre vê a lista de manuais, e só lê um quando uma requisição pede.
1/16 Dobras do bandoneón:
  • user
  • assistant
  • tool_result
O assistente de encomendas de uma padaria. Três livros de receitas na prateleira: essas são as skills dele. Sobre o passe pendura uma comanda por livro, com o nome e uma linha sobre quando usá-lo. Só as comandas vão no system prompt.

EventBus

O problema

O assistente de encomendas de uma padaria precisa conhecer as regras da casa. Qual tamanho de bolo serve 20 pessoas. Que bolos sem castanhas só são assados na sexta de manhã. Quanto é o sinal. Como orçar uma bandeja de buffet. O que vai em cada produto que já está nas prateleiras. O modelo não sabe nada disso.

O conserto óbvio é escrever tudo e colar no system prompt. Funciona no primeiro dia. Depois os manuais crescem, e já são dez. Toda requisição agora carrega todos os manuais, a cada turno, quer o cliente queira um bolo de casamento, quer só o horário de funcionamento. Você paga por tudo isso, as requisições ficam mais lentas, e a única regra que importa fica enterrada entre as que não importam.

Esse é o Tomebloat, o system prompt inchado. Ele alimenta o Gulp, do nível 7: um prompt que já está cheio deixa menos espaço para a conversa em si.

A solução

Divida cada manual em dois. Uma descrição de uma ou duas linhas diz quando a skill se aplica. O corpo diz como fazer o trabalho. Só as descrições vão no system prompt, como um catálogo. Quando uma requisição combina com uma delas, o modelo pede o corpo, e o corpo entra na conversa dali em diante.

Isso é progressive disclosure (revelação progressiva): mostrar primeiro o índice, e o detalhe só quando for preciso. O formato usual é uma pasta por skill com um arquivo SKILL.md. O frontmatter no topo (o bloco entre as linhas ---) guarda o nome e a descrição. Tudo abaixo dele é o corpo.

---
name: custom-cake-order
description: Cakes made to order. Use when a customer wants a cake baked for them — size by guests, flavors, allergies, lead time, deposit.
---

# Custom cake orders

1. Size by guests: up to 12 → 18 cm, up to 24 → 24 cm, more → two tiers.
2. Flavors: chocolate, vanilla, dulce de leche. Nothing else.
3. Nut allergy → the nut-free line. It only bakes on Friday mornings.
4. Check the calendar before you promise a date. Never less than 72 hours.
5. Quote the 50% deposit, and ask before you book. Never book on your own.

Há três jeitos comuns de entregar skills ao modelo. O astorlm suporta os três com skillMode:

  • all

    O corpo completo de cada skill vai no system prompt, desde a primeira requisição.

    Sem chamadas extras, e o modelo não tem como pular um manual. Serve para duas ou três skills curtas de que quase toda requisição precisa. Passou disso, é o Tomebloat.

  • on-demand

    O system prompt lista o nome e a descrição de cada skill. Uma ferramenta load_skill devolve o corpo completo quando o modelo pede.

    Escala para dezenas de skills. Custa uma chamada de ferramenta por skill carregada, e modelos pequenos às vezes respondem sem carregar a skill de que precisavam.

  • filesystem

    O catálogo também informa o caminho de cada SKILL.md, e o modelo o abre com a ferramenta comum de leitura de arquivos.

    A mesma ideia sem ferramenta especial. É assim que o Claude Code, o Codex e o Gemini CLI fazem, então a mesma pasta de skills funciona em todos eles. Precisa de um agente que consiga ler arquivos.

Uma skill não é uma ferramenta. Uma ferramenta é algo que o loop executa para o modelo. Uma skill é algo que o modelo lê, e ela pode dizer ao modelo quais ferramentas chamar e em que ordem, como a receita que diz “confira o calendário antes de prometer uma data”.

O elenco

O mesmo elenco de sempre, desta vez na cozinha de uma padaria.

O Oráculo o modelo
O chef atrás do passe. Lê o que estiver na frente dele, a cada turno, e não cozinha nada sozinho.
As comandas o catálogo
Uma por skill, no trilho sobre o passe: um nome e uma linha sobre quando usá-la. Fazem parte do system prompt, então vão em toda requisição.
A prateleira os corpos das skills
Um livro de receitas por skill, fechado. Quanto mais grosso o livro, mais tokens ele pesa. Nada disso chega ao modelo até alguém ir buscar.
O livro aberto uma skill carregada
load_skill devolve o corpo como resultado de ferramenta (o marcador verde). Ele entra no bandoneón como qualquer resultado, então fica aberto sobre o passe pelo resto da execução.
O forno check_calendar
Uma ferramenta comum. É a receita que diz ao modelo para usá-la.
A barra tamanho da requisição
Quantos tokens a próxima requisição carrega. O comprimento total dela é o que carregaria com os três livros colados no system prompt.

No painel EventBus, load_skill aparece como um tool_execution_start e um tool_execution_end comuns. O loop não faz ideia de que skills existem: para ele, carregar um manual é só mais uma chamada de ferramenta.

O código

Com astorlm: Aponte skillSources para uma pasta de skills. O padrão skillMode: 'on-demand' coloca o catálogo no system prompt e adiciona a ferramenta load_skill para você.

Do zero: Leia cada SKILL.md, coloque uma linha por skill no system prompt e adicione uma ferramenta load_skill que devolva um corpo. O loop do nível 2 não muda nada.

import { OpenAIProvider, createFileSystemSkillSource, createLocalAgent, tool } from 'astorlm'
import { z } from 'zod'

const checkCalendar = tool({
  name: 'check_calendar',
  description: 'Free baking slots and pickup times for a day, per production line.',
  schema: z.object({ day: z.string(), line: z.enum(['regular', 'nut-free']) }),
  execute: async ({ day, line }) => bakerySlots(day, line), // your code
})

const agent = await createLocalAgent({
  // Any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, vLLM, a proxy…
  provider: new OpenAIProvider({
    baseURL: 'http://localhost:11434/v1', // e.g. Ollama's default address
    model: 'your-model', // e.g. 'llama3.1', 'gpt-4o-mini'
    apiKey: 'YOUR_API_KEY', // local servers usually ignore it
  }),
  tools: [checkCalendar],
  // Every folder in ./skills with a SKILL.md is one skill:
  //   skills/custom-cake-order/SKILL.md, skills/catering-quote/SKILL.md, …
  skillSources: [createFileSystemSkillSource({ dir: './skills' })],
  // The default. Only each skill's name and description go in the system prompt,
  // and the agent gets a load_skill tool to fetch a full body when it needs one.
  skillMode: 'on-demand',
  maxTurns: 10,
})

agent.on('tool-end', ({ name, output }) => {
  if (name === 'load_skill') console.log('loaded:', output.slice(0, 60))
})

const last = await agent.run('I need a cake for 20 people this Saturday. Chocolate, and one guest can’t have nuts.')
console.log(last.content)

O que observar

  • A descrição é o gatilho. O modelo escolhe uma skill só pela descrição, do mesmo jeito que escolhe uma ferramenta (nível 3). Diga quando usá-la, não o que ela contém: “Use quando um cliente quer um bolo feito por encomenda” é melhor que “Informações sobre bolos”. Na animação, allergen-info também fala de alergias; só as descrições impedem o modelo de carregar o livro errado.
  • Modelos pequenos pulam a etapa. Alguns respondem direto sem carregar a skill de que precisavam. Diga isso com todas as letras no system prompt (“carregue a skill correspondente antes de responder”), teste o modo filesystem ou, para uma skill de que toda requisição precisa, mantenha-a em all.
  • Uma skill carregada continua carregada. O corpo dela está no histórico, então cada turno seguinte paga por ele, e a compactação (nível 7) pode truncá-lo depois. Mantenha os corpos curtos e divida skills grandes em duas.
  • Skills são instruções, então são código. Quem escreve um SKILL.md dirige o seu agente. Não carregue skills de pastas ou registros que você não controla (nível 14).