iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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

Test yourself

Q1. A tool decorator gives an LLM…
Q2. Tool arguments should be…
Q3. Bind tools to a chat model with…

Discussion

Loading…