Binding tools to a model
A model cannot use a tool it has never heard of. Binding is how you tell it what it has.
This is the line that most tutorials skip past, and it is the one that turns a chat model into something that can act.
from pretend_model import PretendModel
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
@tool
def lookup_order(order_id: str) -> str:
"""Look up the status of an order by its id."""
return f"Order {order_id} shipped on 3 March."
model = PretendModel().bind_tools([lookup_order])
reply = model.invoke([HumanMessage("Where is order A17?")])
print("content: ", repr(reply.content))
print("tool_calls:", reply.tool_calls)Read those two lines carefully
The content is empty. The model had nothing to say, because it did not want to say anything. It wanted to do something.
tool_calls is not empty. It holds a request: the name of the tool, the arguments to call it with, and an id. The model worked out that A17 was the order id from the question.
That is the whole mechanism. A model with tools bound can answer in two ways, with words or with a request, and your program looks at tool_calls to see which one happened.
Nothing has been called yet
This is the part people get wrong. The model did not run lookup_order. It cannot. It is a model, sitting behind an API, with no access to your machine.
It said please call this. Something has to read that request and actually call the function, and that something is your program. In the next lesson LangGraph does it for you.
from pretend_model import PretendModel
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
@tool
def lookup_order(order_id: str) -> str:
"""Look up the status of an order by its id."""
return f"Order {order_id} shipped on 3 March."
model = PretendModel().bind_tools([lookup_order])
request = model.invoke([HumanMessage("Where is order A17?")]).tool_calls[0]
print("the model asked for:", request["name"], "with", request["args"])
print("so we call it: ", lookup_order.invoke(request["args"]))Four lines, and you have done by hand exactly what an agent framework does for you. Read the request, call the function, and you have the answer.
bind_tools gives you back a new model with the tools attached. It does not change the one you had. Assigning the result, as above, is not optional.- Ask something with no order id in it and print
tool_callsagain. - Bind a second tool and ask a question that matches it.
- Print
requestin full and find the id field.
You understood something today that you didn't yesterday.