Você já paga por um pensamento que não precisa: reasoning effort em todo seletor de modelo do Hermes


São 23h30 e você pede ao Hermes para renomear formatTs para formatTimestamp em utils/date.ts. Ele termina em sete segundos — e o trace cobra alguns milhares de tokens de pensamento em ultra reasoning effort. O modelo não é o problema. “Ultra” é um default global que você definiu na semana passada para uma revisão de arquitetura, e ninguém tocou nele desde então. O Hermes sempre deixou você ajustar o reasoning effort; os controles é que estavam espalhados entre /reasoning, config.yaml e um submenu do Desktop que só aparecia no hover. Saber que o recurso existe e realmente usá-lo eram dois grupos distintos de pessoas.

Isso mudou com a PR #110346: todo seletor de modelo agora pede o nível de effort logo depois que você escolhe o modelo. Este guia percorre os seis lugares onde você pode definir o effort (execução avulsa, sessão, o flag do /model, os seletores interativos, hermes model e o Desktop), além da precedência entre eles — para que você pague pelo pensamento que uma tarefa exige, não pelo pensamento que você configurou na semana passada.

O que o reasoning effort controla de fato

Reasoning effort é o orçamento de pensamento que um modelo tem permissão de gastar antes de responder. Para modelos que expõem o controle, o nível é traduzido nos parâmetros de thinking/reasoning da requisição; para modelos que não expõem, a configuração é simplesmente inerte.

Existe exatamente uma fonte de verdade para a escada, em hermes_constants.py:

VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh", "max", "ultra")

Sete níveis, do mais baixo ao mais alto. parse_reasoning_effort() transforma uma palavra digitada em um dict de config: um nível reconhecido vira {"enabled": True, "effort": "<level>"}, uma string vazia ou uma palavra não reconhecida retorna None (ou seja, “use o default do provider”), e none é um desligamento explícito — reasoning_effort: false e none significam ambos “não pense”.

O ponto importante: isso vale por requisição. No mesmo modelo, a diferença de qualidade entre low e ultra costuma ser bem menor que a diferença de latência e custo. Então a pergunta real nunca é “qual nível é o melhor” — é “quantos níveis esta tarefa merece”.

Escopo Como você define Onde isso fica
Apenas uma execução hermes --reasoning low -z "..." aquele processo, nada gravado
Sessão atual (CLI clássico) /reasoning high override da sessão
Sessão atual, com troca de modelo /model X --reasoning high pin da sessão
Seu default global /reasoning high --global agent.reasoning_effort
Todo modelo que você nomear agent.reasoning_overrides por modelo, vence o default global
Uma tarefa auxiliar hermes model → Configure auxiliary models… auxiliary.vision.reasoning_effort
Todo subagent delegation.reasoning_effort o bloco delegation
Um chat no Desktop a pill de reasoning do composer aquela sessão

A alavanca menor: execuções one-shot

Se você está rodando um único comando para um trabalho bem compreendido, não precisa mexer em config nenhuma. O flag --reasoning de nível superior existe exatamente para isso:

# One-shot: um rename. Low dá e sobra.
hermes --reasoning low -z "rename formatTs to formatTimestamp in utils/date.ts"

# Interativo, mas esta sessão está arrumando anotações bagunçadas
hermes chat --reasoning high

O texto de ajuda é explícito: isso sobrescreve o effort efetivo apenas para aquela execução; o nível persistente continua em config.yaml (ou por modelo, em agent.reasoning_overrides). Para scripts shell, jobs de CI e trabalho em lote com arquivos, é a opção mais limpa: o effort viaja junto com o comando em vez de virar discretamente o default de amanhã.

Sessões: /reasoning e /model --reasoning

No CLI clássico, /reasoning é o comando dedicado — ele cuida tanto do nível de effort quanto de como o reasoning é exibido:

/reasoning high           # apenas esta sessão
/reasoning high --global  # grava agent.reasoning_effort para execuções futuras
/reasoning                # sem argumento: escolher interativamente
/reasoning show           # mostrar/ocultar o stream de reasoning (full imprime tudo)
/reasoning none           # desliga o reasoning

A PR #110346 fez da troca de modelo e da definição de effort um único gesto. O parser do /model agora aceita --reasoning <level>, então uma só requisição carrega a escolha do modelo e o effort dele:

/model openrouter/anthropic/claude-opus-4.8 --reasoning high

