Когда количество доступных LLM инструментов (tool-ов) разрастается, традиционные подходы к tool calling становятся непрактичными — утилизация токенов улетает ещё до начала общения. К тому же, модели становится сложнее выбрать нужный набор tool-ов для решения проблемы.
В новом переводе от команды Spring АйО читаем о паттерне Tool Search Tool, предложенном Anthropic и реализованном в Spring AI с помощью ToolSearchToolCallAdvisor. Он позволяет LLM динамически находить нужные инструменты по мере необходимости, экономя до 64% токенов и повышая точность.
По мере того как AI-агенты подключаются к всё большему числу сервисов — Slack, GitHub, Jira, MCP-серверы — библиотеки инструментов стремительно разрастаются. В типичной конфигурации с несколькими серверами может насчитываться более 50 инструментов, потребляющих свыше 55 000 токенов до начала самого диалога. Ещё хуже то, что точность выбора инструмента снижается, когда модель сталкивается с 30+ инструментами с похожими названиями.
Комментарий от Михаила Поливаха
Здесь наверное для вообще общего понимания статьи нужно сделать пару пояснений.
Довольно часто AI Agent-ы взаимодействуют с какими-либо внешними системами, и всё чаще это происходит через MCP протокол. У какого-то ресурса есть MCP сервер, который может быть написать с помощью Spring AI или ещё чего угодно, и MCP client запрашивает этот MCP server, где последний представляет собой некоторый прокси к этому самому ресурсу. MCP server знает
- О том, что может выполнять ресурс
- О том, какие данные он может возвращать
и т.д.
Проблема в том, что с популяризацией MCP протокола и AI Agent-ов поулчилось так, что абстрактный AI Agent рабоатет с огромным количеством MVP серверов, например с MCP серверами для:
- Мессенджера
- Почты
- Файловой системы
- Менеджеру пакетов (например npm) и т.д.
И каждый MVP сервер предлагает оргомное количество функционала. Чаще всего, MCP хост не задумывается, и просто все тулы, что предлагает каждый MVP Server запихивает в контекст LLM.
Как Вы можете догадаться, это вполне себе может привести (и приводит!) к огромному потреблению токенов и, соответственно, сильно удоражает пользование AI Агентами. Это проблема. Её решают.
Эту проблему решает паттерн Tool Search Tool, предложенный компанией Anthropic: вместо загрузки всех определений инструментов заранее, модель обнаруживает нужные инструменты по запросу. Изначально ей предоставляется только инструмент поиска, с помощью которого она запрашивает доступные возможности по мере необходимости, и получает в контекст только соответствующие определения. Это обеспечивает значительную экономию токенов при сохранении доступа к сотням инструментов.
Ключевая идея: хотя этот подход был впервые реализован в Claude от Anthropic, мы можем применить ту же концепцию для любой LLM-модели с помощью Recursive Advisors в Spring AI. Spring AI предоставляет переносимую абстракцию, делающую возможным динамическое обнаружение инструментов для OpenAI, Anthropic, Gemini, Ollama, Azure OpenAI и любых других провайдеров LLM, поддерживаемых Spring AI.
Наши предварительные тесты показывают, что реализация паттерна Tool Search Tool в Spring AI обеспечивает сокращение числа токенов на 34–64% при использовании моделей OpenAI, Anthropic и Gemini, сохраняя при этом полный доступ к сотням инструментов.
Проект Spring AI Tool Search Tool доступен по адресу: spring-ai-tool-search-tool.
Как работает вызов инструментов
Сначала разберёмся, как работает вызов инструментов (он же tool calling) в Spring AI при использовании ToolCallAdvisor — специального рекурсивного советника, который:

