Tools
Tools are callable functions an LLM can invoke. With a name, description, and typed schema, the model picks the right one for the user’s request and supplies the arguments — the heart of agentic apps.
Define tools, bind to a model, call via agent
EXAMPLE
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
from langchain_anthropic import ChatAnthropic
import datetime, requests
# 1) Define tools — name + docstring become the LLM-facing description
@tool
def get_current_time(timezone: str = 'UTC') -> str:
"""Return the current time in the given IANA timezone, e.g. 'Australia/Sydney'."""
from zoneinfo import ZoneInfo
return datetime.datetime.now(ZoneInfo(timezone)).isoformat(timespec='seconds')
@tool
def search_web(query: str, max_results: int = 5) -> list[dict]:
"""Search the web. Returns a list of {title, url, snippet}."""
r = requests.get('https://api.tavily.com/search', json={'query': query, 'max_results': max_results})
r.raise_for_status()
return r.json()['results']
@tool
def get_user(user_id: int) -> dict:
"""Fetch a user record by ID."""
return db.users.find_one({'id': user_id})
tools = [get_current_time, search_web, get_user]
# 2) Bind tools to a chat model
llm = ChatAnthropic(model='claude-opus-4-7', temperature=0)
llm_with_tools = llm.bind_tools(tools)
# 3) Single-turn — model returns tool_calls
resp = llm_with_tools.invoke([HumanMessage(content='What time is it in Sydney?')])
print(resp.tool_calls)
# [{'name': 'get_current_time', 'args': {'timezone': 'Australia/Sydney'}, 'id': '...'}]
# 4) Resolve tool calls + loop until done
from langchain_core.messages import AIMessage, ToolMessage
messages = [HumanMessage(content='What time is it in Sydney?')]
while True:
resp = llm_with_tools.invoke(messages)
messages.append(resp)
if not resp.tool_calls:
break
for call in resp.tool_calls:
fn = next(t for t in tools if t.name == call['name'])
result = fn.invoke(call['args'])
messages.append(ToolMessage(content=str(result), tool_call_id=call['id']))
print(messages[-1].content)
# 5) Use LangGraph's prebuilt agent instead of hand-rolling the loop
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(llm, tools)
out = agent.invoke({'messages': [('user', 'Find me the latest news on Python 3.13')]})
print(out['messages'][-1].content)
# 6) Tool best practices
# • Write clear docstrings — they're the LLM's spec
# • Validate args (Pydantic, type hints) — model can hallucinate types
# • Make tools idempotent or transactional — agents retry
# • Set timeouts on network tools — agents wait
# • Restrict scope — one tool, one verb (`get_user` not `manage_user`)
Why it matters
Tool descriptions are the prompt the model uses to pick them. Write them like you’d brief a junior dev: what it does, what each argument is, what comes back. The shorter and clearer, the better the routing.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
from langchain_core.tools import tool
@tool
def multiply(a: int, b: int) -> int:
'''Multiply two integers.'''
return a * b
bound = llm.bind_tools([multiply])
result = bound.invoke('what is 6 * 7?')
print(result.tool_calls)
Try it Yourself »
Exercise
Mark a Python fn as a Tool.
def multiply(a: int, b: int) -> int: ...
Starts with @.
Discussion
Loading…