Três detalhes que aparecem o tempo todo:

  • O escopo acompanha a troca de modelo. Sessão por padrão; --global grava agent.reasoning_effort; --once captura o valor atual, aplica o effort por um único turno e o restaura depois.
  • O effort é aplicado depois da troca de agent. Isso não é detalhe bobo: o caminho da troca resolve o reasoning_config de novo a partir do config.yaml, então gravar o effort primeiro seria sobrescrito. A ordem é deliberada.
  • Um nível inválido gera erro em vez de não fazer nada em silêncio. /model --reasoning turbo retorna /model --reasoning takes none, minimal, low, medium, high, xhigh, max or ultra.

Você também pode dispensar os flags por completo. O seletor interativo do /model agora tem três etapas — provider, modelo e uma terceira etapa intitulada Reasoning effort for <model> que lista a escada inteira, mais uma linha none (desabilitar o reasoning) e uma linha “manter o atual”. Quando o mapa de capacidades da rota diz que não há controle de reasoning, essa terceira etapa fica oculta em vez de oferecer um botão que não faz nada.

A TUI Ink (hermes --tui) roda o mesmo fluxo de três etapas; a etapa 3 é rotulada Reasoning effort (step 3/3) e emite modelo, provider, effort e escopo como um único comando de gateway. Em plataformas de mensageria (Telegram, Slack e companhia), /model --reasoning passa pelo mesmo aplicador que o /reasoning usa, então os dois não conseguem divergir.

hermes model: o passo de effort para o modelo principal e cada tarefa auxiliar

hermes model é o ponto de entrada que a maioria das pessoas usa primeiro (veja a referência do hermes model). Antes da PR #110346, ele perguntava sobre effort apenas em rotas Copilot, e o fluxo de auxiliares nunca perguntava nada.

Agora:

  • Depois que o modelo principal é salvo, um passo compartilhado vem em seguida: os sete níveis, Disable reasoning e Skip para manter o valor atual. Modelos que o catálogo marca como sem controle de reasoning pulam essa etapa.
  • A lista de providers ganhou uma linha Reasoning effort for the current model… para quando você quer mudar apenas o effort.
  • Configure auxiliary models… (provider → modelo) termina com o mesmo passo de effort, mais uma linha Provider default que deixa o nível a cargo do provider. Esses valores são guardados como auxiliary.<task>.reasoning_effort, ou delegation.reasoning_effort no caso da delegação.
  • A lista de tarefas mostra o nível ali mesmo, por exemplo openrouter · model · high, e Reset all to auto limpa todos os overrides de uma vez.
  • Blocos que deliberadamente não têm essa chave — os slots do MoA e memory_query_rewrite — pulam a etapa. Não faz falta: eles apenas configuram profundidade em outro lugar.

Por que isso importa mais do que parece: compressão, geração de título e checagens de aprovação rodam em todo turno. Baixar essas tarefas de high para minimal (ou desligá-las) economiza mais dinheiro do que ajustar o nível do modelo principal jamais vai economizar.

Desktop: a pill do composer e o effort por tarefa em Settings

O seletor de modelo do Desktop fica no composer, logo à esquerda do microfone. Antes da PR #109907, o único controle de effort era um submenu no hover dentro de uma linha de modelo; a pill combinada exibia Deepseek V4 Flash · Med, os usuários liam aquilo como “este modelo só faz medium”, e o submenu continuava sem ser descoberto.

Agora o effort tem seu próprio affordance:

  • Uma pill de reasoning ao lado da pill de modelo mostra o nível realmente em vigor nesta superfície (primeiro o valor da sessão, senão o default da config) e abre diretamente as linhas Thinking / Fast / Effort. Ela se oculta quando o catálogo reporta reasoning: false para o modelo ativo, e permanece visível enquanto as capacidades ainda são desconhecidas, para nunca piscar durante o fetch do catálogo.
  • A pill de modelo não dobra mais o effort no próprio rótulo; cada controle diz uma coisa só.

O effort por tarefa do lado do Desktop precisou de mais duas PRs. A #110435 adicionou um select Reasoning em cada linha em Settings → Model → Auxiliary models: inherit · main model effort (o default, que remove o override daquela tarefa), Off, ou um nível — gravando a mesma chave auxiliary.<task>.reasoning_effort que o hermes model grava. O contrato de API por trás disso vale conhecer: omitir o campo deixa o override como está, passar um null explícito o limpa (volta a herdar), passar um nível o define. Um valor desconhecido como turbo retorna um 400 listando todos os níveis aceitos, em vez de ser jogado no lixo.

