主题
用工具搜索工具做动态工具发现
随着 AI 智能体连接到更多服务——Slack、GitHub、Jira、MCP 服务端——工具库会迅速增长。一个典型的多服务端部署很容易有 50+ 工具,在任何对话开始前就消耗大量令牌。更糟的是,当模型面对 30+ 个名字相似的工具时,工具选择的准确性会下降。
工具搜索工具 模式——由 Anthropic 首创——解决了这一点:模型不再预先加载所有工具定义,而是按需发现工具。它起初只收到一个搜索工具,在需要时查询能力,并把相关的工具定义展开进上下文。
Spring AI 的实现跨 OpenAI、Anthropic 和 Gemini 模型达成 34-64% 的令牌缩减,同时保持对数百个工具的完全访问。
引言
工具搜索工具扩展了 Spring AI 的 递归顾问,以实现跨 Spring AI 支持的 任何 LLM 服务商 都能工作的动态工具发现。
主要好处:
- 令牌节省 - 只有被发现的工具定义才发给 LLM
- 更高的准确性 - 模型从更小、相关的集合中更可靠地选择工具
- 可扩展性 - 管理数百个工具而上下文不膨胀
- 可移植性 - 适用于 OpenAI、Anthropic、Gemini、Ollama 等
博客文章
📖 完整教程: Smart Tool Selection: Achieving 34-64% Token Savings with Spring AI's Dynamic Tool Discovery
该博客文章涵盖完整的实现细节、性能基准和高级用例。
快速开始
依赖
使用 Spring Boot starter 获得最简单的装配(含 Lucene 与自动配置):
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-tool-search-advisor</artifactId>
</dependency>有了这个 starter,在 application.properties 中启用该顾问——无需 Java 配置:
properties
spring.ai.chat.client.tool-search-advisor.enabled=true完整的配置属性与 ToolIndex 选择选项列表,参见 工具搜索工具自动配置。
或者,只添加该库以做手动 @Bean 配置:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tool-search-advisor</artifactId>
</dependency>基本用法
java
@SpringBootApplication
public class Application {
@Bean
CommandLineRunner demo(ChatClient.Builder builder, ToolIndex toolIndex) {
return args -> {
var advisor = ToolSearchToolCallingAdvisor.builder()
.toolIndex(toolIndex)
.build();
ChatClient chatClient = builder
.defaultTools(new MyTools()) // 100s of tools registered but NOT sent to LLM initially
.defaultAdvisors(advisor) // Activate Tool Search Tool
.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) {
// implementation
}
@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) {
// implementation
}
@Tool(description = "Current date and time for a given location")
public String currentTime(String location) {
// implementation
}
// ... potentially hundreds more tools
}
}工作原理
ToolSearchToolCallingAdvisor 扩展 Spring AI 的 ToolCallingAdvisor 以实现动态工具发现:
- 索引:在对话开始时,所有已注册的工具被索引进
ToolIndex(但不发给 LLM) - 初始请求:只有工具搜索工具的定义被发给 LLM
- 发现调用:当 LLM 需要能力时,它用查询调用搜索工具
- 搜索与展开:
ToolIndex找到匹配的工具,它们的定义被加入下一个请求 - 工具调用:LLM 此刻同时看到搜索工具与被发现的工具定义
- 工具执行:被发现的工具被执行并返回结果
- 响应:LLM 生成最终答案
搜索策略
ToolIndex 接口支持多种搜索实现:
| 策略 | 实现 | 最适于 |
|---|---|---|
| 语义 | VectorToolIndex | 自然语言查询、模糊匹配 |
| 关键词 | LuceneToolIndex | 精确词项匹配、已知工具名 |
| 正则 | RegexToolIndex | 工具名模式(get_*_data) |
所有可用实现与配置选项参见 工具搜索工具。
性能
用 28 个工具的初步基准显示显著令牌节省:
| 模型 | 用工具搜索 | 不用 | 节省 |
|---|---|---|---|
| Gemini | 2,165 令牌 | 5,375 令牌 | 60% |
| OpenAI | 4,706 令牌 | 7,175 令牌 | 34% |
| Anthropic | 6,273 令牌 | 17,342 令牌 | 64% |
何时使用
| 工具搜索工具方式 | 传统方式 |
|---|---|
| 你的系统有 20+ 工具 | 工具库很小(<20 工具) |
| 工具定义消耗 >5K 令牌 | 每个会话都频繁使用所有工具 |
| 构建带多个服务端的 MCP 支撑系统 | 非常紧凑的工具定义 |
| 遇到工具选择准确性问题 |
相关文档
参考
- Anthropic Advanced Tool Use - 原始模式描述
- Spring AI Recursive Advisors Blog - 工具搜索实现的基础