Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Criando um agente personalizado

Este capítulo percorre a adição de um novo agente a um deploy, do primeiro diretório ao primeiro chat. Ele é escrito como um exemplo trabalhado: um agente chamado scribe, rodando o harness ganglion. Leia o guia do administrador primeiro, se você ainda não conheceu tenants, subscriptions e escopos.

O que é um agente

Um agente é uma personalidade com nome com quem os membros conversam. alpha e beta são os dois que vêm de fábrica. Um agente não é um container: cada membro que fala com o scribe ganha o próprio container, o próprio workspace e o próprio histórico, todos clonados do mesmo ponto de partida. Veja agentes e workspaces para esse modelo de isolamento.

Três coisas precisam existir antes que um membro consiga alcançar um novo agente.

  1. Uma entrada no catálogo de agentes do proxy, que nomeia o agente, o runtime que ele usa, o modo de ciclo de vida dele e o modelo padrão dele.
  2. Um diretório de template no disco, que fornece os arquivos de identidade do agente.
  3. Uma rota no gateway, porque o Mycelium é a porta de entrada e ele não encaminha uma requisição para um serviço de que nunca ouviu falar.

O resto deste capítulo são essas três coisas, nessa ordem, mais as variáveis de ambiente que as amarram.

Passo 1: o diretório de template

O proxy resolve o template de um agente em <data-root>/templates/<template>/ (TemplatesDir em crab/crab-shell-proxy/internal/config/config.go). No host, <data-root> é CRAB_HOST_DATA_ROOT; dentro do container do proxy a mesma árvore é montada em CRAB_CONTAINER_DATA_ROOT, que por padrão é /data.

Para um agente ganglion, a parte do template que importa é workspace/, porque esse diretório é a camada de baixo da cascata de persona:

data/templates/scribe/
└── workspace/
    ├── AGENT.md       what the agent does and how it behaves
    ├── SOUL.md        its voice
    ├── HEARTBEAT.md   its recurring task list
    └── USER.md        what the agent starts out knowing about the member

Esses quatro nomes são o conjunto completo. PersonaFiles em crab/crab-shell-proxy/internal/docker/persona.go lista exatamente AGENT.md, SOUL.md, HEARTBEAT.md e USER.md, e os três primeiros são entregues como bind mounts somente leitura, enquanto o USER.md é semeado uma vez e depois deixado em paz — o agente escreve nele conforme aprende sobre o membro.

Um agente ganglion não tem config.json nem .security.yml no template dele. Esses são arquivos do picoclaw. O ganglion_config.go diz isso com todas as letras: a configuração do harness não é semeada a partir de templates/<agent>/config.json e nunca foi. Em vez disso, o proxy renderiza um arquivo de configuração para cada workspace, e as credenciais chegam como variáveis de ambiente. Não copie o config.json de um agente de fábrica para um template ganglion; nada vai lê-lo.

O workspace/skills/ e o workspace/memory/ do template são, do mesmo jeito, só do picoclaw. O seedWorkspace em crab/crab-shell-proxy/internal/docker/provision.go copia a allowlist config.WorkspaceSeedUSER.md, memory/ e skills/ — e ele está no caminho de criação do picoclaw; o createGanglion nunca o chama. Para dar skills a um agente ganglion, publique-as como skills compartilhadas pela área administrativa.

Um valor template: continua obrigatório para todo agente, seja qual for o harness: o validate no config.go recusa um agente que não declare nenhum. Para um agente ganglion ele aponta para o diretório que guarda aqueles arquivos de identidade.

O catálogo é o crab/crab-shell-proxy/config.yaml. O Dockerfile o copia para /etc/crab-shell-proxy/config.yaml e aponta CRAB_CONFIG para esse caminho, então o arquivo commitado é assado dentro da imagem do proxy. Um deploy que monte um arquivo próprio sobre esse caminho, ou aponte CRAB_CONFIG para outro lugar, pode editar o catálogo sem rebuild.

Adicione o agente sob agents::

