Pular para o conteúdo principal

Documentação oficial desta página · v0.20.6

O laço do agente por dentro

O laço do agente por dentro

O motor central de orquestração é a classe AIAgent, em run_agent.py — um arquivo grande que cuida de tudo, da montagem do prompt ao despacho de ferramentas e à troca de provedor por falha.

Responsabilidades centrais​

A AIAgent é responsável por:

  • Montar o prompt de sistema efetivo e os esquemas de ferramenta via prompt_builder.py
  • Escolher o modo de provedor/API correto (chat_completions, codex_responses, anthropic_messages)
  • Fazer chamadas de modelo interrompíveis, com suporte a cancelamento
  • Executar chamadas de ferramenta (em sequência ou concorrentemente, por pool de threads)
  • Manter o histórico da conversa no formato de mensagem da OpenAI
  • Cuidar de compressão, novas tentativas e troca para o modelo de reserva
  • Acompanhar os orçamentos de iteração entre o agente pai e os filhos
  • Descarregar a memória persistente antes que o contexto se perca

Dois pontos de entrada​

# Interface simples — devolve a string da resposta final
response = agent.chat("Corrija o bug em main.py")

# Interface completa — devolve um dict com mensagens, metadados e estatísticas de uso
result = agent.run_conversation(
user_message="Corrija o bug em main.py",
system_message=None, # montado automaticamente se omitido
conversation_history=None, # carregado da sessão automaticamente se omitido
task_id="task_abc123"
)

chat() é um invólucro fino em volta de run_conversation() que extrai o campo final_response do dict de resultado.

Modos de API​

O Hermes suporta três modos de execução de API, resolvidos a partir da escolha de provedor, de argumentos explícitos e de heurísticas sobre a URL base:

Modo de APIUsado paraTipo de cliente
chat_completionsEndpoints compatíveis com OpenAI (OpenRouter, personalizados, a maioria dos provedores)openai.OpenAI
codex_responsesOpenAI Codex / API Responsesopenai.OpenAI no formato Responses
anthropic_messagesAPI Messages nativa da Anthropicanthropic.Anthropic via adaptador

O modo determina como as mensagens são formatadas, como as chamadas de ferramenta são estruturadas, como as respostas são interpretadas e como funcionam o cache e o streaming. Os três convergem para o mesmo formato interno de mensagem (dicts role/content/tool_calls no estilo OpenAI) antes e depois das chamadas de API.

Ordem de resolução do modo:

  1. Argumento explícito api_mode no construtor (prioridade máxima)
  2. Detecção específica de provedor (por exemplo, provedor anthropic → anthropic_messages)
  3. Heurísticas sobre a URL base (por exemplo, api.anthropic.com → anthropic_messages)
  4. Padrão: chat_completions

Ciclo de vida do turno​

Cada iteração do laço do agente segue esta sequência:

run_conversation()
1. Gera o task_id se ele não foi fornecido
2. Acrescenta a mensagem do usuário ao histórico da conversa
3. Monta ou reaproveita o prompt de sistema em cache (prompt_builder.py)
4. Verifica se é preciso comprimir de antemão (>50% do contexto)
5. Monta as mensagens de API a partir do histórico da conversa
- chat_completions: formato OpenAI como está
- codex_responses: converte para itens de entrada da API Responses
- anthropic_messages: converte via anthropic_adapter.py
6. Injeta camadas efêmeras de prompt (avisos de orçamento, pressão de contexto)
7. Aplica os marcadores de cache de prompt se estiver na Anthropic
8. Faz a chamada de API interrompível (_interruptible_api_call)
9. Interpreta a resposta:
- Se houver tool_calls: executa, acrescenta os resultados e volta ao passo 5
- Se for resposta de texto: persiste a sessão, descarrega a memória se preciso e retorna

Formato de mensagem​

Internamente, todas as mensagens usam o formato compatível com a OpenAI:

{"role": "system", "content": "..."}
{"role": "user", "content": "..."}
{"role": "assistant", "content": "...", "tool_calls": [...]}
{"role": "tool", "tool_call_id": "...", "content": "..."}

O conteúdo de raciocínio (de modelos que suportam pensamento estendido) fica guardado em assistant_msg["reasoning"] e pode, opcionalmente, ser exibido pelo reasoning_callback.

Regras de alternância de mensagens​

O laço do agente impõe alternância estrita de papéis nas mensagens:

  • Depois da mensagem de sistema: User → Assistant → User → Assistant → ...
  • Durante uma chamada de ferramenta: Assistant (com tool_calls) → Tool → Tool → ... → Assistant
  • Nunca duas mensagens de assistant seguidas
  • Nunca duas mensagens de user seguidas
  • Só o papel tool pode ter entradas consecutivas (resultados de ferramentas em paralelo)

Os provedores validam essas sequências e rejeitam históricos malformados.

Chamadas de API interrompíveis​

As requisições de API são envolvidas por _interruptible_api_call(), que roda a chamada HTTP de verdade em uma thread de fundo enquanto monitora um evento de interrupção:

┌────────────────────────────────────────────────────┐
│ Thread principal Thread da API │
│ │
│ espera por: HTTP POST │
│ - resposta pronta ───▶ para o provedor │
│ - evento de interrupção │
│ - tempo esgotado │
└────────────────────────────────────────────────────┘

