Skip to content

Criando agents customizados

Personas customizadas estendem o Ongrid com seus próprios specialists. Elas vivem em disco como arquivos <name>.md com YAML frontmatter, exatamente como os built-ins — mesmo loader, mesmo registry, mesmo caminho de dispatch. Escreva uma, monte, e o coordinator pode despachar para ela.

Esta página é o contrato.

Estrutura do arquivo

Uma persona é um único arquivo Markdown com YAML frontmatter:

markdown
---
name: specialist-clickhouse
description: ClickHouse 查询性能 / 分区健康 / mutation backlog 专家
when_to_use: |
  When the user asks about:
    - ClickHouse query plan / scan / shuffle slow
    - Partition merges / mutation backlog
    - Replication lag between replicas
    - System.parts / system.mutations inspection

tools:
  - query_knowledge
  - query_clickhouse_system   # custom BaseTool you registered
  - query_promql              # for clickhouse_* metrics
  - host_bash
  - get_edge_summary

disallowed_tools:
  - host_restart_service

permission_mode: read-only
max_turns: 12
model: anthropic/claude-sonnet-4-7

critical_reminder: |
  You're read-only. Never propose direct ALTER / OPTIMIZE without
  citing the system.mutations evidence first. Always check the
  replication lag before recommending any maintenance command.
---

# specialist-clickhouse

You are Ongrid's ClickHouse specialist.

## Step 0: knowledge base check (mandatory)

Before any inspection, call `query_knowledge` once with a natural-
language description of the question. Hit (score >= 0.6) → follow
the playbook. Cite as `(参考 KB: <title>)` in your final reply.

## Working style

1. Start with `query_clickhouse_system` for system.parts /
   system.mutations / system.replication_queue. One call, broad
   snapshot.
2. If a specific table is suspect, drill into `system.parts` for
   that table with bytes / rows / merge_state.
3. For replication: `system.replication_queue` for failures,
   `clickhouse_replica_delay_seconds` PromQL series for trend.
4. For query perf: `system.query_log` with `query_duration_ms`
   sort + `read_rows` to find the heavy query.

## Output

- 现状 (1-2 sentences): which table, which metric, what's wrong.
- 证据 (2-3 lines): system.* row excerpts + PromQL value.
- 建议 (1 line): observation only, or "recommend dispatching
  specialist-ops to run OPTIMIZE/ALTER under reviewer".

Referência do frontmatter

Os campos que o parser entende (ParseAgentMd):

CampoObrigatórioTipoPropósito
namesimstringChave de spawn. Deve ser única. snake_case ou kebab-case.
descriptionsimstringAparece no catálogo de agents do coordinator.
when_to_usesimstringA primeira linha aparece no catálogo. Estritamente obrigatório porque o coordinator não consegue escolher uma persona sem isso.
toolsnão[]stringWhitelist de nomes de BaseTool. Vazio = não herda nada.
disallowed_toolsnão[]stringBlacklist. Vence sobre whitelist; suporta wildcards (*_skill).
permission_modenãostringread-only / mutating-with-confirm / dual-sign-required. Hoje é informativo; versões futuras podem auto-conectar decorators baseando-se nisso.
max_turnsnãointCap rígido do loop ReAct. Padrão 15.
modelnãostringIdentificador do LLM (ex.: anthropic/claude-sonnet-4-7). Cai para o padrão da org.
critical_remindernãostringEnvolvido em <critical-reminder>...</critical-reminder> no system prompt. Também re-injetado por turno pela camada de grafo.
initial_promptnãostringPrepended ao primeiro turno de usuário do worker. Raramente usado.
backgroundnãobooltrue = spawn assíncrono (UI não bloqueia). Usado pelo reviewer.
omit_claude_mdnãoboolSuprime o prompt base do runtime para essa persona.
metadatanãomapForma livre. metadata.ongrid.{scope, min_ongrid_version} é lido pelo registry; o resto é pass-through.

Campos desconhecidos são preservados em Agent.UnknownFields para que futuras adições ao formato de persona do Claude Code (effort, isolation, mcp_servers, hooks, …) não quebrem o load.

tools vs disallowed_tools

Whitelist + blacklist, blacklist vence. Então:

yaml
tools: ["query_*", "host_bash"]    # tudo começando com query_, mais bash
disallowed_tools: ["query_devices"] # mas não esse

deixa query_promql, query_logql, query_traceql, query_knowledge, … e host_bash, menos query_devices.

Wildcards: *_skill casa com todo nome de tool terminando em _skill. É assim que o reviewer bloqueia todas as execuções de skill em uma única linha.

O AgentTool também é removido automaticamente da bag de qualquer worker — workers não podem dar spawn em workers. Você não precisa listá-lo em disallowed_tools.

Onde as personas vivem

O runtime percorre duas raízes:

  1. A raiz da imagem/app/agents/ dentro do container do manager. Contém as seis personas que vêm de fábrica. Read-only dentro da imagem; sobrevive a restart do container mas não a código custom.
  2. A raiz do marketplace/var/lib/ongrid/agents/ (volume montado). Personas escritas pelo usuário pousam aqui via a UI Settings → Agents ou via o caminho de instalação do marketplace.

