SearchableToolset
Enable agents to dynamically discover tools from large catalogs using keyword-based search.
| Mandatory init variables | catalog: A list of Tools and/or Toolsets, or a single Toolset |
| API reference | SearchableToolset |
| GitHub link | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/searchable_toolset.py |
| Package name | haystack-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_toolsbootstrap tool and discovers other tools on demand. This is activated when the catalog size meets or exceedssearch_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 ofTooland/orToolsetinstances, or a singleToolset. This includes MCPTool and MCPToolset instances.top_k(optional): The default number of tools returned by eachsearch_toolscall. Default is3.search_threshold(optional): Minimum catalog size to activate search mode. Catalogs smaller than this value use passthrough mode instead. Default is8.
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
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"}.
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:
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.