Tool search tool
Tool search tool 使 Claude 能够通过动态发现和按需加载来处理数百或数千个工具。Claude 不是将所有工具定义预先加载到上下文窗口中,而是搜索你的工具目录(包括工具名称、描述、参数名称和参数描述),并只加载需要的工具。
这种方法解决了随着工具库扩展而快速复合的两个问题:
- 上下文膨胀: 工具定义会快速消耗你的上下文预算。典型的多服务器设置(GitHub、Slack、Sentry、Grafana、Splunk)在 Claude 做任何实际工作之前就可以在定义中消耗约 55k 个 token。Tool search 通常将此减少 85% 以上,只加载 Claude 在给定请求中实际需要的 3-5 个工具。
- 工具选择准确性: 一旦你超过 30-50 个可用工具,Claude 正确选择正确工具的能力会显著下降。通过按需呈现一组集中的相关工具,tool search 即使在数千个工具中也能保持高选择准确性。
虽然这是作为服务器端工具提供的,但你也可以实现自己的客户端 tool search 功能。详情参见自定义 tool search 实现。
通过反馈表分享对此功能的反馈。
此功能符合零数据留存 (ZDR) 条件。当你的组织有 ZDR 安排时,通过此功能发送的数据在 API 响应返回后不会被存储。
在 Amazon Bedrock 上,服务器端 tool search 仅通过 InvokeModel API 可用,不支持 Converse API。
在 AWS 上的 Claude Platform 上,服务器端 tool search 与 Claude API 的工作方式相同。AWS 上的 Claude Platform 直接使用 Anthropic Messages API,因此没有 InvokeModel 或 Converse 的区别。
Tool search 的工作原理
有两种 tool search 变体:
- Regex(
tool_search_tool_regex_20251119):Claude 构造正则表达式模式来搜索工具 - BM25(
tool_search_tool_bm25_20251119):Claude 使用自然语言查询来搜索工具
当你启用 tool search tool 时:
- 你在工具列表中包含一个 tool search tool(例如
tool_search_tool_regex_20251119或tool_search_tool_bm25_20251119)。 - 你为不应立即加载的工具提供
defer_loading: true的工具定义。 - Claude 最初只看到 tool search tool 和任何非延迟的工具。
- 当 Claude 需要额外工具时,它使用 tool search tool 进行搜索。
- API 返回 3-5 个最相关的
tool_reference块。 - 这些引用自动展开为完整的工具定义。
- Claude 从发现的工具中选择并调用它们。
这使你的上下文窗口保持高效,同时保持高工具选择准确性。
快速开始
这是一个使用延迟工具的简单示例:
curl https://api.anthropic.com/v1/messages \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data '{
"model": "claude-opus-4-7",
"max_tokens": 2048,
"messages": [
{
"role": "user",
"content": "What is the weather in San Francisco?"
}
],
"tools": [
{
"type": "tool_search_tool_regex_20251119",
"name": "tool_search_tool_regex"
},
{
"name": "get_weather",
"description": "Get the weather at a specific location",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
},
"defer_loading": true
},
{
"name": "search_files",
"description": "Search through files in the workspace",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
"file_types": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["query"]
},
"defer_loading": true
}
]
}'
ant messages create <<'YAML'
model: claude-opus-4-7
max_tokens: 2048
messages:
- role: user
content: What is the weather in San Francisco?
tools:
- type: tool_search_tool_regex_20251119
name: tool_search_tool_regex
- name: get_weather
description: Get the weather at a specific location
input_schema:
type: object
properties:
location:
type: string
unit:
type: string
enum: [celsius, fahrenheit]
required: [location]
defer_loading: true
- name: search_files
description: Search through files in the workspace
input_schema:
type: object
properties:
query:
type: string
file_types:
type: array
items:
type: string
required: [query]
defer_loading: true
YAML
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=2048,
messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
tools=[
{"type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex"},
{
"name": "get_weather",
"description": "Get the weather at a specific location",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
"defer_loading": True,
},
{
"name": "search_files",
"description": "Search through files in the workspace",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
"file_types": {"type": "array", "items": {"type": "string"}},
},
"required": ["query"],
},
"defer_loading": True,
},
],
)
print(response)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-4-7",
max_tokens: 2048,
messages: [
{
role: "user",
content: "What is the weather in San Francisco?"
}
],
tools: [
{
type: "tool_search_tool_regex_20251119",
name: "tool_search_tool_regex"
},
{
name: "get_weather",
description: "Get the weather at a specific location",
input_schema: {
type: "object" as const,
properties: {
location: { type: "string" },
unit: {
type: "string",
enum: ["celsius", "fahrenheit"]
}
},
required: ["location"]
},
defer_loading: true
},
{
name: "search_files",
description: "Search through files in the workspace",
input_schema: {
type: "object" as const,
properties: {
query: { type: "string" },
file_types: {
type: "array",
items: { type: "string" }
}
},
required: ["query"]
},
defer_loading: true
}
]
});
console.log(response);
using System;
using System.Text.Json;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus4_7,
MaxTokens = 2048,
Messages = [
new() {
Role = Role.User,
Content = "What is the weather in San Francisco?"
}
],
Tools = [
new ToolUnion(new ToolSearchToolRegex20251119
{
Type = ToolSearchToolRegex20251119Type.ToolSearchToolRegex20251119
}),
new ToolUnion(new Tool()
{
Name = "get_weather",
Description = "Get the weather at a specific location",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["location"] = JsonSerializer.SerializeToElement(new { type = "string" }),
["unit"] = JsonSerializer.SerializeToElement(new { type = "string", @enum = new[] { "celsius", "fahrenheit" } }),
},
Required = ["location"],
},
DeferLoading = true,
}),
new ToolUnion(new Tool()
{
Name = "search_files",
Description = "Search through files in the workspace",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["query"] = JsonSerializer.SerializeToElement(new { type = "string" }),
["file_types"] = JsonSerializer.SerializeToElement(new { type = "array", items = new { type = "string" } }),
},
Required = ["query"],
},
DeferLoading = true,
}),
]
};
var message = await client.Messages.Create(parameters);
Console.WriteLine(message);
package main
import (
"context"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go"
)
func main() {
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus4_7,
MaxTokens: 2048,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in San Francisco?")),
},
Tools: []anthropic.ToolUnionParam{
{OfToolSearchToolRegex20251119: &anthropic.ToolSearchToolRegex20251119Param{
Type: anthropic.ToolSearchToolRegex20251119TypeToolSearchToolRegex20251119,
}},
{OfTool: &anthropic.ToolParam{
Name: "get_weather",
Description: anthropic.String("Get the weather at a specific location"),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"location": map[string]any{"type": "string"},
"unit": map[string]any{
"type": "string",
"enum": []string{"celsius", "fahrenheit"},
},
},
Required: []string{"location"},
},
DeferLoading: anthropic.Bool(true),
}},
{OfTool: &anthropic.ToolParam{
Name: "search_files",
Description: anthropic.String("Search through files in the workspace"),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"query": map[string]any{"type": "string"},
"file_types": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
},
Required: []string{"query"},
},
DeferLoading: anthropic.Bool(true),
}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.Tool;
import com.anthropic.models.messages.Tool.InputSchema;
import com.anthropic.models.messages.ToolSearchToolRegex20251119;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
InputSchema weatherSchema = InputSchema.builder()
.properties(JsonValue.from(Map.of(
"location", Map.of("type", "string"),
"unit", Map.of(
"type", "string",
"enum", List.of("celsius", "fahrenheit")
)
)))
.putAdditionalProperty("required", JsonValue.from(List.of("location")))
.build();
InputSchema searchSchema = InputSchema.builder()
.properties(JsonValue.from(Map.of(
"query", Map.of("type", "string"),
"file_types", Map.of(
"type", "array",
"items", Map.of("type", "string")
)
)))
.putAdditionalProperty("required", JsonValue.from(List.of("query")))
.build();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_4_7)
.maxTokens(2048L)
.addUserMessage("What is the weather in San Francisco?")
.addTool(ToolSearchToolRegex20251119.builder()
.type(ToolSearchToolRegex20251119.Type.TOOL_SEARCH_TOOL_REGEX_20251119)
.build())
.addTool(Tool.builder()
.name("get_weather")
.description("Get the weather at a specific location")
.inputSchema(weatherSchema)
.deferLoading(true)
.build())
.addTool(Tool.builder()
.name("search_files")
.description("Search through files in the workspace")
.inputSchema(searchSchema)
.deferLoading(true)
.build())
.build();
Message response = client.messages().create(params);
IO.println(response);
}
<?php
use Anthropic\Client;
$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));
$message = $client->messages->create(
maxTokens: 2048,
messages: [
['role' => 'user', 'content' => 'What is the weather in San Francisco?'],
],
model: 'claude-opus-4-7',
tools: [
[
'type' => 'tool_search_tool_regex_20251119',
'name' => 'tool_search_tool_regex',
],
[
'name' => 'get_weather',
'description' => 'Get the weather at a specific location',
'input_schema' => [
'type' => 'object',
'properties' => [
'location' => ['type' => 'string'],
'unit' => [
'type' => 'string',
'enum' => ['celsius', 'fahrenheit'],
],
],
'required' => ['location'],
],
'defer_loading' => true,
],
[
'name' => 'search_files',
'description' => 'Search through files in the workspace',
'input_schema' => [
'type' => 'object',
'properties' => [
'query' => ['type' => 'string'],
'file_types' => [
'type' => 'array',
'items' => ['type' => 'string'],
],
],
'required' => ['query'],
],
'defer_loading' => true,
],
],
);
echo $message;
require "anthropic"
client = Anthropic::Client.new
message = client.messages.create(
model: "claude-opus-4-7",
max_tokens: 2048,
messages: [
{ role: "user", content: "What is the weather in San Francisco?" }
],
tools: [
{
type: "tool_search_tool_regex_20251119",
name: "tool_search_tool_regex"
},
{
name: "get_weather",
description: "Get the weather at a specific location",
input_schema: {
type: "object",
properties: {
location: { type: "string" },
unit: {
type: "string",
enum: ["celsius", "fahrenheit"]
}
},
required: ["location"]
},
defer_loading: true
},
{
name: "search_files",
description: "Search through files in the workspace",
input_schema: {
type: "object",
properties: {
query: { type: "string" },
file_types: {
type: "array",
items: { type: "string" }
}
},
required: ["query"]
},
defer_loading: true
}
]
)
puts message
工具定义
Tool search tool 有两个变体:
{
"type": "tool_search_tool_regex_20251119",
"name": "tool_search_tool_regex"
}
{
"type": "tool_search_tool_bm25_20251119",
"name": "tool_search_tool_bm25"
}
Regex 变体查询格式:Python 正则表达式,不是自然语言
使用 tool_search_tool_regex_20251119 时,Claude 使用 Python 的 re.search() 语法构造正则表达式模式,而不是自然语言查询。常见模式:
"weather"- 匹配包含 "weather" 的工具名称/描述"get_.*_data"- 匹配如get_user_data、get_weather_data的工具"database.*query|query.*database"- 用于灵活性的 OR 模式"(?i)slack"- 不区分大小写搜索
最大查询长度:200 个字符
BM25 变体查询格式:自然语言
使用 tool_search_tool_bm25_20251119 时,Claude 使用自然语言查询来搜索工具。
延迟工具加载
通过添加 defer_loading: true 标记工具为按需加载:
{
"name": "get_weather",
"description": "Get current weather for a location",
"input_schema": {
"type": "object",
"properties": {
"location": { "type": "string" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["location"]
},
"defer_loading": true
}
关键点:
- 没有
defer_loading的工具立即加载到上下文中 - 带
defer_loading: true的工具仅在 Claude 通过搜索发现时加载 - Tool search tool 本身永远不应有
defer_loading: true - 保持你最常用的 3-5 个工具为非延迟以获得最佳性能
两种 tool search 变体(regex 和 bm25)都搜索工具名称、描述、参数名称和参数描述。
延迟的工作原理: 延迟工具不包含在系统提示前缀中。当模型通过 tool search 发现延迟工具时,API 在对话中内联追加一个 tool_reference 块,然后在传递给 Claude 之前将其展开为完整的工具定义。前缀未被修改,因此 prompt caching 得以保留。Strict mode 的语法(约束工具调用输出以匹配你的 schema 的规则)从完整工具集构建,因此 defer_loading 和 strict mode 可以组合使用而无需语法重编译。
响应格式
当 Claude 使用 tool search tool 时,响应包含新的块类型:
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll search for tools to help with the weather information."
},
{
"type": "server_tool_use",
"id": "srvtoolu_01ABC123",
"name": "tool_search_tool_regex",
"input": {
"query": "weather"
}
},
{
"type": "tool_search_tool_result",
"tool_use_id": "srvtoolu_01ABC123",
"content": {
"type": "tool_search_tool_search_result",
"tool_references": [{ "type": "tool_reference", "tool_name": "get_weather" }]
}
},
{
"type": "text",
"text": "I found a weather tool. Let me get the weather for San Francisco."
},
{
"type": "tool_use",
"id": "toolu_01XYZ789",
"name": "get_weather",
"input": { "location": "San Francisco", "unit": "fahrenheit" }
}
],
"stop_reason": "tool_use"
}
理解响应
server_tool_use: 表示 Claude 正在调用 tool search tooltool_search_tool_result: 包含搜索结果,嵌套有tool_search_tool_search_result对象tool_references: 指向发现工具的tool_reference对象数组tool_use: Claude 调用发现的工具
tool_reference 块在显示给 Claude 之前自动展开为完整的工具定义。你不需要自己处理此展开。只要你提供所有匹配的工具定义在 tools 参数中,它就会在 API 中自动发生。
MCP 集成
有关配置 mcp_toolset 的 defer_loading,请参见 MCP connector。
自定义 tool search 实现
你可以通过从自定义工具返回 tool_reference 块来实现自己的 tool search 逻辑(例如使用嵌入或语义搜索)。当 Claude 调用你的自定义搜索工具时,返回一个标准 tool_result,内容数组中包含 tool_reference 块:
{
"type": "tool_result",
"tool_use_id": "toolu_your_tool_id",
"content": [{ "type": "tool_reference", "tool_name": "discovered_tool_name" }]
}
每个被引用的工具必须在顶层 tools 参数中有对应的工具定义,并设置 defer_loading: true。这种方法让你可以使用更复杂的搜索算法,同时保持与 tool search 系统的兼容性。
响应格式部分展示的 tool_search_tool_result 格式是 Anthropic 内置 tool search 内部使用的服务端格式。对于自定义客户端实现,请始终使用前面示例中展示的标准 tool_result 格式和 tool_reference 内容块。
有关使用嵌入的完整示例,请参见使用嵌入的 tool search cookbook。
错误处理
Tool search tool 与工具使用示例不兼容。如果你需要提供工具使用示例,请使用不带 tool search 的标准工具调用。
HTTP 错误(400 状态)
这些错误阻止请求被处理:
所有工具被延迟:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "All tools have defer_loading set. At least one tool must be non-deferred."
}
}
缺少工具定义:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "Tool reference 'unknown_tool' has no corresponding tool definition"
}
}
工具结果错误(200 状态)
工具执行期间的错误返回 200 响应,错误信息在响应体中:
{
"type": "tool_search_tool_result",
"tool_use_id": "srvtoolu_01ABC123",
"content": {
"type": "tool_search_tool_result_error",
"error_code": "invalid_pattern"
}
}
错误码:
too_many_requests:tool search 操作超出速率限制invalid_pattern:格式错误的正则表达式模式pattern_too_long:模式超出 200 字符限制unavailable:tool search 服务暂时不可用
常见错误
400 错误:所有工具被延迟
原因: 你在所有工具(包括搜索工具)上设置了 defer_loading: true
修复: 从 tool search tool 移除 defer_loading:
{
"type": "tool_search_tool_regex_20251119",
"name": "tool_search_tool_regex"
}
400 错误:缺少工具定义
原因: tool_reference 指向不在你 tools 数组中的工具
修复: 确保每个可能被发现的工具都有完整定义:
{
"name": "my_tool",
"description": "Full description here",
"input_schema": {
"type": "object"
},
"defer_loading": true
}
Claude 找不到预期的工具
原因: 工具名称、描述、参数名称或参数描述与正则表达式模式不匹配
调试步骤:
- 检查工具名称、描述、参数名称和参数描述。Claude 搜索所有这些字段。
- 测试你的模式:
import re; re.search(r"your_pattern", "tool_name")。 - 记住搜索默认区分大小写(使用
(?i)进行不区分大小写搜索)。 - Claude 使用宽泛模式如
".*weather.*"而非精确匹配。
提示: 在工具描述中添加常见关键词以提高可发现性
Prompt caching
有关 defer_loading 如何保留 prompt caching,请参见工具使用与 prompt caching。
系统自动在整个对话历史中展开 tool_reference 块,因此 Claude 可以在后续轮次中复用发现的工具而无需重新搜索。
流式传输
启用流式传输后,你将作为流的一部分接收 tool search 事件:
event: content_block_start
data: {"type": "content_block_start", "index": 1, "content_block": {"type": "server_tool_use", "id": "srvtoolu_xyz789", "name": "tool_search_tool_regex"}}
// 搜索查询流式传输
event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "input_json_delta", "partial_json": "{\"query\":\"weather\"}"}}
// 搜索执行期间暂停
// 搜索结果流式传输
event: content_block_start
data: {"type": "content_block_start", "index": 2, "content_block": {"type": "tool_search_tool_result", "tool_use_id": "srvtoolu_xyz789", "content": {"type": "tool_search_tool_search_result", "tool_references": [{"type": "tool_reference", "tool_name": "get_weather"}]}}}
// Claude 使用发现的工具继续
批量请求
你可以在 Messages Batches API 中包含 tool search tool。通过 Messages Batches API 的 tool search 操作与常规 Messages API 请求中的定价相同。
限制和最佳实践
限制
- 最大工具数: 你的目录中最多 10,000 个工具
- 搜索结果: 每次搜索返回 3-5 个最相关的工具
- 模式长度: 正则表达式模式最大 200 个字符
- 模型支持: Claude Mythos Preview、Sonnet 4.0+、Opus 4.0+、Haiku 4.5+
何时使用 tool search
好的使用场景:
- 你的系统中有 10+ 个可用工具
- 工具定义消耗 >10k 个 token
- 大型工具集遇到工具选择准确性问题
- 构建具有多个服务器(200+ 个工具)的 MCP 驱动系统
- 工具库随时间增长
传统工具调用可能更好的场景:
- 总共少于 10 个工具
- 所有工具在每个请求中频繁使用
- 非常小的工具定义(总共 <100 个 token)
优化提示
- 保持 3-5 个最常用的工具为非延迟
- 编写清晰、描述性的工具名称和描述
- 在工具名称中使用一致的命名空间:按服务或资源加前缀(例如
github_、slack_),以便搜索查询自然地呈现正确的工具组 - 在描述中使用与用户描述任务方式匹配的语义关键词
- 添加描述可用工具类别的系统提示部分:"You can search for tools to interact with Slack, GitHub, and Jira"
- 监控 Claude 发现的工具以优化描述
使用量
Tool search tool 使用量在响应使用量对象中跟踪:
{
"usage": {
"input_tokens": 1024,
"output_tokens": 256,
"server_tool_use": {
"tool_search_requests": 2
}
}
}