LangChain LangGraph
Module 1 Module 2 Module 3
Module 1 · 基础能力
1 1.1a 基础模型
2 1.1b Prompting
3 1.2a Tools
4 1.2b Web Search
5 1.3 Memory
6 1.4 Multimodal
7 1.5 Personal Chef
SUM Module Summary
HomeLangChainModule 1 · 基础能力1.2a Tools
📓 Notebook 📖 Explained
LANGCHAIN MODULE 1 · LESSON 1.2a

工具(Tools)
@tool 装饰器 · Agent 工具调用 · ReAct 循环

@tool 定义 Python 函数为工具,绑定到 Agent——模型会自动决定何时调用,调用结果再反馈给模型,形成 ReAct 推理-行动循环。

1 什么是工具(Tool)?为什么 Agent 需要工具?

LLM 本身只能处理文本——它没有实时联网能力、无法执行代码、也不能调用外部 API。工具(Tool)就是赋予 Agent 这些能力的机制:把 Python 函数包装成工具后,模型可以在推理过程中"调用"它,获取真实数据,再把结果纳入后续推理。

工具调用 vs 直接让 LLM 回答

问 LLM "467 的平方根是多少"——它可能给出 21.6 或类似近似值,但不保证精确。绑定 square_root 工具后,模型会调用 Python 的 x ** 0.5 返回精确值 21.61018278497431。这是工具存在的根本价值:让 Agent 能利用外部精确计算或实时数据。

2 @tool 装饰器:将 Python 函数变为 LangChain 工具

LangChain 提供 @tool 装饰器,把普通 Python 函数变成可被 Agent 调用的工具。这是推荐的标准写法

推荐格式:指定 name 和 description

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

@tool 装饰器做了什么?它把函数包装成一个 LangChain StructuredTool 对象,该对象包含:

属性来源说明
name 装饰器第一个参数 "square_root" 模型调用工具时使用的名称标识
description 装饰器 description= 参数 告诉模型这个工具的用途,模型靠这个决定何时调用
args_schema 函数参数的类型注解自动推断 参数的 JSON Schema(x: float{"x": {"type": "number"}}
func 被装饰的原始函数 实际执行逻辑
description 的重要性

description 是模型决策的核心依据。模型看不到工具的源代码,只看 namedescription。如果描述不准确,模型可能在错误的时候调用(或不调用)工具。好的工具描述应该:说清楚工具做什么、什么时候应该用它、输入输出是什么。

3 直接调用工具:tool.invoke() 测试

工具定义好后,可以不经过 Agent,直接用 tool.invoke() 测试其功能是否正确:

# 直接调用工具测试,不需要 Agent
result = square_root.invoke({"x": 467})
print(result)  # → 21.61018278497431
执行输出
21.61018278497431
开发习惯建议

在把工具绑定到 Agent 之前,先用 tool.invoke() 独立测试工具逻辑。这样能快速排查是工具本身有 bug,还是 Agent 调用工具时出了问题——两类问题的修复方式完全不同。

4 把工具绑定到 Agent:tools 参数

通过 create_agenttools 参数,把工具列表传给 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 square root of **467** is approximately **21.6102**.

If you'd also like to know the square of any number, just let me know!

模型没有自己"算"这个值——它调用了 square_root 工具,得到精确结果 21.61018278497431,然后在回复里使用这个值。

5 ReAct 执行流程:模型如何决定调用哪个工具

Agent 的工具调用遵循 ReAct(Reasoning + Acting) 模式:

ReAct 工具调用循环

用户输入
HumanMessage
LLM 推理
决定是否调用工具
执行工具
ToolMessage 记录结果
LLM 再推理
基于工具结果回答
最终回复
AIMessage

如有需要可多次循环"推理 → 调用工具 → 推理",直到 LLM 认为可以给出最终答案为止

具体步骤详解

  1. 用户发送问题HumanMessage("What is the square root of 467?")
  2. LLM 看到工具列表:模型接收到工具的 name + description + args_schema。它推理:"这个问题需要计算,我应该调用 square_root 工具"
  3. 模型输出 tool_call:AIMessage 的 content 为空,但有 tool_calls=[{"name": "square_root", "args": {"x": 467}}]
  4. LangGraph 执行工具:调用 square_root(x=467),得到 21.61018278497431,封装为 ToolMessage
  5. LLM 基于结果回答:模型看到工具结果,生成最终的 AIMessage 文本响应

6 响应结构解析:tool_calls 与消息历史

工具调用后,response['messages'] 列表包含 4 条消息,比普通调用多了一条 ToolMessage

from pprint import pprint

pprint(response['messages'])
print("\n--- tool_calls 详情 ---")
print(response["messages"][1].tool_calls)
消息历史(4条)
1. HumanMessage: "What is the square root of 467?"

2. AIMessage: content=''  # 空内容,工具调用在 tool_calls 里
   tool_calls=[{
     'name': 'square_root',
     'args': {'x': 467},
     'id': 'call_00_...', 'type': 'tool_call'
   }]

3. ToolMessage: '21.61018278497431'  # 工具执行结果

4. AIMessage: "The square root of **467** is approximately **21.6102**..."

AIMessage.tool_calls 字段详解

tool_call = response["messages"][1].tool_calls[0]
# 结构如下:
# {
#   "name": "square_root",    # 调用的工具名
#   "args": {"x": 467},       # 传入的参数
#   "id": "call_00_...",      # 工具调用 ID(用于匹配 ToolMessage)
#   "type": "tool_call"       # 固定值
# }
一个常见误区

工具调用时,response['messages'][-1] 是最终的文字回答(AIMessage),而不是工具结果。工具结果在 messages[2] 的 ToolMessage 里。通常我们只需要 response['messages'][-1].content 获取最终文字答案。

7 工具设计最佳实践

实践推荐做法避免
工具名称 动词+名词,清晰表达功能:"search_web""get_stock_price" 模糊的名称:"tool1""helper"
描述 说明用途、何时用、输入输出。示例:"Search the web for real-time information. Use when the question requires current data." 过于简短:"search"
类型注解 所有参数都加类型注解(x: float),LangChain 自动生成 schema 省略类型注解(会导致 schema 不准确)
返回值 返回字符串或可序列化的 dict,方便模型理解 返回复杂对象或二进制数据
错误处理 返回描述性错误字符串,让模型知道失败原因 抛出异常(会中断整个 Agent 执行)
Lesson 1.2a 核心要点
@tool 装饰器
  • • 推荐格式:@tool("name", description="...")
  • • 自动从类型注解生成 JSON Schema
  • tool.invoke({"x": 467}) 可独立测试
工具调用流程
  • • 模型→ tool_call → 工具执行 → ToolMessage
  • • messages 列表:Human + AI(空) + Tool + AI(回答)
  • • 最终答案:messages[-1].content