agents:
  scribe:
    harness: "ganglion"
    serviceName: "scribe"
    token: { env: "MYC_PICOCLAW_SCRIBE_TOKEN" }
    template: "scribe"
    mode: "scale-to-zero"
    idleTimeout: 30s
    model:
      provider: "deepseek"
      name: "deepseek-chat"
      apiKeyEnv: "SCRIBE_API_KEY"

serviceName precisa bater com o valor que o Mycelium injeta como x-mycelium-service-name, que é a chave de serviço do gateway, do passo 3. token é o bearer que o gateway apresenta; o proxy rejeita qualquer requisição cujo Authorization não bata com ele.

Declare harness: "ganglion" explicitamente. A chave é opcional e o DefaultHarness hoje é o ganglion (config.go), então omiti-la funcionaria — mas o mesmo arquivo argumenta contra confiar nisso: todo agente no config.yaml deste repositório escreve o harness dele por extenso, porque uma atualização de config não deveria mudar o runtime de um agente por omissão. O picoclaw continua plenamente atendido, e harness: "picoclaw" continua sendo o valor certo para um agente que precisa dele; veja harnesses para a diferença.

mode decide o ciclo de vida do container. scale-to-zero para o container depois de idleTimeout sem atividade; continuous o mantém rodando. idleTimeout precisa ser maior que zero quando o modo é scale-to-zero, e o validate recusa o agente caso contrário. Um agente ganglion é um bom candidato a scale-to-zero: ele escreve cada turno na transcrição antes de o modelo ser chamado e reconstrói a partir dessa transcrição uma janela de contexto que esteja faltando, então um container parado não perde nada.

O bloco model: é o piso do agente — o modelo em que um workspace roda quando o inventário de modelos não resolve nada para ele. Você também pode listar alternativas sob models:, que vira a allowlist selecionável; SelectableModels no config.go devolve o padrão seguido dessa lista, sem duplicatas de provedor e nome.

Passo 3: a imagem, e o que acontece sem ela

Um agente ganglion precisa de CRAB_GANGLION_IMAGE. Ela não tem padrão, de propósito: o comentário sobre GanglionImage no config.go registra que uma tag móvel uma vez deixou um deploy rodando por semanas um binário de três semanas atrás, porque a imagem do harness não é um serviço do compose e um redeploy nunca a puxa. Aponte-a para um digest ou uma tag por commit.

Quando ela não está definida, o agente não derruba o proxy. O ganglionUnprovisioned remove o agente do catálogo na carga, registra isso em DisabledAgents com o nome da configuração que falta, e as rotas do agente então respondem 404. O mesmo acontece quando o apiKeyEnv do agente resolve para um valor vazio, ou quando a variável de ambiente do token dele não está definida. Leia o log de boot do proxy se um agente novo parecer não existir: o motivo está lá, e ele nomeia a variável.

O docker-compose.yaml usa por padrão zombie-crab/crab-ganglion:dev para CRAB_GANGLION_IMAGE, que é uma tag construída localmente. O docker-compose.prod.yaml não define imagem ganglion nenhuma e nenhum workflow em .github/workflows/ publica uma, então um deploy de produção tem de construir e publicar a imagem do harness por conta própria antes que um agente ganglion possa iniciar.

Passo 4: a rota do gateway

O Mycelium roteia pelo primeiro segmento do caminho, e esse segmento é a chave de serviço literal. Copie o bloco [[alpha]] em deploy/standalone/config.standalone.toml — o bloco de serviço, o [[alpha.secret]] dele e cada [[alpha.path]] — e renomeie para scribe. Quem chama então alcança o agente em /scribe/.... Mantenha host, healthCheckPath, o conjunto completo de paths e os grupos protectedByRoles como estão; só o nome muda.

Faça isso em cada modo em que você faz deploy, porque são arquivos separados: deploy/standalone/config.standalone.toml e deploy/prod/config.base.toml.

As entradas protectedByRoles também declaram o papel de convidado. O nome de um papel de convidado é a chave do agente — o gateway declara protectedByRoles = [{ name = "alpha", permission = "write" }] e o Mycelium cria esses papéis no boot, que é o fato sobre o qual crab/crab-exoskeleton-webapp/lib/invitations.ts é construído. Então um agente novo traz o papel dele à existência, e convidar alguém para ele é, depois disso, um convite comum feito na seção Members da área administrativa.

