langchain-ai/langchain
Tools
The BaseTool abstraction lets a chat model invoke arbitrary code. Source: libs/core/langchain_core/tools/.
Purpose
A "tool" is a callable wrapped with:
- a name (what the model uses to invoke it)
- a description (what the model reads to decide when to use it)
- an args schema (a Pydantic model or JSON Schema that the model fills)
- an output schema (optional)
- sync and async execution functions
The model's tool-calling endpoint receives the schemas; the runtime maps a tool call back to the right callable and runs it. BaseChatModel.bind_tools(tools) is how tools attach to a model.
Directory layout
libs/core/langchain_core/tools/
├── __init__.py
├── base.py # BaseTool (~55 KB), the @tool decorator helpers
├── convert.py # tool() decorator, convert_runnable_to_tool, JSON-schema conversion
├── render.py # render_text_description (for prompt rendering)
├── retriever.py # create_retriever_tool
├── simple.py # Tool (the simplest concrete subclass)
└── structured.py # StructuredToolKey abstractions
| Symbol | File | Description |
|---|---|---|
BaseTool |
libs/core/langchain_core/tools/base.py |
Abstract base; defines name, description, args_schema, _run, _arun, invoke, ainvoke |
Tool |
libs/core/langchain_core/tools/simple.py |
Simplest subclass — wraps a single function with a string args schema |
StructuredTool |
libs/core/langchain_core/tools/structured.py |
Tool with an arbitrary Pydantic args schema |
@tool (decorator) |
libs/core/langchain_core/tools/convert.py |
Decorate a Python function and get a BaseTool automatically |
convert_runnable_to_tool |
libs/core/langchain_core/tools/convert.py |
Wrap any Runnable as a BaseTool |
create_retriever_tool |
libs/core/langchain_core/tools/retriever.py |
Make a BaseRetriever callable as a tool |
render_text_description |
libs/core/langchain_core/tools/render.py |
Render a list of tools as text for prompt-based agents |
ToolException |
libs/core/langchain_core/tools/base.py |
Raise to signal a recoverable tool error to the model |
InjectedToolArg, InjectedToolCallId, InjectedState, InjectedStore |
libs/core/langchain_core/tools/base.py |
Annotation markers that exclude args from the schema and inject them at runtime |
How @tool works
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(..., description="What to search for")
max_results: int = Field(5, description="How many results to return")
@tool(args_schema=SearchInput)
def search(query: str, max_results: int = 5) -> list[str]:
"""Search the web for the given query."""
return _do_search(query, max_results)The decorator:
- Inspects the function signature and docstring.
- Generates a Pydantic args schema (or uses
args_schema=if you provide one). - Builds the JSON Schema the model will see.
- Returns a
BaseToolinstance whose_runcalls the function.
You can also write @tool without arguments — the function's name and docstring become the tool's name and description, and a schema is derived from the type hints.
Async tools
A function decorated with @tool may be async; BaseTool.ainvoke will await it. For mixed sync/async tools, the base class falls back to running the sync _run in a thread when only _arun is requested, and vice versa.
Injected arguments
Sometimes a tool needs context that the model shouldn't see — for example, the user's session, the agent's full state, the call ID, or a backing key-value store. Annotate the parameter:
from typing import Annotated
from langchain_core.tools import InjectedState, InjectedToolCallId
@tool
def remember(
fact: str,
state: Annotated[dict, InjectedState],
tool_call_id: Annotated[str, InjectedToolCallId],
) -> str:
"""Remember a fact."""
state["facts"].append(fact)
return "Remembered"state and tool_call_id are stripped from the schema the model sees, and supplied at execution time by langgraph's ToolNode or langchain.agents.
ToolException
Raising ToolException("invalid input") from a tool body returns the message to the model as a ToolMessage so it can correct itself, instead of bubbling up as an unhandled exception. This is the recoverable-error idiom every agent loop expects.
Tool calls and tool messages
When a model emits an AIMessage with tool_calls=[ToolCall(name="search", args={...}, id="...")], the agent's tool node:
- Looks up
BaseToolby name. - Validates
argsagainst the tool's args schema. - Calls
tool.invoke(args)(orainvoke). - Appends a
ToolMessage(tool_call_id="...", content=result)to the state. - Loops back to the model.
If the tool raises a non-ToolException, the agent's tool-retry middleware (if installed) can catch it and retry; otherwise the error surfaces.
Schema conversion for providers
Different providers want tool schemas in slightly different shapes:
- OpenAI Chat Completions:
{"type": "function", "function": {"name": ..., "description": ..., "parameters": ...}} - OpenAI Responses API: variants for
function,web_search,file_search - Anthropic:
{"name": ..., "description": ..., "input_schema": ...}
BaseChatModel.bind_tools(tools) calls partner-specific converters that flatten the LangChain BaseTool into the provider's expected shape. The convention is a convert_to_<provider>_tool helper in each partner — for example convert_to_anthropic_tool is exported from libs/partners/anthropic/langchain_anthropic/__init__.py.
Integration points
- Every chat model consumes tools via
bind_tools. - Agents in
langchain.agents.create_agentandlangchain_classic.agents.AgentExecutorconsume tools to build a tool node. - Retrievers can be exposed as tools via
create_retriever_tool. langgraph.prebuilt.ToolNodeis the runtime that actually executes tools inside an agent graph.
Entry points for modification
- For a new way to define a tool, extend
convert.py(e.g. parsing a different decorator metadata format). - For a new injection annotation, follow the pattern of
InjectedState/InjectedToolCallIdinbase.py. Annotated args are filtered before schema generation and supplied at runtime by the executor. - For provider-specific tool features (e.g. OpenAI's
strict=TrueJSON schema validation), the partner's_compat.pyis the right place — keep the base abstraction provider-agnostic.
Related
- primitives/language-models — how
bind_toolsattaches tools to a model - primitives/messages —
ToolCall,ToolMessage, content blocks for server-side tools - features/agents — how tools fit into the agent loop
- features/middleware — tool-call limits, tool retries, shell-tool sandboxing
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.