A PR #110535 fechou o ciclo dos pins de auxiliares no Desktop: todos os 11 slots podem receber pin, salvar configurações não relacionadas não reescreve nem derruba mais esses pins (as escritas de config passaram a ser por seção, em vez de gravar de volta no disco um snapshot totalmente expandido), e quando uma tarefa de revisão é encaminhada para outro modelo, o reasoning effort dela é respeitado em vez de ser engolido pela configuração da sessão pai.

Uma pegadinha: o app Desktop lembra localmente um preset de effort/fast por modelo. Isso é uma conveniência do app e não muda crons nem subagents — para esses, use hermes model ou o arquivo de config.

Como as camadas se empilham e onde model_overrides se encaixa

Precedência, do mais forte para o mais fraco (é também a ordem de resolução em runtime): override de sessão (/reasoning --session, /model --reasoning) → override por modelo agent.reasoning_overrides → default global agent.reasoning_effort. Um config.yaml para copiar:

agent:
  # O default global; é a linha que `/reasoning high --global` grava
  reasoning_effort: medium
  # Por modelo: as chaves toleram variação de escrita (exata, pontos/traços
  # intercambiáveis, prefixo de provider opcional), a primeira correspondência vence,
  # e ela bate o default acima.
  # Não há suporte a `hermes config set` para esta chave — edite o YAML direto.
  reasoning_overrides:
    "openrouter/anthropic/claude-opus-4.8": high
    "deepseek/deepseek-v4-flash": low

auxiliary:
  compression:
    reasoning_effort: minimal   # resumir histórico não exige pensamento profundo
  title_generation:
    reasoning_effort: none      # títulos: simplesmente desligue
  vision:
    reasoning_effort: ""        # vazio = default do provider

delegation:
  reasoning_effort: low         # subagents; deixe vazio para herdar do pai

model_overrides tem outro papel aqui (veja o guia de model_overrides): é onde vivem os metadados de modelo — context_window, supports_vision, supports_reasoning e afins. Ele não define o effort, mas decide se o controle de effort existe: quando o mapa de capacidades diz que uma rota não tem controle de reasoning, o passo do hermes model, a terceira etapa do seletor /model e a pill do Desktop se ocultam todos juntos. Então, se você está em um modelo local ou novíssimo que o catálogo nunca ouviu falar, declare as capacidades primeiro e os controles de effort vão aparecer.

Duas exceções que vale internalizar:

  • background_review.reasoning_effort é ignorado enquanto a revisão fica no modelo principal. Nesse caso o fork herda a config de reasoning da conversa palavra por palavra, que é o que mantém os bytes da requisição dele no prefixo de prompt-cache do pai. Para ter um modelo e um effort diferentes, defina também o provider/model da revisão — caso contrário, o Hermes te avisa uma vez, de propósito.
  • O effort do MoA é configurado por slot dentro do preset, não no bloco auxiliary: moa.presets.<name>.reference_models[].reasoning_effort e aggregator.reasoning_effort. Cada referência pode rodar na própria profundidade e o aggregator pode ir mais fundo — é por isso que os blocos do MoA não têm chave reasoning_effort, por design.

Uma configuração inicial que se paga sozinha

Se você prefere não guardar toda a cadeia de precedência na cabeça, faça estas cinco coisas:

  1. Deixe o modelo principal em medium. Suba o nível para uma tarefa específica com /model ... --reasoning high em vez de editar o default global.
  2. Rode hermes model → Configure auxiliary models… e coloque compression em minimal e title_generation desligada. Elas rodam a cada turno, então se pagam imediatamente.
  3. Defina delegation.reasoning_effort: low. Subagents normalmente executam uma tarefa que já foi decomposta; eles não precisam replanejá-la.
  4. Use hermes --reasoning low -z "..." para scripts e trabalhos one-shot, para que uma necessidade temporária nunca vire um default permanente.
  5. Se uma execução longa empacar, verifique o orçamento de turnos antes de recorrer a mais pensamento — é sobre isso o guia de max_turns. E em rotas de plano gratuito, a configuração sem API key combina bem com minimal.

A versão curta

Reasoning effort não é um botão de ajuste em que “mais alto é melhor”; é um problema de escopo. Em um único config.yaml você tem botões separados para a sessão, por modelo, por tarefa auxiliar e por subagent. A PR #110346 levou esses botões para os seletores que você já usa — o que resta é decidir quantos níveis cada trabalho merece, para que aquela revisão do mês passado pare de pagar tarifa ultra por um rename deste mês.