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 API | Usado para | Tipo de cliente |
|---|---|---|
chat_completions | Endpoints compatíveis com OpenAI (OpenRouter, personalizados, a maioria dos provedores) | openai.OpenAI |
codex_responses | OpenAI Codex / API Responses | openai.OpenAI no formato Responses |
anthropic_messages | API Messages nativa da Anthropic | anthropic.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:
- Argumento explícito
api_modeno construtor (prioridade máxima) - Detecção específica de provedor (por exemplo, provedor
anthropic→anthropic_messages) - Heurísticas sobre a URL base (por exemplo,
api.anthropic.com→anthropic_messages) - 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
toolpode 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
- Exceção: ferramentas marcadas como interativas (por exemplo,
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():
| Ferramenta | Por que é interceptada |
|---|---|
todo | Lê/escreve o estado de tarefas local do agente |
memory | Escreve em arquivos de memória persistente com limite de caracteres |
session_search | Consulta o histórico de sessões no banco de sessões do agente |
delegate_task | Cria 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:
| Callback | Quando dispara | Usado por |
|---|---|---|
tool_progress_callback | Antes/depois de cada execução de ferramenta | Spinner da CLI, mensagens de progresso do gateway |
thinking_callback | Quando o modelo começa/para de pensar | Indicador "pensando..." da CLI |
reasoning_callback | Quando o modelo devolve conteúdo de raciocínio | Exibição de raciocínio na CLI, blocos de raciocínio no gateway |
clarify_callback | Quando a ferramenta clarify é chamada | Prompt de entrada da CLI, mensagem interativa do gateway |
step_callback | Depois de cada turno completo do agente | Rastreio de passos do gateway, progresso do ACP |
stream_delta_callback | A cada token do streaming (quando habilitado) | Exibição de streaming da CLI |
tool_gen_callback | Quando uma chamada de ferramenta é lida do stream | Prévia da ferramenta no spinner da CLI |
status_callback | Mudanç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):
- Consulta a lista
fallback_providersna configuração - Tenta cada reserva na ordem
- Se der certo, continua a conversa com o novo provedor
- 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
- A memória é descarregada em disco primeiro (para não perder dados)
- Os turnos do meio da conversa são resumidos em um sumário compacto
- As últimas N mensagens são preservadas intactas (
compression.protect_last_n, padrão: 20) - Os pares de mensagem chamada/resultado de ferramenta são mantidos juntos (nunca separados)
- 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
/resumeouhermes chat --resume
Arquivos-fonte principais
| Arquivo | Para que serve |
|---|---|
run_agent.py | Classe AIAgent — o laço completo do agente |
agent/prompt_builder.py | Montagem do prompt de sistema a partir de memória, skills, arquivos de contexto e personalidade |
agent/context_engine.py | ABC ContextEngine — gerenciamento de contexto plugável |
agent/context_compressor.py | Motor padrão — algoritmo de sumarização com perda |
agent/prompt_caching.py | Marcadores de cache de prompt da Anthropic e métricas de cache |
agent/auxiliary_client.py | Cliente de LLM auxiliar para tarefas laterais (visão, sumarização) |
model_tools.py | Coleta dos esquemas de ferramenta, despacho de handle_function_call() |
Documentos relacionados
- Resolução do runtime de provedores
- Montagem do prompt
- Compressão de contexto e cache de prompt
- Runtime de ferramentas
- Visão geral da arquitetura