Choosing between multiple tools
In our Quickstart we went
over how to build a Chain that calls a single multiply
tool. Now letโs
take a look at how we might augment this chain so that it can pick from
a number of tools to call. Weโll focus on Chains since
Agents can route between
multiple tools by default.
Setupโ
Weโll need to install the following packages for this guide:
%pip install --upgrade --quiet langchain langchain-openai
And set these environment variables:
import getpass
import os
os.environ["OPENAI_API_KEY"] = getpass.getpass()
# If you'd like to use LangSmith, uncomment the below
# os.environ["LANGCHAIN_TRACING_V2"] = "true"
# os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()
Toolsโ
Recall we already had a multiply
tool:
from langchain_core.tools import tool
@tool
def multiply(first_int: int, second_int: int) -> int:
"""Multiply two integers together."""
return first_int * second_int
And now we can add to it a exponentiate
and add
tool:
@tool
def add(first_int: int, second_int: int) -> int:
"Add two integers."
return first_int + second_int
@tool
def exponentiate(base: int, exponent: int) -> int:
"Exponentiate the base to the exponent power."
return base**exponent
The main difference between using one Tool and many, is that in the case
of many we canโt be sure which Tool the model will invoke. So we cannot
hardcode, like we did in the
Quickstart, a specific
tool into our chain. Instead weโll add call_tool_list
, a
RunnableLambda
that takes the JsonOutputToolsParser
output and
actually builds the end of the chain based on it, meaning it appends the
Tools that were envoked to the end of the chain at runtime. We can do
this because LCEL has the cool property that in any Runnable (the core
building block of LCEL) sequence, if one component returns more
Runnables, those are run as part of the chain.
from operator import itemgetter
from typing import Union
from langchain.output_parsers import JsonOutputToolsParser
from langchain_core.runnables import (
Runnable,
RunnableLambda,
RunnableMap,
RunnablePassthrough,
)
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-3.5-turbo")
tools = [multiply, exponentiate, add]
model_with_tools = model.bind_tools(tools)
tool_map = {tool.name: tool for tool in tools}
def call_tool(tool_invocation: dict) -> Union[str, Runnable]:
"""Function for dynamically constructing the end of the chain based on the model-selected tool."""
tool = tool_map[tool_invocation["type"]]
return RunnablePassthrough.assign(output=itemgetter("args") | tool)
# .map() allows us to apply a function to a list of inputs.
call_tool_list = RunnableLambda(call_tool).map()
chain = model_with_tools | JsonOutputToolsParser() | call_tool_list
chain.invoke("What's 23 times 7")
[{'type': 'multiply',
'args': {'first_int': 23, 'second_int': 7},
'output': 161}]
chain.invoke("add a million plus a billion")
[{'type': 'add',
'args': {'first_int': 1000000, 'second_int': 1000000000},
'output': 1001000000}]
chain.invoke("cube thirty-seven")
[{'type': 'exponentiate',
'args': {'base': 37, 'exponent': 3},
'output': 50653}]