mirror of
https://github.com/deepset-ai/haystack.git
synced 2026-02-06 23:12:43 +00:00
* Update documentation and remove unused assets. Enhanced the 'agents' and 'components' sections with clearer descriptions and examples. Removed obsolete images and updated links for better navigation. Adjusted formatting for consistency across various documentation pages. * remove dependency * address comments * delete more empty pages * broken link * unduplicate headings * alphabetical components nav
405 lines
14 KiB
Plaintext
405 lines
14 KiB
Plaintext
---
|
||
title: "Tool"
|
||
id: tool
|
||
slug: "/tool"
|
||
description: "`Tool` is a data class representing a function that Language Models can prepare a call for."
|
||
---
|
||
|
||
# Tool
|
||
|
||
`Tool` is a data class representing a function that Language Models can prepare a call for.
|
||
|
||
A growing number of Language Models now support passing tool definitions alongside the prompt.
|
||
|
||
Tool calling refers to the ability of Language Models to generate calls to tools - be they functions or APIs - when responding to user queries. The model prepares the tool call but does not execute it.
|
||
|
||
If you are looking for the details of this data class's methods and parameters, visit our [API documentation](/reference/data-classess-api#module-tool).
|
||
|
||
## Tool class
|
||
|
||
`Tool` is a simple and unified abstraction to represent tools in the Haystack framework.
|
||
|
||
A tool is a function for which Language Models can prepare a call.
|
||
|
||
The `Tool` class is used in Chat Generators and provides a consistent experience across models. `Tool` is also used in the [`ToolInvoker`](../pipeline-components/tools/toolinvoker.mdx) component that executes calls prepared by Language Models.
|
||
|
||
```python
|
||
@dataclass
|
||
class Tool:
|
||
name: str
|
||
description: str
|
||
parameters: Dict[str, Any]
|
||
function: Callable
|
||
```
|
||
|
||
- `name` is the name of the Tool.
|
||
- `description` is a string describing what the Tool does.
|
||
- `parameters` is a JSON schema describing the expected parameters.
|
||
- `function` is invoked when the Tool is called.
|
||
|
||
Keep in mind that the accurate definitions of `name` and `description` are important for the Language Model to prepare the call correctly.
|
||
|
||
`Tool` exposes a `tool_spec` property, returning the tool specification to be used by Language Models.
|
||
|
||
It also has an `invoke` method that executes the underlying function with the provided parameters.
|
||
|
||
## Tool Initialization
|
||
|
||
Here is how to initialize a Tool to work with a specific function:
|
||
|
||
```python
|
||
from haystack.tools import Tool
|
||
|
||
def add(a: int, b: int) -> int:
|
||
return a + b
|
||
|
||
parameters = {
|
||
"type": "object",
|
||
"properties": {
|
||
"a": {"type": "integer"},
|
||
"b": {"type": "integer"}
|
||
},
|
||
"required": ["a", "b"]
|
||
}
|
||
|
||
add_tool = Tool(name="addition_tool",
|
||
description="This tool adds two numbers",
|
||
parameters=parameters,
|
||
function=add)
|
||
|
||
print(add_tool.tool_spec)
|
||
|
||
print(add_tool.invoke(a=15, b=10))
|
||
```
|
||
|
||
```
|
||
{'name': 'addition_tool',
|
||
'description': 'This tool adds two numbers',
|
||
'parameters':{'type': 'object',
|
||
'properties':{'a':{'type': 'integer'}, 'b':{'type': 'integer'}},
|
||
'required':['a', 'b']}}
|
||
|
||
25
|
||
```
|
||
|
||
### @tool decorator
|
||
|
||
The `@tool` decorator simplifies converting a function into a Tool. It infers Tool name, description, and parameters from the function and automatically generates a JSON schema. It uses Python's `typing.Annotated` for the description of parameters. If you need to customize Tool name and description, use `create_tool_from_function` instead.
|
||
|
||
```python
|
||
from typing import Annotated, Literal
|
||
from haystack.tools import tool
|
||
|
||
@tool
|
||
def get_weather(
|
||
city: Annotated[str, "the city for which to get the weather"] = "Munich",
|
||
unit: Annotated[Literal["Celsius", "Fahrenheit"], "the unit for the temperature"] = "Celsius"):
|
||
'''A simple function to get the current weather for a location.'''
|
||
return f"Weather report for {city}: 20 {unit}, sunny"
|
||
|
||
print(get_weather)
|
||
```
|
||
|
||
```
|
||
Tool(name='get_weather', description='A simple function to get the current weather for a location.',
|
||
parameters={
|
||
'type': 'object',
|
||
'properties': {
|
||
'city': {'type': 'string', 'description': 'the city for which to get the weather', 'default': 'Munich'},
|
||
'unit': {
|
||
'type': 'string',
|
||
'enum': ['Celsius', 'Fahrenheit'],
|
||
'description': 'the unit for the temperature',
|
||
'default': 'Celsius',
|
||
},
|
||
}
|
||
},
|
||
function=<function get_weather at 0x7f7b3a8a9b80>)
|
||
```
|
||
|
||
### create_tool_from_function
|
||
|
||
The `create_tool_from_function` method provides more flexibility than the`@tool` decorator and allows setting Tool name and description. It infers the Tool parameters automatically and generates a JSON schema automatically in the same way as the `@tool` decorator.
|
||
|
||
```python
|
||
from typing import Annotated, Literal
|
||
from haystack.tools import create_tool_from_function
|
||
|
||
def get_weather(
|
||
city: Annotated[str, "the city for which to get the weather"] = "Munich",
|
||
unit: Annotated[Literal["Celsius", "Fahrenheit"], "the unit for the temperature"] = "Celsius"):
|
||
'''A simple function to get the current weather for a location.'''
|
||
return f"Weather report for {city}: 20 {unit}, sunny"
|
||
|
||
tool = create_tool_from_function(get_weather)
|
||
|
||
print(tool)
|
||
```
|
||
|
||
```
|
||
Tool(name='get_weather', description='A simple function to get the current weather for a location.',
|
||
parameters={
|
||
'type': 'object',
|
||
'properties': {
|
||
'city': {'type': 'string', 'description': 'the city for which to get the weather', 'default': 'Munich'},
|
||
'unit': {
|
||
'type': 'string',
|
||
'enum': ['Celsius', 'Fahrenheit'],
|
||
'description': 'the unit for the temperature',
|
||
'default': 'Celsius',
|
||
},
|
||
}
|
||
},
|
||
function=<function get_weather at 0x7f7b3a8a9b80>)
|
||
```
|
||
|
||
## Toolset
|
||
|
||
A Toolset groups multiple Tool instances into a single manageable unit. It simplifies the passing of tools to components like Chat Generators or `ToolInvoker`, and supports filtering, serialization, and reuse.
|
||
|
||
```python
|
||
from haystack.tools import Toolset
|
||
|
||
math_toolset = Toolset([add_tool, subtract_tool])
|
||
```
|
||
|
||
See more details and examples on the [Toolset documentation page](toolset.mdx).
|
||
|
||
## Usage
|
||
|
||
To better understand this section, make sure you are also familiar with Haystack's [`ChatMessage`](../concepts/data-classes/chatmessage.mdx) data class.
|
||
|
||
### Passing Tools to a Chat Generator
|
||
|
||
Using the `tools` parameter, you can pass tools as a list of Tool instances or a single Toolset during initialization or in the `run` method. Tools passed at runtime override those set at initialization.
|
||
|
||
:::info
|
||
Chat Generators support
|
||
|
||
Not all Chat Generators currently support tools, but we are actively expanding tool support across more models.
|
||
|
||
Look out for the `tools` parameter in a specific Chat Generator's `__init__` and `run` methods.
|
||
:::
|
||
|
||
```python
|
||
from haystack.dataclasses import ChatMessage
|
||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||
|
||
## Initialize the Chat Generator with the addition tool
|
||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[add_tool])
|
||
|
||
## here we expect the Tool to be invoked
|
||
res=chat_generator.run([ChatMessage.from_user("10 + 238")])
|
||
print(res)
|
||
|
||
## here the model can respond without using the Tool
|
||
res=chat_generator.run([ChatMessage.from_user("What is the habitat of a lion?")])
|
||
print(res)
|
||
```
|
||
|
||
```
|
||
{'replies':[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>,
|
||
_content=[ToolCall(tool_name='addition_tool',
|
||
arguments={'a':10, 'b':238},
|
||
id='call_rbYtbCdW0UbWMfy2x0sgF1Ap'
|
||
)],
|
||
_meta={...})]}
|
||
|
||
|
||
{'replies':[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>,
|
||
_content=[TextContent(text='Lions primarily inhabit grasslands, savannas, and open woodlands. ...'
|
||
)],
|
||
_meta={...})]}
|
||
```
|
||
|
||
The same result of the previous run can be achieved by passing tools at runtime:
|
||
|
||
```python
|
||
## Initialize the Chat Generator without tools
|
||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini")
|
||
|
||
## pass tools in the run method
|
||
res_w_tool_call=chat_generator.run([ChatMessage.from_user("10 + 238")], tools=math_toolset)
|
||
print(res_w_tool_call)
|
||
```
|
||
|
||
### Executing Tool Calls
|
||
|
||
To execute prepared tool calls, you can use the [`ToolInvoker`](../pipeline-components/tools/toolinvoker.mdx) component. This component acts as the execution engine for tools, processing the calls prepared by the Language Model.
|
||
|
||
Here's an example:
|
||
|
||
```python
|
||
import random
|
||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||
from haystack.components.tools import ToolInvoker
|
||
from haystack.tools import Tool
|
||
|
||
## Define a dummy weather toolimport random
|
||
|
||
def dummy_weather(location: str):
|
||
return {"temp": f"{random.randint(-10,40)} °C",
|
||
"humidity": f"{random.randint(0,100)}%"}
|
||
|
||
weather_tool = Tool(
|
||
name="weather",
|
||
description="A tool to get the weather",
|
||
function=dummy_weather,
|
||
parameters={
|
||
"type": "object",
|
||
"properties": {"location": {"type": "string"}},
|
||
"required": ["location"],
|
||
},
|
||
)
|
||
|
||
## Initialize the Chat Generator with the weather tool
|
||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[weather_tool])
|
||
|
||
## Initialize the Tool Invoker with the weather tool
|
||
tool_invoker = ToolInvoker(tools=[weather_tool])
|
||
|
||
user_message = ChatMessage.from_user("What is the weather in Berlin?")
|
||
|
||
replies = chat_generator.run(messages=[user_message])["replies"]
|
||
print(f"assistant messages: {replies}")
|
||
|
||
## If the assistant message contains a tool call, run the tool invoker
|
||
if replies[0].tool_calls:
|
||
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
|
||
print(f"tool messages: {tool_messages}")
|
||
```
|
||
|
||
```
|
||
assistant messages:[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='weather',
|
||
arguments={'location': 'Berlin'}, id='call_YEvCEAmlvc42JGXV84NU8wtV')], _meta={'model': 'gpt-4o-mini-2024-07-18',
|
||
'index':0, 'finish_reason': 'tool_calls', 'usage':{'completion_tokens':13, 'prompt_tokens':50, 'total_tokens':
|
||
63}})]
|
||
|
||
tool messages: [ChatMessage(_role=<ChatRole.TOOL: 'tool'>, _content=[ToolCallResult(result="{'temp': '22 °C',
|
||
'humidity': '35%'}", origin=ToolCall(tool_name='weather', arguments={'location': 'Berlin'},
|
||
id='call_YEvCEAmlvc42JGXV84NU8wtV'), error=False)], _meta={})]
|
||
```
|
||
|
||
### Processing Tool Results with the Chat Generator
|
||
|
||
In some cases, the raw output from a tool may not be immediately suitable for the end user.
|
||
|
||
You can refine the tool’s response by passing it back to the Chat Generator. This generates a user-friendly and conversational message.
|
||
|
||
In this example, we’ll pass the tool’s response back to the Chat Generator for final processing:
|
||
|
||
```python
|
||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||
from haystack.components.tools import ToolInvoker
|
||
from haystack.tools import Tool
|
||
|
||
## Define a dummy weather toolimport random
|
||
|
||
def dummy_weather(location: str):
|
||
return {"temp": f"{random.randint(-10,40)} °C",
|
||
"humidity": f"{random.randint(0,100)}%"}
|
||
|
||
weather_tool = Tool(
|
||
name="weather",
|
||
description="A tool to get the weather",
|
||
function=dummy_weather,
|
||
parameters={
|
||
"type": "object",
|
||
"properties": {"location": {"type": "string"}},
|
||
"required": ["location"],
|
||
},
|
||
)
|
||
|
||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[weather_tool])
|
||
tool_invoker = ToolInvoker(tools=[weather_tool])
|
||
|
||
user_message = ChatMessage.from_user("What is the weather in Berlin?")
|
||
|
||
replies = chat_generator.run(messages=[user_message])["replies"]
|
||
print(f"assistant messages: {replies}")
|
||
|
||
if replies[0].tool_calls:
|
||
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
|
||
print(f"tool messages: {tool_messages}")
|
||
# we pass all the messages to the Chat Generator
|
||
messages = [user_message] + replies + tool_messages
|
||
final_replies = chat_generator.run(messages=messages)["replies"]
|
||
print(f"final assistant messages: {final_replies}")
|
||
```
|
||
|
||
```
|
||
assistant messages:[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='weather',
|
||
arguments={'location': 'Berlin'}, id='call_jHX0RCDHRKX7h8V9RrNs6apy')], _meta={'model': 'gpt-4o-mini-2024-07-18',
|
||
'index':0, 'finish_reason': 'tool_calls', 'usage':{'completion_tokens':13, 'prompt_tokens':50, 'total_tokens':
|
||
63}})]
|
||
|
||
tool messages: [ChatMessage(_role=<ChatRole.TOOL: 'tool'>, _content=[ToolCallResult(result="{'temp': '2 °C',
|
||
'humidity': '15%'}", origin=ToolCall(tool_name='weather', arguments={'location': 'Berlin'},
|
||
id='call_jHX0RCDHRKX7h8V9RrNs6apy'), error=False)], _meta={})]
|
||
|
||
final assistant messages: [ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[TextContent(text='The
|
||
current weather in Berlin is 2 °C with a humidity level of 15%.')], _meta={'model': 'gpt-4o-mini-2024-07-18',
|
||
'index': 0, 'finish_reason': 'stop', 'usage': {'completion_tokens': 19, 'prompt_tokens': 85, 'total_tokens':
|
||
104}})]
|
||
```
|
||
|
||
### Passing Tools to Agent
|
||
|
||
You can also use `Tool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. Internally, the `Agent` component includes a `ToolInvoker` and the ChatGenerator of your choice to execute tool calls and process tool results.
|
||
|
||
```python
|
||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||
from haystack.dataclasses import ChatMessage
|
||
from haystack.tools.tool import Tool
|
||
from haystack.components.agents import Agent
|
||
from typing import List
|
||
|
||
## Tool Function
|
||
def calculate(expression: str) -> dict:
|
||
try:
|
||
result = eval(expression, {"__builtins__": {}})
|
||
return {"result": result}
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
## Tool Definition
|
||
calculator_tool = Tool(
|
||
name="calculator",
|
||
description="Evaluate basic math expressions.",
|
||
parameters={
|
||
"type": "object",
|
||
"properties": {
|
||
"expression": {"type": "string", "description": "Math expression to evaluate"}
|
||
},
|
||
"required": ["expression"]
|
||
},
|
||
function=calculate,
|
||
outputs_to_state={"calc_result": {"source": "result"}}
|
||
)
|
||
|
||
## Agent Setup
|
||
agent = Agent(
|
||
chat_generator=OpenAIChatGenerator(),
|
||
tools=[calculator_tool],
|
||
exit_conditions=["calculator"],
|
||
state_schema={
|
||
"calc_result": {"type": int},
|
||
}
|
||
)
|
||
|
||
## Run the Agent
|
||
agent.warm_up()
|
||
response = agent.run(messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")])
|
||
|
||
## Output
|
||
print(response["messages"])
|
||
print("Calc Result:", response.get("calc_result"))
|
||
```
|
||
|
||
## Additional References
|
||
|
||
🧑🍳 Cookbooks:
|
||
|
||
- [Build a GitHub Issue Resolver Agent](https://haystack.deepset.ai/cookbook/github_issue_resolver_agent)
|
||
- [Newsletter Sending Agent with Haystack Tools](https://haystack.deepset.ai/cookbook/newsletter-agent)
|
||
- [Create a Swarm of Agents](https://haystack.deepset.ai/cookbook/swarm) |