Passo 5: o ambiente

Duas variáveis, no .env e nunca em um arquivo de configuração:

MYC_PICOCLAW_SCRIBE_TOKEN=<the same shared secret the gateway route uses>
SCRIBE_API_KEY=<the provider API key>

O token autentica o gateway junto ao proxy. A chave de API nunca chega a um arquivo que o agente possa ler: para um agente ganglion o proxy a passa como GANGLION_API_KEY, ou como uma variável GANGLION_MODEL_KEY_<NAME> por modelo quando o inventário governa o workspace. Modelos e provedores cobre essa nomenclatura.

Passo 6: reiniciar, e o primeiro chat

A configuração do gateway é montada a partir de deploy/<mode>/, então uma mudança de rota precisa de um restart. O catálogo de agentes é copiado para dentro da imagem do proxy, então uma mudança de catálogo precisa de um rebuild — a menos que o seu deploy monte um catálogo próprio, caso em que um restart basta. Localmente, a saída segura para tudo é reconstruir os dois:

docker compose up -d --build crab-shell-proxy mycelium-gateway

Na primeira vez que um membro com o papel scribe envia uma mensagem, o proxy cria o workspace dele: cria workspace/ com os subdiretórios memory/, public/, sessions/ e windows/, gera um token bearer por usuário para o container, semeia USER.md a partir da cascata de persona se alguma camada fornecer um, renderiza o arquivo de configuração do harness e inicia o container.

Os arquivos que o agente entrega de volta ao membro vão em public/attachments/. Veja arquivos e entrega.

Checklist

  • data/templates/scribe/workspace/{AGENT.md,SOUL.md,HEARTBEAT.md,USER.md}
  • Entrada de catálogo em crab/crab-shell-proxy/config.yaml com harness: "ganglion", serviceName, token, template, mode e model
  • CRAB_GANGLION_IMAGE apontando para uma referência imutável
  • Bloco de serviço [[scribe]] na configuração do gateway de cada modo em que você faz deploy
  • MYC_PICOCLAW_SCRIBE_TOKEN e o apiKeyEnv do modelo no .env
  • Proxy reconstruído ou seu catálogo remontado, gateway reiniciado
  • Um membro convidado para o papel scribe pela seção Membros

Quando não funciona

As rotas do agente respondem 404. Ou o gateway não tem o serviço scribe — confira se você editou o arquivo que o modo em execução realmente monta — ou o proxy desabilitou o agente na carga. O log de boot diz qual dos dois.

As requisições são rejeitadas como não autorizadas. O MYC_PICOCLAW_SCRIBE_TOKEN no ambiente do proxy e o token = { env = ... } no bloco de segredo do gateway precisam resolver para o mesmo valor.

O membro conversa, mas o agente não tem personalidade. O GANGLION_SYSTEM_FILE aponta para workspace/AGENT.md dentro do container, e esse arquivo chega como um bind somente leitura vindo da cascata de persona. Se nenhuma camada da cascata fornece AGENT.md — nem a subscription, nem o tenant, nem o template — nenhum bind é emitido e o agente roda sem identidade. Confira se o workspace/AGENT.md do seu template existe.

Uma edição no template não chegou a um membro que já existe. AGENT.md, SOUL.md e HEARTBEAT.md são resolvidos de novo a cada ensure, então uma edição chega até eles. Mas o USER.md só é semeado quando o workspace não tem nenhum, de propósito: é o arquivo que o agente escreve de volta, e sobrescrevê-lo apagaria o que o agente aprendeu. Um novo valor para ele chega apenas aos workspaces novos.

Para onde ir agora

Modelos e provedores explica como o modelo da sua entrada de catálogo se relaciona com o inventário que um administrador gerencia, e quais ferramentas um agente ganha. O guia do administrador trata de convidar membros e de dar ao novo agente skills compartilhadas e arquivos compartilhados.