LangChain (YT style)LangChain 1.4 · Python 3.12+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
46 small wins to finish your pathNext lesson →

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 and the tool execution loop · from the Updated LangChain Version V1 Crash Course · 59:30 to 63:50

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:

ExampleAPI keyFrom the video, run on Groq
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']}")

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

python
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 back

One 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.

python
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.

ExampleAPI key
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)

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.

ExampleAPI key
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)

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_tools returns a copy of the model that holds the tool list; the model itself is unchanged.
  • Sent back with the question and the request, the ToolMessage gives the model what it needs to write the answer.

A text reply vs a tool call

Text replyTool call
reply.textThe answer in wordsEmpty
reply.tool_callsEmpty listOne dict per tool: name, args, id
Your code thenShows it to the userRuns 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.
Watch out. Invoke the tool with the whole tool call, not only its arguments. The returned ToolMessage carries the call's id, and the model matches its answer to the request by that id.
Try it yourself
  • 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_id and compare it with call["id"].

You understood something today that you didn't yesterday.