Busque Uma Vez, Pergunte Três: tool_search Multi-Query com Stemming

Seu agente precisa de uma tool — talvez criar um issue no GitHub, enviar uma mensagem no Slack e pesquisar na web, tudo na mesma tarefa. No sistema antigo, isso eram três chamadas separadas de tool_search, e se ele errasse a grafia de uma query (“issuse” em vez de “issues”) ou pesquisasse uma palavra que não casasse exatamente com o nome da tool, voltava vazio e tinha que tentar de novo. Cada erro custa tokens e latência. A v0.20.6 do Hermes resolve isso com uma atualização séria do tool_search (commit e455e4afd0): busca multi-query, describe em lote e stemming Snowball.
A mudança é pequena na forma e grande no comportamento: o tool_search agora aceita queries: string[] pesquisadas em paralelo contra o mesmo catálogo; os resultados voltam agrupados por query, com um único mapa compartilhado de tools contendo a source, a descrição e os nomes de parâmetros obrigatórios de cada tool encontrada. O tool_describe aceita names: string[] e retorna um mapa indexado por nome — então um nome errado não derruba mais a chamada inteira. E o stemming Snowball faz uma query por “issues” casar com uma tool chamada create_issue, e “browsing” encontrar browser_exec. Vamos ver o que mudou e por que importa.
O jeito antigo vs. o jeito novo
Antes: uma query por chamada, correspondência quase exata, fallback por resposta quando uma query errava:
tool_search("browser") → uma lista de correspondências
tool_search("web search") → outra chamada, outra lista
tool_search("read pdf") → uma terceira chamada
Depois: uma chamada, várias queries, resultados agrupados:
{ "queries": ["browser snapshot", "web search cache", "read pdf"] }
Cada query é pesquisada de forma independente contra o mesmo catálogo, o limite se aplica por query (padrão 5, limitado ao máximo configurado de 25), e a resposta agrupa os nomes de tools correspondentes por query enquanto um único mapa tools compartilhado carrega a source de cada tool, a descrição (limite de 400 caracteres) e os nomes de parâmetros obrigatórios — carregado uma vez, não repetido por query. Quando algumas queries erram, um único bloco de available_sources + hint no nível superior substitui o fallback antigo por resposta.
O schema, direto da definição da tool:
queries: array de strings, “cada uma com algumas keywords descrevendo uma capacidade (ex.:['create github issue', 'send slack message']). Pesquisadas em paralelo; os resultados voltam agrupados por query. Uma string única é aceita e tratada como uma query.”limit: “Número máximo de correspondências por query. O padrão é 5 e é limitado ao máximo configurado (25 por padrão).”
Stemming: casando o que você quer dizer, não o que você digitou
O herói silencioso dessa mudança é o stemming Snowball (inglês). O stemmer é aplicado de forma idêntica tanto no caminho de indexação (quando o catálogo é construído) quanto no caminho de query, então uma query por “issues” casa com uma tool chamada create_issue, “browsing” casa com browser_exec e “searches” casa com web_search. Você não precisa mais adivinhar a forma verbal exata ou a pluralização — o mecanismo normaliza os dois lados.
Detalhe de implementação que vale notar: instâncias do stemmer Snowball mantêm estado de parsing mutável, então não são seguras para compartilhar entre threads — e o dispatch da bridge pode rodar em threads paralelas de chamadas de tools. O Hermes cria um stemmer por thread, de forma lazy — uma correção pequena, mas real, de corretude escondida dentro do recurso.
tool_describe: nomes em lote, falha suave
O tool_describe recebe o mesmo tratamento: agora aceita names: string[] e retorna um mapa indexado por nome. Nomes desconhecidos se acumulam em not_found (com a dica de refresh), e nomes não adiáveis mantêm seu erro de verificação de grafia por nome em errors — um nome errado não derruba mais a chamada inteira. Duplicatas são deduplicadas silenciosamente. Então, depois de um tool_search multi-query, o agente pode descrever vários candidatos em uma chamada só, e um nome com erro de digitação custa uma nota, não uma nova tentativa.
O que isso significa na prática
Três ganhos concretos:
- Menos round-trips. Uma tarefa que precisa de três capacidades faz uma chamada de
tool_searchem vez de três, e depois umtool_describeem lote. Menos chamadas = menos latência e menos chances de o modelo perder o fio da meada. - Descoberta mais barata. O mapa compartilhado de tools significa que os metadados de cada tool encontrada são enviados uma vez, não repetidos por query — e no wire, as próprias definições de tools emagreceram junto com essa mudança (o trabalho mais amplo de schema-diet na mesma janela cortou o
browser_execde 803 para 663 tokens/chamada). A descoberta é uma das partes que mais consomem tokens numa execução longa de agente; isso a reduz. - Melhor recall. O stemming mata a classe de erro “quase, mas não exato”: “issues” →
create_issue, “browsing” →browser_exec, “searches” →web_search. O agente encontra a tool certa mesmo quando formula a query um pouco fora.
Por que importa para os seus workflows
A descoberta de tools é um encanamento invisível — você nunca a vê a menos que falhe, e quando falha, o agente ou se debate ou escolhe uma tool errada-mas-próxima. Multi-query + stemming + describe em lote remove a maior parte dessa superfície de falha. É o tipo de atualização que torna execuções autônomas longas (tarefas em lote, cron jobs, subtarefas delegadas) visivelmente menos frágeis: o agente precisa de uma tool de browser, uma de busca e um leitor de PDF para uma tarefa, e encontra as três de uma vez.
Para mais sobre as tools em si, veja nosso panorama de comandos para a superfície completa do que o tool_search pode encontrar, o orçamento de snapshot do browser para como sessões de browser continuam baratas, e o post sobre cache de busca web para a atualização irmã de cache na mesma janela. O resumo completo da v0.20.6 está nas nossas notas da release.
Uma chamada, três queries, com stemming, agrupadas e deduplicadas — o agente encontra a tool certa mais rápido e mais barato, e “não consegui encontrar uma tool para isso” fica um pouco mais raro a cada dia.