bind_tools: asking for a tool
bind_tools is a model method that gives the model a list of tools and returns a copy that can answer with a request to run one, delivered as tool_calls.
Last updated: 27 Sep, 2026 · LangChain 1.4
bind_tools: telling a model which tools exist
A tool has to be bound to a model before the model can ask for it. model.bind_tools([get_weather]) returns model_with_tools, a model that knows about the tool but does not run it. The other way is create_agent, which takes the model and the tools and does this for you; the next lesson covers it. Asked "What's the weather like in Boston?", model_with_tools replies with a tool call, a request naming the tool and its arguments, instead of an answer:
from langchain.chat_models import init_chat_model
model = init_chat_model("groq:openai/gpt-oss-120b")
from langchain.tools import tool
@tool
def get_weather(location:str)->str:
"""Get the weather at a location"""
return f"It's sunny in {location}"
model_with_tools=model.bind_tools([get_weather])
response = model_with_tools.invoke("What's the weather like in Boston?")
for tool_call in response.tool_calls:
print(f"Tool: {tool_call['name']}")
print(f"Args: {tool_call['args']}")Tool: get_weather
Args: {'location': 'Boston'}The model decided on its own that the question needs get_weather, and filled in location from the question. It did not run anything: the useful part of the reply is tool_calls.
The video then runs the tool execution loop. The question goes into a list of messages, and model_with_tools.invoke returns an AI message holding the tool call, which is appended to the list. For each tool call, get_weather.invoke(tool_call) runs the tool and returns a tool message, "It's sunny in Boston", which is appended too. A second model_with_tools.invoke on the whole list gives the answer: the weather in Boston is sunny.
Now the same round trip for the shop, using lookup_order from the tools lesson.
The bind_tools method
model_with_tools = model.bind_tools([my_tool]) # a copy that knows the tools
reply = model_with_tools.invoke(messages) # reply.tool_calls holds any requests
result = my_tool.invoke(reply.tool_calls[0]) # run the tool, get a ToolMessage backOne round trip has four steps: bind the tool, send the question, run the tool the model asked for, and send the result back for the answer. The model here is the real Groq model from the invoke, batch and stream lesson.
One round trip by hand
Start the file with lookup_order, the tool from Tools: a function the model can call.
from langchain.tools import tool
ORDERS = {"A17": "shipped on 3 March", "C40": "waiting for stock"}
@tool
def lookup_order(order_id: str) -> str:
"""Look up an order's shipping status by its id, such as A17."""
status = ORDERS.get(order_id)
return f"{order_id} {status}." if status else f"{order_id} is not an order we have."Bind one tool and send a question. The model replies with a request, not text.
from langchain.messages import HumanMessage, SystemMessage
from langchain.chat_models import init_chat_model
model = init_chat_model("groq:openai/gpt-oss-120b", temperature=0).bind_tools([lookup_order]) # uses your GROQ_API_KEY
system = SystemMessage("You are the support assistant for a small online shop. Answer in one or two short sentences, using only what the tools returned.")
question = HumanMessage("Where is my order A17?")
request = model.invoke([system, question])
print(repr(request.text)) # the reply's text
print(request.tool_calls)''
[{'name': 'lookup_order', 'args': {'order_id': 'A17'}, 'id': 'fc_9c06d627-6839-4c3a-a46e-18f39aedc8bc', 'type': 'tool_call'}]Empty text, '', and one tool call: the model asked for lookup_order with order_id set to A17, and it is up to your code to run it.
Now run that tool and send its result back for the answer.
call = request.tool_calls[0]
result = lookup_order.invoke(call)
print(type(result).__name__, result.text)
answer = model.invoke([system, question, request, result])
print(answer.text)ToolMessage A17 shipped on 3 March. Your order A17 was shipped on 3 March.
Something had to run the tool and hand the result back, and here that was your code. create_agent is this loop written once, run until the model stops asking for tools.
Reading the tool call and answer
bind_toolsreturns a copy of the model that holds the tool list; the model itself is unchanged.- Sent back with the question and the request, the
ToolMessagegives the model what it needs to write the answer.
A text reply vs a tool call
| Text reply | Tool call | |
|---|---|---|
reply.text | The answer in words | Empty |
reply.tool_calls | Empty list | One dict per tool: name, args, id |
| Your code then | Shows it to the user | Runs the tool and calls the model again |
When to bind tools
- Any assistant that must fetch data before answering: an order status, a price, a record.
- Letting the model pick which of several tools to call, and with what arguments.
ToolMessage carries the call's id, and the model matches its answer to the request by that id.Related
- Previous: Tools: a function the model can call
- Next: create_agent: the agent loop
- Reference: Tool calling
- Bind no tools and invoke the same question. Which reply do you get?
- Ask "Where are A17 and C40?" and count the tool calls; a model may ask for both in one reply or one at a time.
- Print
result.tool_call_idand compare it withcall["id"].
You understood something today that you didn't yesterday.