What is .bind_tools()?
.bind_tools() attaches structured tool definitions to a chat model, enabling function calling (also called tool use). When you call model.bind_tools(tools), LangChain serialises each tool's input schema — whether it comes from a Pydantic model, a @tool-decorated function, or a raw JSON schema dict — into the provider-specific wire format: OpenAI's tools parameter, Anthropic's tools array, Gemini's function_declarations, and so on. The method returns a new Runnable that sends that schema on every subsequent request.
The model never directly executes the tools. Instead, it emits an AIMessage with a tool_calls list indicating which tool to invoke and with what arguments. Your application code — or a ToolNode in LangGraph — is responsible for dispatching the call, collecting the result, appending a ToolMessage back to the conversation, and re-invoking the model. This separation is intentional: it keeps the model stateless and lets you intercept, validate, or mock tool calls in tests.
.bind_tools() returns a new Runnable that wraps the original model — the original model object is unchanged. You can chain it with other .bind_*() or .with_config() calls, and the result supports the full Runnable interface including .invoke(), .ainvoke(), .stream(), and .batch(). For agents, create one model_with_tools variable at startup and reuse it per invocation rather than rebinding on every call, which avoids redundant serialisation overhead.
Use Cases
- • Function calling
- • Agent tool binding
- • Structured output
- • API schema declaration
- • Tool selection
- • Agentic workflows
Key Features
- ✓ Attach tools to models
- ✓ Auto schema generation
- ✓ Multiple tools
- ✓ Provider-agnostic
- ✓ Tool call responses
- ✓ Structured output
When NOT to Use
For models without tool-calling support. When you only need structured output without tool dispatch — use .with_structured_output() instead.
Notes
Not all models support tool calling
GPT-4o, Claude 3.5+, Gemini 1.5+, and Mistral Large support .bind_tools(). Older GPT-3.5-turbo API versions and some fine-tuned models do not. Calling bind_tools on an unsupported model raises NotImplementedError at runtime, not at bind time.
Models can return multiple tool calls in one response
GPT-4o and Claude 3+ emit parallel tool calls — multiple entries in response.tool_calls — when a query requires more than one tool. Always iterate over response.tool_calls as a list, not just call response.tool_calls[0], or you will silently drop tool invocations.
Tool schema quality directly affects call accuracy
The model reads your tool's description field to decide when to call it and the args_schema to know what arguments to pass. Vague descriptions cause missed calls; overly broad input types cause hallucinated arguments. Use Pydantic models with explicit field descriptions for production tools.
tool_choice forces a specific tool — use with caution
Passing tool_choice={"type": "function", "function": {"name": "my_tool"}} (OpenAI) forces the model to call that exact tool. In a multi-step agent loop, this can create an infinite cycle if the forced tool is also the one that triggers re-invocation. Reserve it for single-turn structured-output extraction.
Method Signature
model_with_tools = model.bind_tools(tools, tool_choice=None)
Parameters
| Parameter | Type | Required | Purpose |
|---|---|---|---|
| tools | List[BaseTool | Callable | dict] | Yes | List of tool definitions (functions, Pydantic models, or dicts) |
| tool_choice | str | dict | None | No | Force a specific tool or "any" / "auto" |
Return Value
Type:
Runnable
Description:
New Runnable with tools bound
Example Output:
model.bind_tools([search, calculate])
Code Examples
Basic tool binding with @tool decorator
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers."""
return a * b
model = ChatOpenAI(model="gpt-4o")
model_with_tools = model.bind_tools([multiply])
response = model_with_tools.invoke([HumanMessage(content="What is 6 * 7?")])
print(response.tool_calls) # [{"name": "multiply", "args": {"a": 6, "b": 7}}]
Full tool-call loop: invoke → execute → re-invoke
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, ToolMessage
@tool
def get_weather(city: str) -> str:
"""Get current weather for a city."""
return f"Sunny, 22°C in {city}"
model = ChatOpenAI(model="gpt-4o")
model_with_tools = model.bind_tools([get_weather])
messages = [HumanMessage(content="Whats the weather in Tokyo?")]
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)
# Execute each requested tool call
for call in ai_msg.tool_calls:
result = get_weather.invoke(call["args"])
messages.append(ToolMessage(content=result, tool_call_id=call["id"]))
# Re-invoke to get the final answer
final = model_with_tools.invoke(messages)
print(final.content)
Handling parallel tool calls from GPT-4o
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
@tool
def search(query: str) -> str:
"""Search the web for a query."""
return f"Results for: {query}"
@tool
def calculate(expression: str) -> float:
"""Evaluate a math expression."""
return eval(expression) # noqa: S307 (demo only)
model = ChatOpenAI(model="gpt-4o")
# Parallel tool calls: gpt-4o may emit both in one response
model_with_tools = model.bind_tools([search, calculate])
response = model_with_tools.invoke(
[HumanMessage(content="Search for LangChain and calculate 15 * 8")]
)
print(f"Tool calls received: {len(response.tool_calls)}")
for call in response.tool_calls:
print(call["name"], call["args"])
Common Mistakes
❌ model.bind_tools(tool) # Single tool not in list
✅ model.bind_tools([tool1, tool2]) # List of tools
Related LangChain References
Browse the full LangChain API reference index to explore more classes, methods, and decorators, or start with the LangChain introduction tutorial for end-to-end context on building with .bind_tools() and the wider framework.
.bind_tools() FAQ
What does .bind_tools() do in LangChain?
Attach tools to a model for function calling. .bind_tools() attaches structured tool definitions to a chat model, enabling function calling (also called tool use). When you call model.bind_tools(tools), LangChain serialises each tool's input schema — whether it comes from a Pydantic model, a @tool-decorated function, or a raw JSON schema dict — into the provider-specific wire format: OpenAI's tools parameter, Anthropic's tools array, Gemini's function_declarations, and so on. The method returns a new Runnable that sends …
Which LangChain classes support .bind_tools()?
.bind_tools() is available on Chat models with tool support. Pin your installed LangChain version and verify the method exists in that release before deploying.
When should I use .bind_tools()?
Use .bind_tools() when your LangChain chains, agents, or pipelines need the behavior described in this guide.
What does .bind_tools() return?
.bind_tools() returns a Runnable. New Runnable with tools bound
Does .bind_tools() have an async equivalent?
.bind_tools() does not have a documented async variant. Avoid .bind_tools() For models without tool-calling support. When you only need structured output without tool dispatch — use .with_structured_output() instead.
Where can I explore more LangChain API reference pages?
Open the LangChain API reference index on DevShelfHub to browse classes, methods, and decorators, each with runnable examples, parameters, common mistakes, and cross-links.