Перехватывает запрос
ChatClientдо того, как он попадёт в LLMВключает определения инструментов (т.е. этих самых tool-ов) в промпт, отправляемый модели — для всех зарегистрированных инструментов!
Обнаруживает запросы на вызов инструментов в ответе модели
Выполняет запрошенные инструменты с помощью
ToolCallingManagerПовторно обращается к модели с результатами вызова инструментов до тех пор, пока не будет получен окончательный ответ
Вызов инструментов происходит в рекурсивном цикле — советник (advisor) продолжает обращаться к LLM до тех пор, пока не прекратятся запросы на использование инструментов.
Проблема
Стандартный механизм вызова инструментов (например, ToolCallAdvisor) отправляет все определения инструментов в LLM заранее. Это приводит к трём серьёзным проблемам при работе с большими наборами инструментов:
Раздувание контекста — огромное потребление токенов ещё до начала диалога
Путаница в инструментах — модели сложно выбрать правильный инструмент при наличии 30+ схожих
Повышенные затраты — вы платите за передачу определений инструментов, даже если они не используются в запросе
Решение: Tool Search Tool
Расширив ToolCallAdvisor из Spring AI, мы создали ToolSearchToolCallAdvisor, который реализует динамическое обнаружение инструментов. Он перехватывает цикл вызова инструментов и выборочно подставляет определения только тех инструментов, которые модель определяет как нужные:

