Documentação oficial desta página · v0.20.6
Delegação para subagentes
A ferramenta delegate_task cria instâncias filhas de AIAgent com contexto isolado, acesso a ferramentas herdado e sessões de terminal próprias. Cada filho ganha uma conversa nova e trabalha de forma independente — só o resumo final dele entra no contexto do pai.
Chamadas de modelo de nível superior rodam em segundo plano automaticamente. O Hermes devolve um identificador na hora, para a conversa continuar, e depois posta o resultado de volta como uma mensagem nova. Um subagente orquestrador espera pelos próprios trabalhadores para poder sintetizar os resultados antes de retornar.
Tarefa única
delegate_task(
goal="Depurar por que os testes falham",
context="Erro: assertion em test_foo.py linha 42"
)
Lote em paralelo
Até 3 subagentes simultâneos por padrão (configurável, sem teto rígido):
delegate_task(tasks=[
{"goal": "Pesquisar o tema A", "context": "Foque em fontes primárias recentes"},
{"goal": "Pesquisar o tema B", "context": "Compare as principais explicações"},
{"goal": "Consertar o build", "context": "Raiz do projeto: /home/usuario/projeto"}
])
Como funciona o contexto do subagente
Subagentes começam com uma conversa completamente nova. Eles têm zero conhecimento do histórico de conversa do pai, das chamadas de ferramenta anteriores ou de qualquer coisa discutida antes da delegação. O único contexto do subagente vem dos campos goal e context que o agente pai preenche quando chama delegate_task.
Uma exceção: quando o pai tem um diretório de espaço de trabalho resolvido, o prompt de sistema de cada subagente embute os arquivos de contexto de projeto daquele espaço de trabalho (.hermes.md > cadeia AGENTS.md > CLAUDE.md > .cursorrules — a mesma descoberta, a mesma prioridade e os mesmos limites de tamanho do prompt de sistema do agente principal; SOUL.md fica de fora). Subagentes trabalhando em um repositório operam sob as convenções do próprio repositório sem precisar redescobri-las.
Isso significa que o agente pai precisa passar tudo o que o subagente precisa na chamada:
# RUIM - o subagente não faz ideia do que é "o erro"
delegate_task(goal="Conserte o erro")
# BOM - o subagente tem todo o contexto de que precisa
delegate_task(
goal="Consertar o TypeError em api/handlers.py",
context="""O arquivo api/handlers.py tem um TypeError na linha 47:
'NoneType' object has no attribute 'get'.
A função process_request() recebe um dict de parse_body(),
mas parse_body() devolve None quando falta o Content-Type.
O projeto está em /home/usuario/meuprojeto e usa Python 3.11."""
)
O subagente recebe um prompt de sistema focado, montado a partir do seu goal e do seu context, que o instrui a concluir a tarefa e entregar um resumo estruturado do que fez, do que encontrou, de quais arquivos modificou e de quais problemas apareceram.
Exemplos práticos
Pesquisa em paralelo
Pesquise vários temas ao mesmo tempo e junte os resumos:
delegate_task(tasks=[
{
"goal": "Pesquisar o estado atual do WebAssembly em 2025",
"context": "Foque em: suporte nos navegadores, runtimes fora do navegador, suporte a linguagens"
},
{
"goal": "Pesquisar o estado atual da adoção do RISC-V em 2025",
"context": "Foque em: chips de servidor, sistemas embarcados, ecossistema de software"
},
{
"goal": "Pesquisar o avanço da computação quântica em 2025",
"context": "Foque em: avanços em correção de erros, aplicações práticas, principais atores"
}
])
Revisão de código + correção
Delegue um fluxo de revisar e corrigir para um contexto novo:
delegate_task(
goal="Revisar o módulo de autenticação em busca de problemas de segurança e corrigir os que encontrar",
context="""Projeto em /home/usuario/webapp.
Arquivos do módulo de autenticação: src/auth/login.py, src/auth/jwt.py, src/auth/middleware.py.
O projeto usa Flask, PyJWT e bcrypt.
Foque em: injeção de SQL, validação de JWT, tratamento de senhas, gerenciamento de sessão.
Corrija os problemas encontrados e rode a suíte de testes (pytest tests/auth/)."""
)
Refatoração em vários arquivos
Delegue uma refatoração grande que inundaria o contexto do pai:
delegate_task(
goal="Refatorar todos os arquivos Python em src/ para trocar print() por logging de verdade",
context="""Projeto em /home/usuario/meuprojeto.
Use o módulo 'logging' com logger = logging.getLogger(__name__).
Troque as chamadas de print() pelos níveis de log apropriados:
- print(f"Error: ...") -> logger.error(...)
- print(f"Warning: ...") -> logger.warning(...)
- print(f"Debug: ...") -> logger.debug(...)
- Outros prints -> logger.info(...)
Não mexa em print() em arquivos de teste nem na saída da CLI.
Rode o pytest depois para verificar que nada quebrou."""
)
Detalhes do modo em lote
Quando um agente de nível superior fornece um array tasks, o Hermes devolve um identificador de segundo plano, roda os subagentes em paralelo e posta um resultado consolidado depois que todos os filhos terminam. Um subagente orquestrador espera pelo lote dele no turno atual para poder sintetizar os resultados.
- Concorrência máxima: 3 tarefas por padrão (configurável por
delegation.max_concurrent_childrenou pela variável de ambienteDELEGATION_MAX_CONCURRENT_CHILDREN; piso de 1, sem teto rígido). Lotes maiores que o limite devolvem um erro de ferramenta em vez de serem truncados em silêncio. - Pool de threads: usa
ThreadPoolExecutorcom o limite de concorrência configurado como número máximo de trabalhadores - Exibição de progresso: no modo CLI, uma visão em árvore mostra as chamadas de ferramenta de cada subagente em tempo real, com linhas de conclusão por tarefa. No modo gateway, o progresso é agrupado e repassado ao callback de progresso do pai
- Ordenação dos resultados: os resultados são ordenados pelo índice da tarefa para bater com a ordem de entrada, independentemente da ordem de conclusão
- Cancelamento: mensagens seguintes não cancelam um lote de segundo plano de nível superior.
/stopou fechar/resetar a sessão dona cancela os filhos ativos dela. Filhos síncronos de orquestradores continuam seguindo o estado de interrupção do pai
Delegação síncrona de tarefa única a partir de um orquestrador roda direto, sem o custo do pool de threads.
Conclusões de segundo plano duráveis
Quando uma delegação em segundo plano termina, o Hermes guarda o evento de conclusão no
state.db do perfil ativo antes de publicá-lo na fila normal de turnos
novos. Se o Hermes reiniciar depois da conclusão mas antes da entrega, o evento
pendente é restaurado e roteado pelas mesmas checagens de posse. Consumidores
concorrentes usam uma reivindicação durável, então só o consumidor que aceita com
sucesso o turno sintético confirma a entrega; tentativas frustradas liberam a reivindicação para
nova tentativa.
Isso não retoma a execução do filho depois de uma queda. Uma delegação cujo processo dono
some enquanto ela ainda está rodando é registrada como unknown, porque o
Hermes não consegue provar se os efeitos colaterais externos aconteceram. Registros pendentes e
entregues são limitados em quantidade e locais ao perfil.
Notificações de processos em segundo plano do filho
Processos em segundo plano que um subagente inicia (por exemplo, npm ci com
notify_on_complete) tecnicamente roteiam as notificações de conclusão e de padrão
observado para a conversa pai, porque qualquer coisa que sobreviva ao
filho precisa de um consumidor durável. Por padrão essas notificações são
suprimidas na conversa do pai — o resultado consolidado da delegação do filho
é a entrega, e paredes de "processo terminou" no meio da conversa vindas dos builds
internos de um filho são ruído. Eventos suprimidos são registrados em nível de debug
com o ID de sessão do processo e o ID de tarefa do subagente, então continuam diagnosticáveis.
O resultado da delegação em si nunca é suprimido. Para restaurar a entrega das notificações de processo do filho (cada uma carrega uma linha de atribuição "Started by subagent …"):
delegation:
surface_child_process_notifications: true # padrão: false
Sobrescrita de modelo
Você pode configurar um modelo diferente para os subagentes pelo config.yaml — útil para delegar tarefas simples a modelos mais baratos/rápidos:
# Em ~/.hermes/config.yaml
delegation:
model: "google/gemini-flash-2.0" # Modelo mais barato para os subagentes
provider: "openrouter" # Opcional: rotear os subagentes para outro provedor
Se omitido, os subagentes usam o mesmo modelo do pai.
Estratégia de custo: planejador de fronteira, trabalhadores baratos
Decompor um problema em subtarefas bem especificadas exige julgamento de nível de fronteira; executar uma subtarefa que já chega com objetivo claro, contexto completo e um contrato de saída normalmente não exige. Enquanto isso, é nos filhos que os tokens vão embora — um lote paralelo de subagentes costuma queimar a grande maioria do total de tokens de uma execução, então o modelo dos trabalhadores é onde o custo de fato mora. Fixar delegation.model em um modelo barato enquanto sua sessão principal fica em um modelo de fronteira mantém a qualidade do planejamento onde ela importa e corta gasto onde está o volume:
# ~/.hermes/config.yaml
model:
default: "seu-modelo-de-fronteira" # o pai (planejador) fica no modelo de fronteira
delegation:
model: "seu-modelo-barato" # todos os filhos de delegate_task rodam neste
provider: "openrouter" # opcional: rotear os filhos para outro provedor
Ordem de resolução: delegation.base_url (endpoint direto) tem precedência, depois delegation.provider (pacote completo de credenciais resolvido pelo sistema de provedores em runtime) e, quando nenhum dos dois está definido, os filhos herdam o provedor e as credenciais do pai; delegation.model se aplica em todos os casos e, quando está vazio, os filhos herdam o modelo do pai.
Repare que a fixação é global: delegate_task não tem parâmetro de modelo por tarefa, então todo filho de um lote roda no modelo de delegação configurado. Para subtarefas sensíveis à qualidade que precisam de um modelo mais forte, ou deixe delegation.model sem definir naquela sessão, ou entregue a tarefa ao quadro kanban, que aceita sim uma sobrescrita de modelo por tarefa.
O comando /review
O /review cria um subagente de segundo plano independente e com privilégios plenos, cujo único trabalho é revisar o que a sua conversa acabou de produzir — um PR, um diff, código, documentação, um design. Funciona em todas as interfaces: CLI, TUI, aplicativo Desktop e todas as plataformas de mensagens do gateway.
/review # revisa o que as últimas 10 mensagens apresentaram
/review focus on security # acrescenta instruções extras para o revisor
O que acontece:
- As últimas 10 mensagens de usuário/assistente são capturadas como evidência inicial do revisor (saídas de ferramenta e mensagens de sistema ficam de fora).
- Um subagente revisor é despachado no mesmo trilho de delegação em segundo plano do
delegate_task— ele recebe o conjunto normal completo de ferramentas de subagente (terminal, web, arquivos, navegador...), então de fato abre o PR, lê o diff e roda código em vez de julgar pelo trecho. - O revisor herda o contexto de trabalho do agente principal: quaisquer skills que o agente principal tinha carregado (pré-carregadas na inicialização ou via
skill_viewdurante a sessão) são citadas no briefing dele, com a instrução de carregá-las e julgar o trabalho pelas convenções delas. Como todo subagente, o prompt de sistema dele também embute os arquivos de contexto de projeto do espaço de trabalho (AGENTS.md / CLAUDE.md / .cursorrules) como convenções obrigatórias. - Quando termina, a revisão completa dele volta para a mesma sessão como uma conclusão normal de subagente em segundo plano — o seu agente principal a vê e pode agir sobre ela (corrigir os apontamentos, mandar ajustes, responder para você).
O fluxo canônico: o seu agente principal abre um PR, você digita /review e um segundo par de olhos investiga enquanto você segue trabalhando; a revisão cai de volta na conversa endereçada ao agente que criou o PR.
Modelo de revisão
Por padrão o revisor roda no seu modelo principal. Para fixar um modelo dedicado de revisão, defina auxiliary.review no config.yaml:
auxiliary:
review:
provider: openrouter # ou nous, anthropic, um base_url direto, ...
model: anthropic/claude-opus-4.6 # um modelo revisor forte
As credenciais resolvem exatamente como numa fixação de delegation.provider (pacote completo do provedor em runtime: base_url, chave de api, api_mode). provider: auto com model vazio significa "herdar o modelo do agente principal" — o padrão.
O /review é deliberadamente separado do /refine: o /refine revisa a conversa para atualizar memória e skills, o /review revisa o produto do trabalho que a conversa criou.
Acesso herdado a ferramentas
O delegate_task não aceita um parâmetro toolsets visível ao modelo. Cada subagente herda os conjuntos de ferramentas habilitados do pai, para que o modelo não possa dar a um filho capacidades que o pai não tem. Configure as ferramentas do pai antes de começar a conversa se o trabalho delegado precisar de capacidades adicionais.
Certas ferramentas são bloqueadas para subagentes mesmo quando o pai as tem:
delegate_task— bloqueada para subagentes folha (o padrão). Mantida para filhos comrole="orchestrator", limitada pormax_spawn_depth— veja Limite de profundidade e orquestração aninhada abaixo.clarify— subagentes não podem interagir com o usuáriomemory— nada de escrever na memória persistente compartilhadasend_message— nada de efeitos colaterais entre plataformascronjob— nada de agendar mais trabalho em nome do pai
Os dois papéis mantêm execute_code (chamada programática de ferramentas), para que os filhos possam agrupar trabalho mecânico.
Máximo de iterações
Cada subagente tem um limite de iterações (padrão: 50) que controla quantos turnos de chamada de ferramenta ele pode dar:
delegate_task(
goal="Conferência rápida de arquivo",
context="Verifique se /etc/nginx/nginx.conf existe e imprima as 10 primeiras linhas",
max_iterations=10 # Tarefa simples, não precisa de muitos turnos
)
Tempo limite do filho
Por padrão não há tempo limite de relógio para subagentes. Os filhos só falham pelo que estão de fato fazendo — erros de API, erros de ferramenta ou o estouro do orçamento de iterações — nunca por um cronômetro do nível da delegação. Versões anteriores traziam um teto rígido (300s, depois 600s), que vivia matando filhos legitimamente ocupados no meio da tarefa: revisões profundas de código, grandes leques de pesquisa e modelos de raciocínio lentos rotineiramente precisam de mais de 10 minutos enquanto avançam o tempo todo.
Filhos genuinamente travados continuam sendo detectados: o monitor de obsolescência do heartbeat para de renovar a atividade do pai quando um filho não avança (nenhuma chamada de API, nenhum início de ferramenta e nenhum tique de carimbo de atividade), deixando o tempo limite de inatividade do gateway disparar em um trabalhador de fato emperrado. Uma espera de modelo em andamento ainda conta como progresso — subagentes renovam o relógio de atividade enquanto esperam pelo provedor, então uma conclusão local lenta / de prefill longo não é tratada como travada.
Se você quer um teto rígido mesmo assim (por exemplo, controle de custo em delegação sem supervisão disparada por cron), habilite por instalação:
delegation:
child_timeout_seconds: 0 # padrão: 0 = sem tempo limite
# child_timeout_seconds: 1800 # teto rígido opcional (piso de 30s)
Um valor positivo impõe um limite rígido de relógio a cada filho; 0 ou um valor negativo desliga isso.
Quando um teto configurado dispara, o resultado do filho carrega metadados
estruturados de tempo limite ao lado da mensagem de erro, para que pais e hooks distingam uma
morte por cronômetro de outras falhas sem ler texto: timeout_seconds
(o teto configurado), timed_out_after_seconds (relógio real) e
timeout_phase (before_first_llm_call quando o filho nunca chegou à
primeira requisição, after_llm_calls caso contrário). Os três são null em
erros que não são de tempo limite.
Com um teto rígido configurado, se um subagente estoura o tempo tendo feito zero chamadas de API (normalmente: provedor inalcançável, falha de autenticação ou rejeição do esquema de ferramentas), o delegate_task escreve um diagnóstico estruturado em ~/.hermes/logs/subagent-timeout-<sessao>-<timestamp>.log contendo um retrato da configuração do subagente, o rastro de resolução de credenciais, quaisquer mensagens de erro iniciais e os stack traces de todas as threads vivas (não só as do próprio filho) — um filho parado esperando por uma thread auxiliar aninhada é indistinguível de um provedor lento sem o quadro completo.
Detecção de travamento em subagentes de segundo plano
Delegações em segundo plano (delegate_task(background=true)) são vigiadas por um
monitor de travamento baseado em progresso — ligado por padrão, sem configuração.
Diferente de um tempo limite de relógio, ele nunca toca em um filho que está avançando,
não importa quanto tempo ele rode.
O monitor amostra os sinais de progresso de cada filho destacado — contagem de chamadas de API, ferramenta atual e carimbo da última atividade (que avança a cada token transmitido, transição de ferramenta e fronteira de chamada de API, então um filho no meio de um stream de resposta longa sempre conta como vivo):
- Filhos que avançam nunca são tocados. Qualquer sinal de avanço reseta o relógio.
- Um filho cujo progresso está completamente congelado além do limiar de obsolescência (450s ocioso, 1200s dentro de uma ferramenta — comandos de terminal e buscas na web legitimamente lentos ganham o teto maior) é interrompido e recebe uma janela de tolerância de 120s. Um filho que se desenrola a tempo entrega os resultados parciais pelo caminho normal de conclusão.
- Um filho que nunca retorna é finalizado à força com um evento terminal de conclusão
stalled, para que a sessão dona ouça um desfecho em vez de ficar em silêncio, e a vaga assíncrona se libere para novo trabalho.
O evento stalled carrega metadados estruturados espelhando os campos de tempo limite
do caminho síncrono: stalled_after_quiet_seconds, stall_threshold_seconds,
stall_phase (idle / in_tool) e stall_grace_seconds.
Isso fechou um modo de falha antigo em que um filho de segundo plano emperrado deixava a sessão dele com cara de morta até um reinício de processo. O emperramento por baixo (filhos travando na primeira chamada de API depois de dias de gateway no ar) também foi corrigido na raiz: filhos delegados agora rodam as requisições de API no protocolo OpenAI inline, na própria thread de conversa, em vez de em uma thread trabalhadora aninhada — a camada onde o emperramento morava. O monitor de travamento fica como rede de segurança para qualquer outra coisa.
Monitorar subagentes em execução (/agents)
A TUI traz uma sobreposição /agents (alias /tasks) que transforma o leque recursivo de delegate_task em uma superfície de auditoria de primeira classe:
- Visão em árvore ao vivo dos subagentes em execução e recém-terminados, agrupados por pai
- Totais de custo, tokens e arquivos tocados por ramo
- Controles de matar e pausar — cancele um subagente específico em pleno voo sem interromper os irmãos
- Revisão posterior: percorra o histórico turno a turno de cada subagente mesmo depois que eles voltaram ao pai
A CLI clássica só imprime /agents como um resumo em texto; é na TUI que a sobreposição brilha. Veja TUI — Comandos de barra.
Na CLI clássica e em toda plataforma de gateway (Telegram, Discord, Slack, ...),
o /agents também lista delegações de segundo plano com atividade ao vivo por filho,
amostrada direto de cada filho em execução:
Background delegations: 1 running
- deleg_ab12cd34 · running · research the delegation stall monitor
- child 1: 4 api calls · in web_search · active 12s ago
- child 2: 7 api calls · between turns · active 3s ago
Uma delegação que o monitor de travamento sinalizou aparece como
stalling · no progress 450s — interrupting, e filhos quietos há muito tempo mas
saudáveis mostram o tempo de silêncio, para você distinguir "lento" de "travado" num
relance.
Redirecionar um subagente em execução
Interromper um filho joga fora o trabalho em andamento dele; muitas vezes você só quer redirecioná-lo.
A partir do agente pai (visível ao modelo)
O agente pai orquestra os próprios filhos em execução com a mesma ferramenta delegate_task com que os criou — sem ferramenta de controle separada:
{"action": "list"}
{"action": "steer", "subagent_id": "sa-0-1a2b3c4d", "message": "focus on pricing instead"}
{"action": "stop", "subagent_id": "sa-0-1a2b3c4d"}
listdevolve os filhos vivos da conversa:subagent_id, goal, status,running_seconds,accepting_steere o caminho da transcrição ao vivo. Os ids também voltam na resposta de despacho da criação, emsubagent_ids.steerenfileira uma correção de rota em um filho em execução sem pará-lo (semântica de entrega abaixo).stopencerra um filho antes da hora, na próxima fronteira de iteração; o resultado parcial ainda volta para a conversa como uma mensagem normal de conclusão.
Ações de controle rodam de forma síncrona dentro do turno (nunca em segundo plano), ficam restritas à própria árvore de criação de quem chamou — uma conversa nunca consegue ver ou controlar os filhos de outra sessão — e nunca consomem a cota de criação de subagentes por turno, então o stop continua funcionando mesmo depois de a cota estourar.
A partir da TUI / do gateway (visível à sessão)
O steer_subagent(subagent_id, text) em tools/delegate_tool.py é o espelho, do lado do redirecionamento, do interrupt_subagent(): ele enfileira texto em um filho vivo pelo mesmo mecanismo do /steer — o texto é anexado ao último resultado de ferramenta do filho na próxima fronteira de iteração, a chamada de ferramenta em andamento nunca é cortada, e o filho a vê como uma mensagem de usuário fora de banda. Hosts programáticos chegam nisso pelo RPC de gateway subagent.steer, restrito à sessão, que fica ao lado do subagent.interrupt:
{"method": "subagent.steer", "params": {"session_id": "owning-ui-session", "subagent_id": "sa-0-1a2b3c4d", "text": "focus on pricing instead"}}
Os ids de subagente vêm de delegation.status (ou de list_active_subagents()) — o mesmo lugar de onde o subagent.interrupt os pega. O gateway só aceita redirecionamento vindo exatamente da sessão viva de interface/gateway que criou o filho. Uma identidade de sessão ausente, alheia, ambígua ou velha/reciclada é rejeitada; conhecer um id global de subagente não é autoridade. Chamadores diretos dentro do processo mantêm o contrato do helper sem escopo, de propósito.
Enfileirado não é entregue, mas nunca é sucesso fingido. Uma resposta "queued" significa que o texto foi aceito antes da fronteira de conclusão do filho, não necessariamente que o filho o viu. Aceitação e conclusão são sincronizadas: ou o filho ainda consegue consumir o texto, ou o texto exato é drenado para o resultado como pending_steer. Chamadas depois do fechamento retornam "rejected". Se um filho aceitou o redirecionamento mas já tinha produzido a resposta final, a entrada de conclusão que o pai recebe o mantém como missed_steer, com uma nota anexada ao resumo:
[steer did not land — the subagent finished before it could be delivered: focus on pricing instead]
Assim o pai (ou o operador que o conduz) distingue um filho redirecionado de um que terminou com as instruções antigas, e reemite a orientação como um acompanhamento em vez de confiar que ela chegou.
Transcrições ao vivo
Todo despacho de delegate_task também cria um log somente-acréscimo e legível por humanos para cada tarefa, para que você (ou o agente pai) acompanhe um subagente trabalhando em tempo real, em vez de esperar o resumo consolidado:
<hermes_home>/cache/delegation/live/<delegation_id>/task-<n>.log
A resposta de despacho inclui os caminhos em live_transcripts, e os arquivos são pré-criados na hora do despacho, então isso funciona na hora:
tail -f ~/.hermes/cache/delegation/live/deleg_ab12cd34/task-0.log
Cada linha tem carimbo de tempo e mostra o texto do assistente do filho, trechos de raciocínio, chamadas de ferramenta (-> tool_name({args})), resultados de ferramenta e um marcador final de status. Um manifest.json no mesmo diretório descreve o lote (objetivos, contagem de tarefas, status por tarefa). Os logs continuam existindo depois da conclusão — eles funcionam como o registro operacional de fidelidade total ao lado do resumo — e diretórios com mais de 7 dias são podados automaticamente em novos despachos. Como moram em cache/delegation, também são legíveis a partir de backends de terminal remotos (Docker/Modal/SSH).
Limite de profundidade e orquestração aninhada
Por padrão, a delegação é plana: um pai (profundidade 0) cria filhos (profundidade 1), e esses filhos não podem delegar adiante. Isso evita delegação recursiva descontrolada.
Para fluxos de várias etapas (pesquisa → síntese, ou orquestração paralela sobre subproblemas), um pai pode criar filhos orquestradores que podem criar os próprios trabalhadores:
delegate_task(
goal="Levantar três abordagens de revisão de código e recomendar uma",
role="orchestrator", # Permite que este filho crie os próprios trabalhadores
context="...",
)
role="leaf"(padrão): o filho não pode delegar adiante — idêntico ao comportamento de delegação plana.role="orchestrator": o filho mantém o conjunto de ferramentasdelegation. Controlado pordelegation.max_spawn_depth(padrão 1 = plano, entãorole="orchestrator"não faz nada nos padrões). Subamax_spawn_depthpara 2 para permitir que filhos orquestradores criem netos folha; 3 ou mais para árvores mais profundas. Não existe teto — o custo é o limite prático.delegation.orchestrator_enabled: false: interruptor global que força todo filho a serleaf, independentemente do parâmetrorole.
Aviso de custo: com max_spawn_depth: 3 e max_concurrent_children: 3, a árvore pode chegar a 3×3×3 = 27 agentes folha simultâneos. Cada nível extra multiplica o gasto — suba o max_spawn_depth de propósito.
Tempo de vida e durabilidade
Chamadas de delegate_task de nível superior visíveis ao modelo rodam em segundo plano automaticamente onde a sessão suporta entrega posterior. O Hermes devolve um identificador na hora, e o resultado volta à conversa depois que o filho ou o lote termina. Subagentes orquestradores esperam pelos trabalhadores deles no turno atual porque precisam sintetizar esses resultados antes de retornar. Endpoints sem estado de requisição/resposta caem para execução síncrona quando não conseguem entregar um resultado destacado depois.
- Mensagens normais de acompanhamento não cancelam filhos de segundo plano. O
/stopcancela as delegações de segundo plano em execução, e fechar ou resetar a sessão dona descarta os filhos ativos dela. - Fechar/resetar a sessão explicitamente interrompe os filhos de segundo plano daquela sessão. Fechar um visualizador de TUI de uma sessão que pertence ao gateway não mata o trabalho do gateway.
- Um reinício do processo do Hermes não retoma um filho em execução. A tentativa dele vira
unknownporque o Hermes não consegue provar quais efeitos colaterais aconteceram. - Um filho que terminou antes do reinício mas cujo resultado não foi entregue é restaurado e roteado de volta pelas checagens normais da sessão dona.
- Filhos cancelados devolvem um resultado estruturado (
status="interrupted",exit_reason="interrupted"), mas, como o pai também foi interrompido, esse resultado muitas vezes nunca chega a uma resposta visível ao usuário.
Para execução durável que precisa sobreviver ao fechamento da sessão ou ao reinício do processo, use:
cronjob(action=create) — agenda uma execução separada do agente; imune a interrupções do turno do pai.terminal(background=True, notify_on_complete=True)— comandos de shell de longa duração que continuam rodando enquanto o agente faz outras coisas.
Propriedades principais
- Cada subagente ganha a própria sessão de terminal (separada da do pai)
- Subagentes herdam os conjuntos de ferramentas habilitados do pai; o modelo não pode escolhê-los nem ampliá-los por chamada
- Delegação aninhada é opcional — só filhos com
role="orchestrator"podem delegar adiante, e só quandomax_spawn_depthé elevado do padrão 1 (plano). Desligue globalmente comorchestrator_enabled: false. - Subagentes folha não podem chamar:
delegate_task,clarify,memory,send_message,cronjob. Subagentes orquestradores mantêm odelegate_taskmas seguem com os outros bloqueios. Os dois papéis mantêmexecute_code(chamada programática de ferramentas), para que os filhos possam agrupar trabalho mecânico em vez de queimar iterações de raciocínio. - O cancelamento segue a posse —
/stopou fechar/resetar a sessão dona cancela os filhos de segundo plano dela; descendentes síncronos sob orquestradores seguem o estado de interrupção do pai - Só o resumo final entra no contexto do pai, mantendo o uso de tokens eficiente
- Subagentes herdam a chave de API, a configuração de provedor e o pool de credenciais do pai (o que permite rotação de chave em limites de taxa)
Isolamento por worktree
Por padrão, os subagentes compartilham o diretório de trabalho do pai — o que serve para pesquisa
e trabalho de muita leitura, mas filhos em paralelo editando o mesmo repositório podem colidir.
Defina delegation.worktree_isolation: true para dar a cada filho o próprio git
worktree, ramificado do HEAD atual do repositório (inspirado no
--subagent-worktree-isolation do Muse Code):
delegation:
worktree_isolation: true # padrão: false
Com o isolamento ligado:
- Cada filho começa o terminal dele em
<repo>/.worktrees/subagent-<id>, no próprio branchhermes-subagent/subagent-<id>, e a mensagem de objetivo diz a ele para trabalhar e commitar ali. - O checkout do pai fica intocado; os filhos não conseguem atropelar as edições uns dos outros.
- Quando um filho termina, a entrada de resultado dele ganha um campo
worktreeinformandopath,branch,commits(à frente da base) edirty. O pai revisa ou mescla cada branch (git log <branch>,git merge <branch>). - Um worktree que fica sem commits e com a árvore limpa é podado automaticamente
(
pruned: true); qualquer um que guarde trabalho é mantido. - A poda exige prova. Se uma sondagem de inspeção do git falhar — ou se a própria
finalização der erro — o worktree e o branch são mantidos e a entrada carrega
inspection_failed: truemais umanote— aícommits/dirtysão valores padrão, não medições, então inspecione o worktree em vez de supor que o filho não produziu nada.
Escopo: opcional, só para git e só para o backend de terminal local. Em um diretório que não é git, em backends docker/ssh/modal, ou se a criação do worktree falhar, a configuração degrada em silêncio para o comportamento atual de espaço de trabalho compartilhado — nunca vira erro.
Delegação vs execute_code
| Fator | delegate_task | execute_code |
|---|---|---|
| Raciocínio | Laço completo de raciocínio do LLM | Só execução de código Python |
| Contexto | Conversa nova e isolada | Sem conversa, só script |
| Acesso a ferramentas | Todas as ferramentas não bloqueadas, com raciocínio | 7 ferramentas via RPC, sem raciocínio |
| Paralelismo | 3 subagentes simultâneos por padrão (configurável) | Script único |
| Melhor para | Tarefas complexas que exigem julgamento | Pipelines mecânicos de vários passos |
| Custo em tokens | Maior (laço completo do LLM) | Menor (só o stdout volta) |
| Interação com o usuário | Nenhuma (subagentes não podem pedir esclarecimento) | Nenhuma |
Regra de bolso: use delegate_task quando a subtarefa exigir raciocínio, julgamento ou resolução de problema em vários passos. Use execute_code quando você precisar de processamento mecânico de dados ou de fluxos roteirizados.
Configuração
# Em ~/.hermes/config.yaml
delegation:
max_iterations: 50 # Máximo de turnos por filho (padrão: 50)
# max_concurrent_children: 3 # Filhos em paralelo por lote (padrão: 3)
# worktree_isolation: false # Dá a cada filho o próprio git worktree (veja Isolamento por worktree acima)
# max_spawn_depth: 1 # Profundidade da árvore (piso 1, sem teto, padrão 1 = plano). Suba para 2 para permitir que filhos orquestradores criem folhas; 3 ou mais para árvores mais profundas.
# orchestrator_enabled: true # Desligue para forçar todos os filhos ao papel de folha.
model: "google/gemini-3-flash-preview" # Sobrescrita opcional de provedor/modelo
provider: "openrouter" # Provedor embutido opcional
api_mode: anthropic_messages # opcional; detectado automaticamente pelo base_url em endpoints anthropic_messages
# Ou use um endpoint personalizado direto em vez de provider:
delegation:
model: "qwen2.5-coder"
base_url: "http://localhost:1234/v1"
api_key: "local-key"
# api_mode: "anthropic_messages" # Opcional. Sobrescrita de protocolo de fio para o base_url ("chat_completions", "codex_responses" ou "anthropic_messages"). Vazio = detecta pela URL (por exemplo, sufixo /anthropic). Defina explicitamente para endpoints que a heurística não consegue classificar (Azure AI Foundry, MiniMax, Zhipu GLM, proxies LiteLLM, …).
Quando o base_url aponta para um endpoint compatível com a Anthropic — por exemplo um caminho terminado em /anthropic, uma rota Claude do Azure Foundry ou um proxy /anthropic da MiniMax — o api_mode é detectado automaticamente como anthropic_messages, para que o subagente use o formato de fio certo sem você definir nada. Defina o api_mode explicitamente quando o palpite da detecção automática estiver errado (raro).
O agente cuida da delegação automaticamente, conforme a complexidade da tarefa. Você não precisa pedir explicitamente que ele delegue — ele vai fazer isso quando fizer sentido.