Quando há interrupção (o usuário manda uma mensagem nova, usa o comando /stop ou envia um sinal):

  • A thread da API é abandonada (a resposta é descartada)
  • O agente pode processar a nova entrada ou encerrar de forma limpa
  • Nenhuma resposta parcial é injetada no histórico da conversa

Execução de ferramentas​

Sequencial ou concorrente​

Quando o modelo devolve chamadas de ferramenta:

  • Uma única chamada → executada direto na thread principal
  • Várias chamadas → executadas concorrentemente por um ThreadPoolExecutor
    • Exceção: ferramentas marcadas como interativas (por exemplo, clarify) forçam execução sequencial
    • Os resultados são reinseridos na ordem original das chamadas, não importa a ordem de conclusão

Fluxo de execução​

para cada tool_call em response.tool_calls:
1. Resolve o handler a partir de tools/registry.py
2. Dispara o hook de plugin pre_tool_call
3. Verifica se é comando perigoso (tools/approval.py)
- Se for perigoso: chama approval_callback e espera o usuário
4. Executa o handler com os args + task_id
5. Dispara o hook de plugin post_tool_call
6. Acrescenta {"role": "tool", "content": resultado} ao histórico

Ferramentas de nível de agente​

Algumas ferramentas são interceptadas pelo run_agent.py antes de chegar ao handle_function_call():

FerramentaPor que é interceptada
todoLê/escreve o estado de tarefas local do agente
memoryEscreve em arquivos de memória persistente com limite de caracteres
session_searchConsulta o histórico de sessões no banco de sessões do agente
delegate_taskCria subagente(s) com contexto isolado

Essas ferramentas alteram o estado do agente diretamente e devolvem resultados sintéticos de ferramenta sem passar pelo registro.

Superfícies de callback​

A AIAgent suporta callbacks específicos de plataforma, que permitem mostrar progresso em tempo real na CLI, no gateway e nas integrações ACP:

CallbackQuando disparaUsado por
tool_progress_callbackAntes/depois de cada execução de ferramentaSpinner da CLI, mensagens de progresso do gateway
thinking_callbackQuando o modelo começa/para de pensarIndicador "pensando..." da CLI
reasoning_callbackQuando o modelo devolve conteúdo de raciocínioExibição de raciocínio na CLI, blocos de raciocínio no gateway
clarify_callbackQuando a ferramenta clarify é chamadaPrompt de entrada da CLI, mensagem interativa do gateway
step_callbackDepois de cada turno completo do agenteRastreio de passos do gateway, progresso do ACP
stream_delta_callbackA cada token do streaming (quando habilitado)Exibição de streaming da CLI
tool_gen_callbackQuando uma chamada de ferramenta é lida do streamPrévia da ferramenta no spinner da CLI
status_callbackMudanças de estado (pensando, executando etc.)Atualizações de status do ACP

Comportamento de orçamento e de reserva​

Orçamento de iterações​

O agente acompanha as iterações pelo IterationBudget:

  • Padrão: 500 iterações (configurável em agent.max_turns)
  • Cada agente tem o próprio orçamento. Subagentes recebem orçamentos independentes, limitados por delegation.max_iterations (padrão 50) — o total de iterações somando pai + subagentes pode passar do limite do pai
  • Em 100%, o agente para e devolve um resumo do trabalho feito

Modelo de reserva​

Quando o modelo principal falha (429 de limite de taxa, erro 5xx do servidor, erro 401/403 de autenticação):

  1. Consulta a lista fallback_providers na configuração
  2. Tenta cada reserva na ordem
  3. Se der certo, continua a conversa com o novo provedor
  4. Em 401/403, tenta renovar a credencial antes de trocar de provedor

O sistema de reserva também cobre as tarefas auxiliares de forma independente — visão, compressão e extração web têm cada uma a sua própria cadeia de reserva, configurável na seção auxiliary.* da configuração.

Compressão e persistência​

Quando a compressão dispara​

  • De antemão (antes da chamada de API): se a conversa passa de 50% da janela de contexto do modelo
  • Compressão automática do gateway: se a conversa passa de 85% (mais agressiva, roda entre turnos)

O que acontece durante a compressão​

  1. A memória é descarregada em disco primeiro (para não perder dados)
  2. Os turnos do meio da conversa são resumidos em um sumário compacto
  3. As últimas N mensagens são preservadas intactas (compression.protect_last_n, padrão: 20)
  4. Os pares de mensagem chamada/resultado de ferramenta são mantidos juntos (nunca separados)
  5. Um novo ID de linhagem de sessão é gerado (a compressão cria uma sessão "filha")

Persistência da sessão​

Depois de cada turno:

  • As mensagens são salvas no armazenamento de sessões (SQLite, via hermes_state.py)
  • As mudanças de memória são descarregadas em MEMORY.md / USER.md
  • A sessão pode ser retomada depois com /resume ou hermes chat --resume

Arquivos-fonte principais​

ArquivoPara que serve
run_agent.pyClasse AIAgent — o laço completo do agente
agent/prompt_builder.pyMontagem do prompt de sistema a partir de memória, skills, arquivos de contexto e personalidade
agent/context_engine.pyABC ContextEngine — gerenciamento de contexto plugável
agent/context_compressor.pyMotor padrão — algoritmo de sumarização com perda
agent/prompt_caching.pyMarcadores de cache de prompt da Anthropic e métricas de cache
agent/auxiliary_client.pyCliente de LLM auxiliar para tarefas laterais (visão, sumarização)
model_tools.pyColeta dos esquemas de ferramenta, despacho de handle_function_call()