docs: add component page for tool calls (#20282)

Note: includes links to API reference pages for ToolCall and other
objects that currently don't exist (e.g.,
https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolCall.html#langchain_core.messages.tool.ToolCall).
This commit is contained in:
ccurme 2024-04-11 12:29:25 -04:00 committed by GitHub
parent 6608089030
commit f02e55aaf7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,68 +1,133 @@
--- ---
sidebar_position: 2 sidebar_position: 2
title: Function calling title: Tool/function calling
--- ---
# Function calling # Tool calling
A growing number of chat models, like :::info
[OpenAI](https://platform.openai.com/docs/guides/function-calling), We use the term tool calling interchangeably with function calling. Although
[Gemini](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling), function calling is sometimes meant to refer to invocations of a single function,
etc., have a function-calling API that lets you describe functions and we treat all models as though they can return multiple tool or function calls in
their arguments, and have the model return a JSON object with a function each message.
to invoke and the inputs to that function. Function-calling is extremely :::
useful for building [tool-using chains and
agents](/docs/use_cases/tool_use/), and for getting
structured outputs from models more generally.
LangChain comes with a number of utilities to make function-calling # Calling Tools
easy. Namely, it comes with:
- simple syntax for binding functions to models Tool calling allows a model to respond to a given prompt by generating output that
- converters for formatting various types of objects to the expected matches a user-defined schema. While the name implies that the model is performing
function schemas some action, this is actually not the case! The model is coming up with the
- output parsers for extracting the function invocations from API arguments to a tool, and actually running the tool (or not) is up to the user -
responses for example, if you want to [extract output matching some schema](/docs/use_cases/extraction/)
- chains for getting structured outputs from a model, built on top of from unstructured text, you could give the model an "extraction" tool that takes
function calling parameters matching the desired schema, then treat the generated output as your final
result.
Well focus here on the first two points. For a detailed guide on output A tool call includes a name, arguments dict, and an optional identifier. The
parsing check out the [OpenAI Tools output arguments dict is structured `{argument_name: argument_value}`.
parsers](/docs/modules/model_io/output_parsers/types/openai_tools/)
and to see the structured output chains check out the [Structured output
guide](/docs/modules/model_io/chat/structured_output/).
Before getting started make sure you have `langchain-core` installed. Many LLM providers, including [Anthropic](https://www.anthropic.com/),
[Cohere](https://cohere.com/), [Google](https://cloud.google.com/vertex-ai),
[Mistral](https://mistral.ai/), [OpenAI](https://openai.com/), and others,
support variants of a tool calling feature. These features typically allow requests
to the LLM to include available tools and their schemas, and for responses to include
calls to these tools. For instance, given a search engine tool, an LLM might handle a
query by first issuing a call to the search engine. The system calling the LLM can
receive the tool call, execute it, and return the output to the LLM to inform its
response. LangChain includes a suite of [built-in tools](/docs/integrations/tools/)
and supports several methods for defining your own [custom tools](/docs/modules/tools/custom_tools).
Tool-calling is extremely useful for building [tool-using chains and agents](/docs/use_cases/tool_use),
and for getting structured outputs from models more generally.
Providers adopt different conventions for formatting tool schemas and tool calls.
For instance, Anthropic returns tool calls as parsed structures within a larger content block:
```
[
{
"text": "<thinking>\nI should use a tool.\n</thinking>",
"type": "text"
},
{
"id": "id_value",
"input": {"arg_name": "arg_value"},
"name": "tool_name",
"type": "tool_use"
}
]
```
whereas OpenAI separates tool calls into a distinct parameter, with arguments as JSON strings:
```
{
"tool_calls": [
{
"id": "id_value",
"function": {
"arguments": '{"arg_name": "arg_value"}',
"name": "tool_name"
},
"type": "function"
}
]
}
```
LangChain implements standard interfaces for defining tools, passing them to LLMs,
and representing tool calls.
## Passing tools to LLMs
Chat models supporting tool calling features implement a `.bind_tools` method, which
receives a list of LangChain [tool objects](https://api.python.langchain.com/en/latest/tools/langchain_core.tools.BaseTool.html#langchain_core.tools.BaseTool)
and binds them to the chat model in its expected format. Subsequent invocations of the
chat model will include tool schemas in its calls to the LLM.
For example, we can define the schema for custom tools using the `@tool` decorator
on Python functions:
```python ```python
%pip install -qU langchain-core langchain-openai from langchain.tools import tool
@tool
def add(a: int, b: int) -> int:
"""Adds a and b."""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""Multiplies a and b."""
return a * b
tools = [add, multiply]
``` ```
```python Or below, we define the schema using Pydantic:
import getpass
import os
```
## Binding functions
A number of models implement helper methods that will take care of
formatting and binding different function-like objects to the model.
Lets take a look at how we might take the following Pydantic function
schema and get different models to invoke it:
```python ```python
from langchain_core.pydantic_v1 import BaseModel, Field from langchain_core.pydantic_v1 import BaseModel, Field
# Note that the docstrings here are crucial, as they will be passed along # Note that the docstrings here are crucial, as they will be passed along
# to the model along with the class name. # to the model along with the class name.
class Add(BaseModel):
"""Add two integers together."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
class Multiply(BaseModel): class Multiply(BaseModel):
"""Multiply two integers together.""" """Multiply two integers together."""
a: int = Field(..., description="First integer") a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer") b: int = Field(..., description="Second integer")
tools = [Add, Multiply]
``` ```
We can bind them to chat models as follows:
import Tabs from "@theme/Tabs"; import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem"; import TabItem from "@theme/TabItem";
@ -72,230 +137,180 @@ import ChatModelTabs from "@theme/ChatModelTabs";
customVarName="llm" customVarName="llm"
fireworksParams={`model="accounts/fireworks/models/firefunction-v1", temperature=0`} fireworksParams={`model="accounts/fireworks/models/firefunction-v1", temperature=0`}
hideGoogle={true} hideGoogle={true}
hideAnthropic={true} hideAnthropic={false}
/> />
We can use the `bind_tools()` method to handle converting We can use the `bind_tools()` method to handle converting
`Multiply` to a "function" and binding it to the model (i.e., `Multiply` to a "tool" and binding it to the model (i.e.,
passing it in each time the model is invoked). passing it in each time the model is invoked).
```python ```python
llm_with_tools = llm.bind_tools([Multiply]) llm_with_tools = llm.bind_tools(tools)
llm_with_tools.invoke("what's 3 * 12")
``` ```
```text ## Tool calls
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Q8ZQ97Qrj5zalugSkYMGV1Uo', 'function': {'arguments': '{"a":3,"b":12}', 'name': 'Multiply'}, 'type': 'function'}]})
```
We can add a tool parser to extract the tool calls from the generated If tool calls are included in a LLM response, they are attached to the corresponding
message to JSON: [message](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessage.html#langchain_core.messages.ai.AIMessage)
or [message chunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk)
as a list of [tool call](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolCall.html#langchain_core.messages.tool.ToolCall)
objects in the `.tool_calls` attribute. A `ToolCall` is a typed dict that includes a
tool name, dict of argument values, and (optionally) an identifier. Messages with no
tool calls default to an empty list for this attribute.
Example:
```python ```python
from langchain_core.output_parsers.openai_tools import JsonOutputToolsParser query = "What is 3 * 12? Also, what is 11 + 49?"
tool_chain = llm_with_tools | JsonOutputToolsParser() llm_with_tools.invoke(query).tool_calls
tool_chain.invoke("what's 3 * 12")
``` ```
```text ```text
[{'type': 'Multiply', 'args': {'a': 3, 'b': 12}}] [{'name': 'Multiply',
'args': {'a': 3, 'b': 12},
'id': 'call_viACG45wBz9jYzljHIwHamXw'},
{'name': 'Add',
'args': {'a': 11, 'b': 49},
'id': 'call_JMFUqoi5L27rGeMuII4MJMWo'}]
``` ```
Or back to the original Pydantic class: The `.tool_calls` attribute should contain valid tool calls. Note that on occasion,
model providers may output malformed tool calls (e.g., arguments that are not
valid JSON). When parsing fails in these cases, instances
of [InvalidToolCall](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.InvalidToolCall.html#langchain_core.messages.tool.InvalidToolCall)
are populated in the `.invalid_tool_calls` attribute. An `InvalidToolCall` can have
a name, string arguments, identifier, and error message.
If desired, [output parsers](/docs/modules/model_io/output_parsers) can further
process the output. For example, we can convert back to the original Pydantic class:
```python ```python
from langchain_core.output_parsers.openai_tools import PydanticToolsParser from langchain_core.output_parsers.openai_tools import PydanticToolsParser
tool_chain = llm_with_tools | PydanticToolsParser(tools=[Multiply]) chain = llm_with_tools | PydanticToolsParser(tools=[Multiply, Add])
tool_chain.invoke("what's 3 * 12") chain.invoke(query)
``` ```
```text ```text
[Multiply(a=3, b=12)] [Multiply(a=3, b=12), Add(a=11, b=49)]
``` ```
If our model isnt using the tool, as is the case here, we can force ### Streaming
tool usage by specifying `tool_choice="any"` or by specifying the name
of the specific tool we want used: When tools are called in a streaming context,
[message chunks](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk)
will be populated with [tool call chunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolCallChunk.html#langchain_core.messages.tool.ToolCallChunk)
objects in a list via the `.tool_call_chunks` attribute. A `ToolCallChunk` includes
optional string fields for the tool `name`, `args`, and `id`, and includes an optional
integer field `index` that can be used to join chunks together. Fields are optional
because portions of a tool call may be streamed across different chunks (e.g., a chunk
that includes a substring of the arguments may have null values for the tool name and id).
Because message chunks inherit from their parent message class, an
[AIMessageChunk](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessageChunk.html#langchain_core.messages.ai.AIMessageChunk)
with tool call chunks will also include `.tool_calls` and `.invalid_tool_calls` fields.
These fields are parsed best-effort from the message's tool call chunks.
Note that not all providers currently support streaming for tool calls.
Example:
```python ```python
llm_with_tools = llm.bind_tools([Multiply], tool_choice="Multiply") async for chunk in llm_with_tools.astream(query):
llm_with_tools.invoke("what's 3 * 12") print(chunk.tool_call_chunks)
``` ```
```text ```text
AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_qIP2bJugb67LGvc6Zhwkvfqc', 'type': 'function', 'function': {'name': 'Multiply', 'arguments': '{"a": 3, "b": 12}'}}]}) []
[{'name': 'Multiply', 'args': '', 'id': 'call_Al2xpR4uFPXQUDzGTSawMOah', 'index': 0}]
[{'name': None, 'args': '{"a"', 'id': None, 'index': 0}]
[{'name': None, 'args': ': 3, ', 'id': None, 'index': 0}]
[{'name': None, 'args': '"b": 1', 'id': None, 'index': 0}]
[{'name': None, 'args': '2}', 'id': None, 'index': 0}]
[{'name': 'Add', 'args': '', 'id': 'call_VV6ck8JSQ6joKtk2xGtNKgXf', 'index': 1}]
[{'name': None, 'args': '{"a"', 'id': None, 'index': 1}]
[{'name': None, 'args': ': 11,', 'id': None, 'index': 1}]
[{'name': None, 'args': ' "b": ', 'id': None, 'index': 1}]
[{'name': None, 'args': '49}', 'id': None, 'index': 1}]
[]
``` ```
If we wanted to force that a tool is used (and that it is used only Note that adding message chunks will merge their corresponding tool call chunks. This is the principle by which LangChain's various [tool output parsers](/docs/modules/model_io/output_parsers/types/openai_tools/) support streaming.
once), we can set the `tool_choice` argument to the name of the tool:
For example, below we accumulate tool call chunks:
```python ```python
llm_with_multiply = llm.bind_tools([Multiply], tool_choice="Multiply") first = True
llm_with_multiply.invoke( async for chunk in llm_with_tools.astream(query):
"make up some numbers if you really want but I'm not forcing you" if first:
) gathered = chunk
first = False
else:
gathered = gathered + chunk
print(gathered.tool_call_chunks)
``` ```
```text ```text
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_f3DApOzb60iYjTfOhVFhDRMI', 'function': {'arguments': '{"a":5,"b":10}', 'name': 'Multiply'}, 'type': 'function'}]}) []
[{'name': 'Multiply', 'args': '', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
[{'name': 'Multiply', 'args': '{"a"', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
[{'name': 'Multiply', 'args': '{"a": 3, ', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 1', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a"', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11,', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": ', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": 49}', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
[{'name': 'Multiply', 'args': '{"a": 3, "b": 12}', 'id': 'call_2MG1IGft6WmgMooqZgJ07JX6', 'index': 0}, {'name': 'Add', 'args': '{"a": 11, "b": 49}', 'id': 'call_uGot9MOHDcz67Bj0h13c7QA5', 'index': 1}]
``` ```
For more see the [ChatOpenAI API
reference](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI.bind_tools).
## Defining functions schemas
In case you need to access function schemas directly, LangChain has a built-in converter that can turn
Python functions, Pydantic classes, and LangChain Tools into the OpenAI format JSON schema:
### Python function
```python ```python
import json print(type(gathered.tool_call_chunks[0]["args"]))
from langchain_core.utils.function_calling import convert_to_openai_tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers together.
Args:
a: First integer
b: Second integer
"""
return a * b
print(json.dumps(convert_to_openai_tool(multiply), indent=2))
``` ```
```text ```text
{ <class 'str'>
"type": "function",
"function": {
"name": "multiply",
"description": "Multiply two integers together.",
"parameters": {
"type": "object",
"properties": {
"a": {
"type": "integer",
"description": "First integer"
},
"b": {
"type": "integer",
"description": "Second integer"
}
},
"required": [
"a",
"b"
]
}
}
}
``` ```
### Pydantic class And below we accumulate tool calls to demonstrate partial parsing:
```python ```python
from langchain_core.pydantic_v1 import BaseModel, Field first = True
async for chunk in llm_with_tools.astream(query):
if first:
gathered = chunk
first = False
else:
gathered = gathered + chunk
print(gathered.tool_calls)
class multiply(BaseModel):
"""Multiply two integers together."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
print(json.dumps(convert_to_openai_tool(multiply), indent=2))
``` ```
```text ```text
{ []
"type": "function", []
"function": { [{'name': 'Multiply', 'args': {}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
"name": "multiply", [{'name': 'Multiply', 'args': {'a': 3}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
"description": "Multiply two integers together.", [{'name': 'Multiply', 'args': {'a': 3, 'b': 1}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
"parameters": { [{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
"type": "object", [{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}]
"properties": { [{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
"a": { [{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
"description": "First integer", [{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
"type": "integer" [{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11, 'b': 49}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
}, [{'name': 'Multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_z3B4o82SQDY5NCnmrXIcVQo4'}, {'name': 'Add', 'args': {'a': 11, 'b': 49}, 'id': 'call_zPAyMWr8hN1q083GWGX2dSiB'}]
"b": {
"description": "Second integer",
"type": "integer"
}
},
"required": [
"a",
"b"
]
}
}
}
``` ```
### LangChain Tool
```python ```python
from typing import Any, Type print(type(gathered.tool_calls[0]["args"]))
from langchain_core.tools import BaseTool
class MultiplySchema(BaseModel):
"""Multiply tool schema."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
class Multiply(BaseTool):
args_schema: Type[BaseModel] = MultiplySchema
name: str = "multiply"
description: str = "Multiply two integers together."
def _run(self, a: int, b: int, **kwargs: Any) -> Any:
return a * b
# Note: we're passing in a Multiply object not the class itself.
print(json.dumps(convert_to_openai_tool(Multiply()), indent=2))
``` ```
```text ```text
{ <class 'dict'>
"type": "function",
"function": {
"name": "multiply",
"description": "Multiply two integers together.",
"parameters": {
"type": "object",
"properties": {
"a": {
"description": "First integer",
"type": "integer"
},
"b": {
"description": "Second integer",
"type": "integer"
}
},
"required": [
"a",
"b"
]
}
}
}
``` ```
## Next steps ## Next steps
- **Output parsing**: See [OpenAI Tools output - **Output parsing**: See [OpenAI Tools output