Use @tool to define a Python function as a tool and bind it to the Agent - the model will automatically decide when to call it, and the call result will be fed back to the model, forming a ReAct inference-action cycle.
LLM itself can only process text - it has no real-time networking capabilities, cannot execute code, and cannot call external APIs.ToolIt is the mechanism that gives Agent these capabilities: after packaging the Python function into a tool, the model can "call" it during the inference process, obtain real data, and then incorporate the results into subsequent inference.
Ask LLM "What is the square root of 467" - it might give 21.6 or a similar approximation, butNot guaranteed to be accurate. After binding thesquare_root tool, the model calls Python's x ** 0.5 which returns the exact value 21.61018278497431. This is the fundamental value of the tool’s existence: to allow Agents to leverage external precise calculations or real-time data.
Provided by LangChain@toolDecorators turn ordinary Python functions into tools that can be called by Agent. This isRecommended standard writing。
from langchain.tools import tool
# 推荐写法:明确指定工具名称和描述
@tool("square_root", description="Calculate the square root of a number")
def square_root(x: float) -> float:
return x ** 0.5
@toolWhat does the decorator do? It wraps the function into a LangChainStructuredToolObject, which contains:
| property | source | illustrate |
|---|---|---|
name |
The first parameter of the decorator"square_root" |
The name identifier used when the model calls the tool |
description |
Decoratordescription=parameter |
Tell the model what this tool is used for,The model relies on this to decide when to call |
args_schema |
Type annotations for function parameters are automatically inferred | JSON Schema of parameters (x: float → {"x": {"type": "number"}}) |
func |
the decorated original function | actual execution logic |
descriptionIt is the core basis for model decision-making. The model cannot see the source code of the tool, only thenameanddescription. If the description is inaccurate, the model may call (or not call) the tool at the wrong time.A good tool description should: state clearly what the tool does, when it should be used, and what its input and output are.
After the tool is defined, it can be used directly without going through the Agent.tool.invoke()Test whether it functions correctly:
# 直接调用工具测试,不需要 Agent
result = square_root.invoke({"x": 467})
print(result) # → 21.61018278497431
Before binding the tool to the Agent, usetool.invoke()Independent testing tool logic. This can quickly check whether there is a bug in the tool itself or whether there is a problem when the Agent calls the tool - the two types of problems have completely different ways of repairing them.
passcreate_agentoftoolsParameters, pass the tool list to the Agent:
from langchain.agents import create_agent
from langchain.messages import HumanMessage
agent = create_agent(
model=deepseek_model(),
tools=[square_root], # 工具列表(可以绑定多个工具)
system_prompt="You are an arithmetic wizard. Use your tools to calculate the square root and square of any number."
)
question = HumanMessage(content="What is the square root of 467?")
response = agent.invoke({"messages": [question]})
print(response['messages'][-1].content)
The model doesn't "calculate" the value itself - it callssquare_rootTools for precise results21.61018278497431, and then use this value in the reply.
Agent's tool invocation followsReAct(Reasoning + Acting)model:
If necessary, you can cycle "Inference → Call Tool → Inference" multiple times until LLM thinks it can give the final answer.
HumanMessage("What is the square root of 467?")tool_calls=[{"name": "square_root", "args": {"x": 467}}]square_root(x=467),get21.61018278497431, encapsulated asToolMessageAfter the tool is called,response['messages']The list contains 4 messages, one more than a normal callToolMessage:
from pprint import pprint
pprint(response['messages'])
print("\n--- tool_calls 详情 ---")
print(response["messages"][1].tool_calls)
tool_call = response["messages"][1].tool_calls[0]
# 结构如下:
# {
# "name": "square_root", # 调用的工具名
# "args": {"x": 467}, # 传入的参数
# "id": "call_00_...", # 工具调用 ID(用于匹配 ToolMessage)
# "type": "tool_call" # 固定值
# }
When the tool is called,response['messages'][-1]It is the final text answer (AIMessage), not the tool result. The tool results inmessages[2]in ToolMessage. Usually we only needresponse['messages'][-1].contentGet the final text answer.
| practice | Recommended practices | avoid |
|---|---|---|
| Tool name | Verb + noun, clearly express function:"search_web"、"get_stock_price" |
Ambiguous name:"tool1"、"helper" |
| describe | Explain the purpose, when to use it, input and output. Example:"Search the web for real-time information. Use when the question requires current data." |
Too brief:"search" |
| type annotation | All parameters are type annotated (x: float), LangChain automatically generates schema |
Omit type annotations (resulting in inaccurate schema) |
| return value | Returns a string or serializable dict to facilitate model understanding | Return complex objects or binary data |
| Error handling | Returns a descriptive error string to let the model know the reason for the failure | Throws an exception (will interrupt the entire Agent execution) |
@tool("name", description="...")tool.invoke({"x": 467})Can be independently testedmessages[-1].content