Процесс работает следующим образом:
Индексация: в начале диалога все зарегистрированные инструменты индексируются в ToolSearcher (но не отправляются в LLM)
Первичный запрос: в LLM отправляется только определение Tool Search Tool (TST) — экономия токенов в контексте
Запрос на обнаружение: когда LLM нужны определённые возможности, она вызывает TST с поисковым запросом
Поиск и расширение:
ToolSearcherнаходит подходящие инструменты (например, «Tool XYZ»), и их определения добавляются в следующий запросВызов инструмента: теперь LLM видит как TST, так и определения найденных инструментов и может вызвать нужный
Выполнение инструмента: найденный инструмент выполняется, а результат передаётся обратно в LLM
Ответ: LLM генерирует окончательный ответ, используя результаты выполнения инструментов
В коде это выглядит следующим образом:
var toolSearchToolCallAdvisor = ToolSearchToolCallAdvisor.builder()
.toolSearcher(toolSearcher)
.maxResults(5)
.build();
ChatClient chatClient = chatClientBuilder
.defaultTools(new MyTools()) // 100s of tools registered but NOT sent to LLM initially
.defaultAdvisors(toolSearchToolCallAdvisor) // Activate Tool Search Tool
.build();
Плагиновые стратегии поиска
Интерфейс ToolSearcher абстрагирует реализацию поиска, поддерживая несколько стратегий (см. tool-searchers для готовых реализаций):
Стратегия |
Реализация |
Оптимально для |
Semantic |
|
Запросы на естественном языке, нечеткое соответствие |
Keyword |
|
Точное совпадение терминов, известные названия инструментов |
Regex |
|
Шаблоны названий инструментов (например, get_*_data) |
Начало работы
Репозиторий проекта на GitHub: spring-ai-tool-search-tool.
Подробные инструкции по настройке и примеры кода доступны в руководстве Quick Start (v1.x), а также в сопутствующем примере приложения (v1.x).
Maven Central (1.0.1):
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>tool-search-tool</artifactId>
<version>1.0.1</version>
</dependency>
<!-- Choose a search strategy -->
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>tool-searcher-lucene</artifactId>
<version>1.0.1</version>
</dependency>
Версия v1.0.x совместима с Spring AI 1.1.x и Spring Boot 3, а версия v2.0.x — с Spring AI 2.x и Spring Boot 4.
Комментарий от Михаила Поливаха
Здесь частично речь про будущую совместимость - Spring AI 2.x ещё не вышел и пока непонятно когда выйдет.
Пример использования
@SpringBootApplication
public class Application {
@Bean
CommandLineRunner demo(ChatClient.Builder builder, ToolSearcher toolSearcher) {
return args -> {
var advisor = ToolSearchToolCallAdvisor.builder()
.toolSearcher(toolSearcher)
.build();
ChatClient chatClient = builder
.defaultTools(new MyTools())
.defaultAdvisors(advisor)
.build();
var answer = chatClient.prompt("""
Help me plan what to wear today in Amsterdam.
Please suggest clothing shops that are open right now.
""").call().content();
System.out.println(answer);
};
}
static class MyTools {
@Tool(description = "Get the weather for a given location at a given time")
public String weather(String location,
@ToolParam(description = "YYYY-MM-DDTHH:mm") String atTime) {...}
@Tool(description = "Get clothing shop names for a given location at a given time")
public List<String> clothing(String location,
@ToolParam(description = "YYYY-MM-DDTHH:mm") String openAtTime) {...}
@Tool(description = "Current date and time for a given location")
public String currentTime(String location) {...}
// ... potentially hundreds more tools
}
}
Для приведённого выше примера поток выполнения будет следующим:
Запрос пользователя:
«Помоги мне решить, что надеть сегодня в Амстердаме. Порекомендуй магазины одежды, которые сейчас открыты.»Инициализация:
Индексируются все инструменты: weather, clothing, currentTime (+ потенциально ещё сотни)Первый вызов LLM — модель видит только toolSearchTool
→ LLM вызывает: toolSearchTool(query="current time date") → ["currentTime"]Второй вызов LLM — модель видит toolSearchTool + currentTime
→ LLM вызывает: currentTime("Amsterdam") → "2025-12-08T11:30"
→ затем: toolSearchTool(query="weather location") → ["weather"]Третий вызов LLM — модель видит toolSearchTool + currentTime + weather
→ LLM вызывает: weather("Amsterdam") → "Ясно, 15°C"
→ затем: toolSearchTool(query="clothing shops") → ["clothing"]Четвёртый вызов LLM — модель видит toolSearchTool + currentTime + weather + clothing
→ LLM вызывает: clothing("Amsterdam", "2025-12-08T11:30") → ["H&M", "Zara", "Uniqlo"]Финальный ответ:
«С учётом солнечной погоды 15°C в Амстердаме, рекомендую надеть лёгкие слои. Вот магазины одежды, которые сейчас открыты: H&M, Zara, Uniqlo…»
Измерения производительности
⚠️ Предупреждение: это предварительные, ручные измерения, полученные после нескольких запусков. Они не усреднены по множеству итераций и должны рассматриваться как иллюстративные, а не как статистически точные.
Для оценки экономии токенов мы провели предварительные тесты с демонстрационным приложением и следующей конфигурацией:
Задача:
«Помоги мне решить, что надеть сегодня в Амстердаме. Порекомендуй магазины одежды, которые сейчас открыты.»Набор инструментов:
Всего 28 инструментов:
• 3 релевантных — weather, clothing, currentTime
• 25 нерелевантных «заглушек» — специально добавлены, чтобы показать, как эффективно поиск отбирает только нужные инструменты среди множества нерелевантныхСтратегии поиска:
• Lucene (на основе ключевых слов)
• VectorStore (семантический поиск)Тестируемые модели:
• Gemini (gemini-3-pro-preview)
• OpenAI (gpt-5-mini-2025-08-07)
• Anthropic (claude-sonnet-4-5-20250929)
Измерения проводились с использованием специального TokenCounterAdvisor, отслеживающего и агрегирующего использование токенов.
Результаты с использованием Lucene-поиска
Модель |
Подход |
Всего токенов |
Токены запроса |
Токены ответа |
Запросов |
Экономия |
Gemini |
С TST |
2 165 |
1 412 |
231 |
4 |
60% |
Без TST |
5 375 |
4 800 |
176 |
3 |
— |
|
OpenAI |
С TST |
4 706 |
2 770 |
1 936 |
5 |
34% |
Без TST |
7 175 |
5 765 |
1 410 |
3 |
— |
|
Anthropic |
С TST |
6 273 |
5 638 |
635 |
5 |
64% |
Без TST |
17 342 |
16 752 |
590 |
4 |
— |
Результаты с использованием VectorStore-поиска
Модель |
Подход |
Всего токенов |
Токены запроса |
Токены ответа |
Запросов |
Экономия |
Gemini |
С TST |
2 214 |
1 502 |
234 |
4 |
57% |
Без TST |
5 122 |
4 767 |
73 |
3 |
— |
|
OpenAI |
С TST |
3 697 |
2 109 |
1 588 |
4 |
47% |
Без TST |
6 959 |
5 771 |
1 188 |
3 |
— |
|
Anthropic |
С TST |
6 319 |
5 642 |
677 |
5 |
63% |
Без TST |
17 291 |
16 744 |
547 |
4 |
— |
Ключевые наблюдения
Значительная экономия токенов во всех моделях:
Паттерн Tool Search Tool обеспечил снижение общего потребления токенов на 34–64% в зависимости от модели и стратегии поиска.Основной фактор — токены запроса:
Экономия достигается в первую очередь за счёт сокращения токенов промпта — с TST в контекст подставляются только обнаруженные инструменты, а не все 28 сразу.Компромисс: больше запросов, меньше токенов:
Подход с TST требует 4–5 запросов против 3–4 без него, но суммарная стоимость токенов значительно ниже.
Комментарий от Евгения Сулейманова
Очень важное утверждение о том, что главный "налог" tool-calling - это не сами вызовы, а раздувание промпта всеми схемами инструментов, и TST логично режет его за счет lazy-discovery, параллельно улучшая выбор среди однотипных tools. Но это не бесплатная магия: вы меняете "один жирный запрос" на 4–5 тонких, поэтому важно мерить не только токены, а end-to-end стоимость (latency + число round-trip-ов) и добавить кэш результатов поиска/подобранных инструментов между итерациями. Качество будет упираться в качество метаданных инструментов: стабильные имена/ID, хороший description с синонимами, явные входы/выходы, плюс фильтрация "опасных"/дорогих инструментов на уровне searcher.
Для прода я бы добавил guardrails: fallback на "полный список" при провале поиска, лимиты на расширение контекста, и A/B-валидацию точности выбора инструмента (не только успешность ответа). В целом это правильный шаг к масштабируемым агентам в MCP-мире, и ценность Spring AI здесь именно в переносимой рекурсивной архитектуре advisor-ов, которую можно докручивать под разные модели и качества tool-библиотек.
Обе стратегии поиска эффективны:
Lucene и VectorStore показали сравнимую производительность, при этом VectorStore дал немного лучшую эффективность для OpenAI в данном тесте.Все модели корректно завершили задачу:
Все три модели (Gemini, OpenAI, Anthropic) поняли, что нужно сначала вызвать currentTime, прежде чем обращаться к другим инструментам — это демонстрирует корректное понимание зависимостей между инструментами.Разные стратегии обнаружения:
Модели продемонстрировали различные подходы: некоторые запрашивали все нужные инструменты сразу, другие — по одному. Тем не менее, все они использовали параллельный вызов инструментов, где это было возможно, чтобы оптимизировать выполнение.-
Старые модели могут испытывать трудности:
Более ранние версии моделей могут не справляться с паттерном поиска инструментов: пропускать нужные инструменты или принимать неэффективные решения. В таких случаях рекомендуется:Добавить systemMessageSuffix для дополнительной инструктивности
Попробовать разные конфигурации tool-searcher
Использовать паттерн LLM as Judge для обеспечения надёжного поведения на разных моделях
Когда использовать
Подход Tool Search Tool |
Традиционный подход |
Более 20 инструментов в системе |
Небольшая библиотека инструментов (<20) |
Определения инструментов занимают более 5 000 токенов |
Все инструменты регулярно используются в каждом сеансе |
Разработка MCP-систем с несколькими серверами |
Очень компактные определения инструментов |
Наблюдаются проблемы с точностью выбора инструментов |
— |
Заключение
Паттерн Tool Search Tool — это шаг к созданию масштабируемых AI-агентов. Объединяя инновационный подход Anthropic с переносимой абстракцией Spring AI, мы можем строить системы, которые эффективно управляют тысячами инструментов и при этом сохраняют высокую точность вне зависимости от провайдера LLM.
Сила рекурсивной архитектуры советников Spring AI заключается в том, что она позволяет реализовывать сложные сценарии обнаружения инструментов, которые работают универсально — будь то модели GPT от OpenAI, Claude от Anthropic, локальные модели Ollama или любой другой LLM, поддерживаемый Spring AI. Вы получаете все преимущества динамического обнаружения инструментов — без привязки к конкретной реализации или провайдеру.

Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.