Skip to main content
Version: 3.1-unstable

SearchableToolset

Enable agents to dynamically discover tools from large catalogs using keyword-based search.

Mandatory init variablescatalog: A list of Tools and/or Toolsets, or a single Toolset
API referenceSearchableToolset
GitHub linkhttps://github.com/deepset-ai/haystack/blob/main/haystack/tools/searchable_toolset.py
Package namehaystack-ai

Overview

SearchableToolset is designed for working with large tool catalogs. Instead of exposing all tools at once, which can overwhelm the LLM context, it provides a single search_tools bootstrap tool. The agent uses this tool to find and load specific tools from the catalog using BM25 keyword search.

Once the agent calls search_tools, the matching tools become immediately available and the agent can invoke them in subsequent iterations.

Modes of operation

SearchableToolset operates in one of two modes depending on catalog size:

  • Search mode (default for large catalogs): The agent starts with only the search_tools bootstrap tool and discovers other tools on demand. This is activated when the catalog size meets or exceeds search_threshold.
  • Passthrough mode (small catalogs): All tools are exposed directly, with no discovery step needed. This is activated automatically when the catalog has fewer tools than search_threshold.

Parameters

  • catalog (required): The source of tools — a list of Tool and/or Toolset instances, or a single Toolset. This includes MCPTool and MCPToolset instances.
  • top_k (optional): The default number of tools returned by each search_tools call. Default is 3.
  • search_threshold (optional): Minimum catalog size to activate search mode. Catalogs smaller than this value use passthrough mode instead. Default is 8.
info

SearchableToolset does not support adding new tools after initialization or merging with other toolsets. Use catalog to provide all tools upfront.

Warm-up

SearchableToolset builds its search index during warm_up(). When used with an Agent, constructing the Agent does not trigger this — warm-up happens when you call Agent.warm_up() or automatically at run time.

All tool names in the catalog must be unique: warm_up() raises a ValueError if the catalog contains tools with duplicate names, since a search hit could otherwise resolve to the wrong tool.

The Agent evaluates its exit_conditions at runtime, so an exit condition can name any tool in the catalog, even one the agent has not discovered yet.

Usage

Basic usage with an Agent

python
from typing import Annotated
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import create_tool_from_function, SearchableToolset


def get_weather(city: Annotated[str, "The city to get the weather for"]) -> str:
"""Get current weather for a city."""
return f"Sunny, 22°C in {city}"


def search_web(query: Annotated[str, "The search query"]) -> str:
"""Search the web for information."""
return f"Results for: {query}"


# Build a catalog from tools
catalog = [
create_tool_from_function(get_weather),
create_tool_from_function(search_web),
# ... many more tools
]

toolset = SearchableToolset(catalog=catalog)

agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=toolset,
)

# The agent initially sees only `search_tools`. It will call it to find relevant tools,
# then use the discovered tools to answer the question.
result = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")])
print(result["messages"][-1].text)

Customizing the bootstrap tool

You can customize the name, description, and parameter descriptions of the search_tools bootstrap tool:

  • search_tool_name: Custom name for the bootstrap tool. Default is "search_tools".
  • search_tool_description: Custom description for the bootstrap tool.
  • search_tool_parameters_description: Custom descriptions for the bootstrap tool's parameters. Keys must be a subset of {"tool_keywords", "k"}.
python
toolset = SearchableToolset(
catalog=catalog,
search_tool_name="find_tools",
search_tool_description="Search for tools in the catalog by keyword.",
search_tool_parameters_description={
"tool_keywords": "Keywords to find tools, e.g. 'email send'",
"k": "Max number of tools to return",
},
)

Reusing the toolset across multiple agent runs

You can safely reuse the same SearchableToolset instance across multiple agent runs, including concurrent ones. Each Agent run operates on an isolated, run-scoped copy of the toolset (created with spawn()), so tools discovered in one run do not persist into, or collide with, other runs — every run starts fresh from the catalog:

python
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=toolset,
)

result1 = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")])

# The next run starts fresh: tools discovered in the previous run are not carried over
result2 = agent.run(messages=[ChatMessage.from_user("Search for news about AI.")])

If you drive the toolset directly (outside an Agent), you can call clear() to reset the discovered tools yourself.