Files
wehub-resource-sync c56bef871b
Sync docs with Docusaurus / sync (push) Waiting to run
Tests / Check if changed (push) Waiting to run
Tests / format (push) Blocked by required conditions
Tests / check-imports (push) Blocked by required conditions
Tests / Unit / macos-latest (push) Blocked by required conditions
Tests / Unit / ubuntu-latest (push) Blocked by required conditions
Tests / Unit / windows-latest (push) Blocked by required conditions
Tests / mypy (push) Blocked by required conditions
Tests / Integration / ubuntu-latest (push) Blocked by required conditions
Tests / Integration / macos-latest (push) Blocked by required conditions
Tests / Integration / windows-latest (push) Blocked by required conditions
Tests / notify-slack-on-failure (push) Blocked by required conditions
Tests / Mark tests as completed (push) Blocked by required conditions
Docker image release / Build base image (push) Waiting to run
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:28 +08:00

136 lines
5.7 KiB
Plaintext

---
title: "SearchableToolset"
id: searchabletoolset
slug: "/searchabletoolset"
description: "Enable agents to dynamically discover tools from large catalogs using keyword-based search."
---
# SearchableToolset
Enable agents to dynamically discover tools from large catalogs using keyword-based search.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `catalog`: A list of Tools and/or Toolsets, or a single Toolset |
| **API reference** | [SearchableToolset](/reference/tools-api#searchabletoolset) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/searchable_toolset.py |
| **Package name** | `haystack-ai` |
</div>
## 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](mcptool.mdx) and [MCPToolset](mcptoolset.mdx) 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`](../pipeline-components/agents-1/agent.mdx), 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()`](toolset.mdx#run-scoped-copies-and-tool-selection)), 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.