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 →

Tools: a function the model can call

A tool is a Python function wrapped by @tool so a model can ask for it by name: it takes its name from the function, its description from the docstring, and its argument schema from the type hints.

Last updated: 27 Sep, 2026 · LangChain 1.4

Creating a tool with @tool · from the Updated LangChain Version V1 Crash Course · 55:44 to 59:35

A tool is a schema plus a function

An agent is a model connected to tools. A tool can be an API request, a search engine, a news service, a tool built into LangChain or any function of your own. A model cannot fetch data from a database, search the web or run code by itself; a tool gives it that reach. Every tool pairs a schema, which is the tool's name, a description and its arguments, with a function that does the work when the model asks for it. The tool decorator, imported from langchain.tools, turns a normal Python function into a tool.

python
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}"

The docstring matters most. When the tool is bound to a model, the docstring is how the model learns what the function does and when to use it: the model sees the name get_weather, the docstring as the description, and one string argument, location, never the code. The return value is hard-coded here; a real tool would make an API or database request at that point.

The model never runs the tool either: it asks for it by name, and your code runs it. The shop's version keeps order statuses in a dictionary, and looking one up is ordinary Python.

Writing the lookup_order tool

Decorate a plain function with @tool. The docstring and the type hints do the work.

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

@tool turns the function into a tool object. It takes the name from the function, the description from the docstring, and the argument types from the type hints. Later lessons that use this tool define it again at the top of their own code, so each one runs on its own. To give the tool a different name from the function, write @tool("order_status").

What the model is told

Below the tool, print the three things @tool built. This is all the model ever learns about the tool.

Example
print(lookup_order.name)
print(lookup_order.description)
print(lookup_order.args)

The docstring says what the tool does and what an order id looks like, and the schema tells the model to send one string called order_id.

Running a tool yourself

Call a tool with a dictionary of arguments, the form a model produces.

Example
print(lookup_order.invoke({"order_id": "A17"}))
print(lookup_order.invoke({"order_id": "B22"}))

B22 is not in the dictionary, so the tool says so in words the model can pass on, not with an exception.

A tool without type hints

Leave the type hint off and the schema changes. Watch what the model is told about the argument.

Example
from langchain.tools import tool


@tool
def lookup_order(order_id):
    """Look up an order's shipping status by its id, such as A17."""
    return "unknown"


print(lookup_order.args)

Type hints are meant to be required, but 1.4.2 accepts the function anyway and drops the type from the schema, so a model is no longer told that order_id is a string. Keep the hints.

With type hints vs without

With <code>order_id: str</code>Without a hint
Schema{'title': 'Order Id', 'type': 'string'}{'title': 'Order Id'}
Model told the typeYes, a stringNo type at all
Accepted by 1.4.2YesYes, but keep the hint
A Tavily web search tool and a custom tool · from the Agentic AI With LangGraph And MCP Crash Course, Part 1 · 57:35 to 62:52

A real tool: web search with Tavily

get_weather is a fake: it says every city is sunny. A real tool reaches something outside your program. Tavily is a search API built for LLMs and RAG, and langchain-tavily wraps it as a ready-made tool. The video adds langchain-tavily to requirements.txt, installs it with uv add -r requirements.txt, and copies a free key from tavily.com into .env as TAVILY_API_KEY. Here this part is optional: it needs pip install langchain-tavily and that key. TavilySearch(max_results=2) makes the tool, and the video invokes it with "What is langgraph":

ExampleAPI key
from langchain_tavily import TavilySearch

tool = TavilySearch(max_results=2)
result = tool.invoke("What is langgraph")

for hit in result["results"]:
    print(round(hit["score"], 2), hit["title"])
    print("  ", hit["url"])

Each result carries a title, a URL and a relevance score, and max_results=2 kept the top two. A ready-made tool is used exactly like one you write: it has a name (tavily_search), a description and arguments, and a model can be given it in the same tools list.

A custom tool next to it

Next the video writes a custom function, multiply, with a docstring: a summary line, a description of each argument and of the return value. multiply has no @tool: bind_tools and create_agent accept a plain function and turn it into a tool themselves, from its name, type hints and docstring. The docstring follows the Google style, with an Args section. When a plain function is passed to bind_tools, those argument descriptions go into the schema; @tool ignores them unless you write @tool(parse_docstring=True).

python
def multiply(a:int,b:int)->int:
    """Multiply a and b

    Args:
        a (int): first int
        b (int): second int

    Returns:
        int: output int
    """
    return a*b

tools = [tool, multiply]

Both tools go into one list, and llm.bind_tools(tools) returns the model with the tools attached. Printed, it shows a RunnableBinding around ChatGroq that lists both tools, tavily_search and multiply. The bind_tools: asking for a tool lesson shows what a model does once tools are bound to it.

When to write a tool

  • Looking data up in your own database or API.
  • Taking an action, such as creating a ticket or sending an email.
  • Any step you want the model to request but your code to run and control.
Watch out. The docstring is not decoration: it is how the model decides when to call the tool. Delete it and @tool raises an error, and a vague one leaves the model guessing when to use it.
Try it yourself
  • Add a second argument, customer: str, and print args again.
  • Pass the name positionally, @tool("order_status"), and print the tool's name.
  • Delete the docstring and read the error.

Little by little, you're building something great.