Ambas são mescladas no mesmo AgentRegistry. Em colisão de nome o loader registra um warning e mantém o primeiro load. Para sobrescrever uma persona built-in, salve sua versão com o mesmo name via a UI Settings — AgentRegistry.Replace faz upsert in place.

Por onde começar

O caminho mais rápido é copiar agents/specialist-disk.md no seu editor, renomear, e ajustar a tool bag. O formato carrega todas as convenções (KB-first, receita em 4 passos, formato de saída) que funcionam bem com o coordinator.

Hot reload vs restart

AçãoHot-reloadable?Como
Editar corpo da persona (system prompt)SimSettings → Agents → Save
Mudar whitelist de toolsSimIdem. Filtro é aplicado por spawn.
Mudar model / max_turnsSimIdem. Novos spawns pegam os novos valores.
Adicionar uma nova personaSimSettings → Agents → New, ou drop do arquivo + Reload
Deletar uma personaSimSettings → Agents → Delete, ou remover arquivo + Reload
Sobrescrever uma built-in (mesmo name)SimReplace faz upsert; coordinator usa a nova.
Mudar quais tools existem na bagNãoRegistro de BaseTool é binary-side.
Adicionar uma nova BaseToolNãoExige mudança de código + restart do manager.
Mudar semântica de default_localeNãoIsso é código de runtime.

O lock em volta do AgentRegistry é um sync.RWMutex. Um turno de coordinator em andamento que já obteve um ponteiro de persona segue usando o snapshot; o próximo turno do coordinator vê a nova persona.

Debugando

"Coordinator nunca despacha para minha persona"

  1. Cheque o catálogo de agents no system prompt do coordinator (o manager loga o prompt renderizado no startup com --log-level=debug). Sua persona deve aparecer com seu description e a primeira linha de when_to_use.
  2. Se o catálogo não a tem: o loader registrou um warning. Cheque AgentRegistry.Warnings() via API (GET /api/v1/agents/warnings) ou procure linhas chatruntime: parse <path> no log do manager.
  3. Se o catálogo a tem mas o LLM não a escolhe: aperte o when_to_use. Comece com um padrão concreto de trigger; o LLM é instruído a ler a primeira linha como a dica de match.

"Worker spawna mas falha imediatamente"

Causas comuns:

  • Uma tool whitelisted não está na bag. O runtime filtra e silenciosamente descarta qualquer coisa não presente; o worker não consegue chamar o que não existe. Cheque GET /api/v1/skills para a bag ativa.
  • O identificador do modelo está errado. O resolver de chat model mapeia anthropic/<x> para default_provider se não estiver configurado. Defina default_provider como anthropic em Settings → LLM ou fixe um provider+model concreto na persona.
  • max_turns muito baixo. Um worker que esgota os turns antes de produzir uma mensagem assistant final retorna como failed. Aumente para 15+ em qualquer persona não-trivial.

"Worker retorna OK mas a saída é lixo"

O corpo da persona é seu system prompt. Aperte:

  • Comece com Step 0: uma única chamada KB forçada. Ancora o worker.
  • Especifique o formato de saída verbatim no corpo. O coordinator faz parse nesse formato.
  • Use critical_reminder para restrições duras (read-only, sem PII, idioma de saída). É envolvido em <critical-reminder> E re-injetado por turno — o LLM o vê em cada iteração.

Testando sua persona

Dois pontos de integração:

Pela superfície de chat

Abra /chat, faça uma pergunta que case com o when_to_use da sua persona. Observe o SPA — se o coordinator despachar, um "Agent tile" aparece com o name da sua persona + a description do AgentTool. Clique para o transcript do worker.

Pela API

bash
curl -X POST http://localhost:8080/api/v1/chat \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"prompt": "<the question that should trigger your persona>"}'

A resposta streaming traz à tona:

  • deltas de text — a prosa do coordinator.
  • envelopes agent_tile — cada dispatch de AgentTool.
  • envelopes task_notification — conclusão de worker.

Se sua persona for despachada, o agent_tile.persona correspondente será o seu name.

Quando NÃO escrever uma persona custom

  • A tarefa é responder com 1 tool. Não embrulhe "consultar meu Prometheus customizado" em uma persona. Registre um BaseTool custom e deixe o coordinator chamar.
  • A tarefa é one-off. Personas são para padrões repetidos. Para uma investigação one-shot, basta perguntar ao coordinator diretamente.
  • A tarefa precisa chamar todos os 5 specialists. Isso é exatamente para o que serve o coordinator; não escreva um meta-specialist que recrie o comportamento do coordinator.

Regra boa: escreva uma persona quando a mesma forma de pergunta recorre, a resposta exige 5+ chamadas de tool, e a tool bag é mais estreita do que a que o coordinator carrega.

Compartilhando personas

  • Coloque o arquivo .md no seu repo ops. Monte-o no container do manager sob /var/lib/ongrid/agents/. O registry pega no startup (ou em uma chamada de Reload).
  • Para rollout em toda a org, distribua pelo marketplace de skills — a instalação pelo marketplace empacota personas + skills juntos e dispara um Reload automaticamente.

Relacionado