Se você tem código Spring AI que usa toolNames(), SpringBeanToolCallbackResolver ou depende do loop interno de tool execution dentro do ChatModel, ele vai quebrar quando o Spring AI 2.0 GA sair. O RC1 lançado em 06/06/2026 consolida todas as mudanças. Aqui está o guia completo com before/after para cada breaking change do Tool Calling Overhaul.
Nesse artigo você vai encontrar: o mapa completo dos breaking changes do Tool Calling no RC1, código before/after funcional para cada mudança, e a sequência correta para migrar sem quebrar seu agente em produção.
Por que o Tool Calling mudou tanto no 2.0
No Spring AI 1.x, cada ChatModel (OpenAI, Anthropic, Ollama, etc.) tinha um loop interno de execução de tools. Quando o LLM retornava uma tool call, o próprio ChatModel executava a ferramenta, pegava o resultado e mandava de volta ao modelo de forma automática e invisível.
O problema: comportamento inconsistente entre providers, sem ponto de extensão limpo para logging, rate limiting ou autorização. O Spring AI 2.0 corrigiu isso: o loop de execução saiu dos ChatModels e foi para o ToolCallingAdvisor. Mais controle, mais testabilidade — mas código que dependia do comportamento automático precisa ser atualizado.
Breaking Change 1: toolNames() foi removido
A API toolNames() e toolBeanDefinitionNames() foram removidas do ChatClient e de todas as implementações de ChatOptions. O SpringBeanToolCallbackResolver também foi removido.
// ANTES (Spring AI 1.x) - NÃO COMPILA no 2.0
ChatResponse response = chatClient.prompt()
.user("Temperatura em São Paulo?")
.options(OpenAiChatOptions.builder()
.toolNames("climaService") // removido
.build())
.call()
.chatResponse();
// DEPOIS (Spring AI 2.0) - ToolCallback explícito
@Component
public class ClimaToolCallback implements ToolCallback {
@Override
public String call(ToolRequest request) {
return "{"temperatura": 28, "cidade": "São Paulo"}";
}
@Override
public ToolDefinition getToolDefinition() {
return ToolDefinition.builder()
.name("climaService")
.description("Retorna temperatura atual de uma cidade")
.inputTypeSchema(/* JSON schema */)
.build();
}
}
// Uso:
ChatResponse response = chatClient.prompt()
.user("Temperatura em São Paulo?")
.tools(climaToolCallback) // instância explícita
.call()
.chatResponse();
Breaking Change 2: internalToolExecutionEnabled removido
// ANTES - NÃO COMPILA no 2.0
ChatOptions options = OpenAiChatOptions.builder()
.internalToolExecutionEnabled(false) // removido
.build();
// DEPOIS - controle via ToolCallingAdvisor
@Bean
public ChatClient chatClient(ChatModel chatModel,
ToolCallingAdvisor toolCallingAdvisor) {
return ChatClient.builder(chatModel)
.defaultAdvisors(toolCallingAdvisor)
.build();
}
Breaking Change 3: ToolCallingAdvisor é obrigatório
Sem o ToolCallingAdvisor, as tools não são executadas e não há erro — só silêncio. O agente retorna resposta vazia ou JSON bruto de tool call.
// ANTES (1.x) - execução automática no ChatModel
this.chatClient = chatClientBuilder.build();
// DEPOIS (2.0) - ToolCallingAdvisor obrigatório
this.chatClient = chatClientBuilder
.defaultAdvisors(toolCallingAdvisor) // obrigatório
.build();
// Configuração do bean:
@Bean
public ToolCallingAdvisor toolCallingAdvisor(ToolCallingManager toolCallingManager,
ObservationRegistry observationRegistry) {
return ToolCallingAdvisor.builder()
.toolCallingManager(toolCallingManager)
.observationRegistry(observationRegistry)
.build();
}
Breaking Change 4: ChatClientCustomizer depreciado
// ANTES (1.x) - deprecated no 2.0
@Bean
public ChatClientCustomizer toolsCustomizer(ToolCallingAdvisor advisor) {
return builder -> builder.defaultAdvisors(advisor);
}
// DEPOIS (2.0) - novo nome
@Bean
public ChatClientBuilderCustomizer toolsCustomizer(ToolCallingAdvisor advisor) {
return builder -> builder.defaultAdvisors(advisor);
}
Novo no RC1: ToolSearchToolCallingAdvisor
Quando você tem muitas tools, mandar todas as definições para o LLM a cada request aumenta custo e degrada qualidade. O novo ToolSearchToolCallingAdvisor indexa tools e carrega apenas as relevantes para cada query:
@Bean
public ToolSearchToolCallingAdvisor toolSearchAdvisor(
VectorStore toolVectorStore,
List<ToolCallback> allTools,
ToolCallingManager toolCallingManager) {
ToolIndex toolIndex = VectorStoreToolIndex.builder()
.vectorStore(toolVectorStore)
.tools(allTools)
.build();
return ToolSearchToolCallingAdvisor.builder()
.toolIndex(toolIndex)
.toolCallingManager(toolCallingManager)
.maxToolsPerRequest(5)
.build();
}
Sequência segura de migração
1. Identificar código afetado
grep -r "toolNames\|SpringBeanToolCallbackResolver\|internalToolExecutionEnabled\|ChatClientCustomizer" src/
2. Converter para ToolCallback explícito
Para cada tool resolvida por nome, criar implementação de ToolCallback e registrar via .tools().
3. Adicionar ToolCallingAdvisor ao ChatClient
Todo ChatClient que executa tools precisa do advisor. Sem ele: silêncio.
4. Renomear ChatClientCustomizer
Trocar ChatClientCustomizer por ChatClientBuilderCustomizer.
5. Rodar testes de integração reais
Testes unitários com mocks não detectam o problema do loop. Use WireMock mockando o provider para verificar que a tool foi chamada e o resultado devolvido ao modelo.
FAQ: Perguntas Frequentes
@Tool ainda funciona no 2.0?
A anotação continua suportada. O que mudou é o mecanismo de resolução. O método precisa ser empacotado em MethodToolCallback e passado explicitamente. O Spring AI fornece helpers para isso.
Quando sai a GA do Spring AI 2.0?
RC1 é um milestone de estabilização de API. Após RC1, apenas bug fixes. Dado o ritmo de releases, espere a GA em julho de 2026.
Vale migrar para RC1 em produção?
RC1 é estável para homologação. Para produção, aguardar a GA. Mas criar a branch de migração agora e estar pronto para o bump quando a GA sair é a estratégia correta.
ToolSearchToolCallingAdvisor requer vector store?
Existem três implementações de ToolIndex: VectorStoreToolIndex (semântico), LuceneToolIndex (textual, sem embedding) e RegexToolIndex (matching por nome). Para menos de 10 tools, o ToolCallingAdvisor padrão é mais simples.
A migração para Spring AI 2.0 não é opcional quando a GA sair. Com a sequência certa, é uma mudança de horas. Comenta aqui o breaking change que mais te preocupa no seu projeto.
Segue o Meu Universo Nerd para não perder o próximo conteúdo sobre Spring AI e Java.