LangChain Tools 是什么
在 LangChain 中,Tool(工具)是一个 LLM 可以"调用"的函数,赋予模型与外部世界交互的能力。Tool 本质上是一个 Python 函数加上一个描述——LLM 读取描述后自主决定何时调用、传入什么参数。
Tool
一个带有名称(name)、描述(description)和参数 Schema 的可调用单元。LLM 根据描述判断工具的用途,根据 Schema 生成调用参数。
@tool 装饰器
LangChain 0.3.x 中将普通 Python 函数转换为 Tool 的最简洁方式。函数的 docstring 成为工具描述,类型注解成为参数 Schema。
ToolNode
LangGraph 中处理工具调用的节点,负责执行 LLM 请求的工具调用并将结果返回给模型。
ReAct(Reason + Act)
Agent 的基本工作模式:模型交替进行推理(Thought)和行动(Action),每次 Action 调用一个工具,观察结果(Observation),再决定下一步。
用 @tool 定义工具
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
import requests
# 方式一:@tool 装饰器(最简洁,推荐)
@tool
def search_wikipedia(query: str) -> str:
"""在维基百科搜索信息并返回摘要。
适用于:查询历史、科学、人物、地理等知识性问题。
不适用于:实时数据(股价、天气)或私有数据。
Args:
query: 搜索关键词,如 "Python programming language"
"""
url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{query.replace(' ', '_')}"
resp = requests.get(url)
if resp.status_code == 200:
return resp.json().get("extract", "未找到相关信息")[:500]
return f"搜索失败,状态码: {resp.status_code}"
@tool
def calculate(expression: str) -> str:
"""计算数学表达式,返回结果。
支持:加减乘除、幂运算、括号、math 模块函数(如 sqrt, sin, cos)。
不支持:代码执行、文件操作。
Args:
expression: 数学表达式字符串,如 "2**10 + 3*5" 或 "math.sqrt(144)"
"""
import math
try:
# 安全的 eval:只允许 math 模块
result = eval(expression, {"__builtins__": {}, "math": math})
return str(result)
except Exception as e:
return f"计算错误: {e}"
# 将工具绑定到模型
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools([search_wikipedia, calculate])
# 直接调用(模型决定是否调用工具)
response = llm_with_tools.invoke("Python 语言是谁发明的?另外 2 的 10 次方是多少?")
print(response.tool_calls)
# [{'name': 'search_wikipedia', 'args': {'query': 'Python programming language'}},
# {'name': 'calculate', 'args': {'expression': '2**10'}}]
构建完整 ReAct Agent
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_openai import ChatOpenAI
import json
def run_agent(question: str, tools: list, max_iterations: int = 5):
"""简易 ReAct Agent:自主调用工具直到得出答案"""
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools)
tools_map = {t.name: t for t in tools}
messages = [HumanMessage(content=question)]
for i in range(max_iterations):
response = llm_with_tools.invoke(messages)
messages.append(response)
# 如果没有工具调用,说明模型已得出最终答案
if not response.tool_calls:
return response.content
# 执行所有工具调用
for tool_call in response.tool_calls:
tool_name = tool_call["name"]
tool_args = tool_call["args"]
# 调用对应工具
result = tools_map[tool_name].invoke(tool_args)
# 将工具结果添加到消息历史
messages.append(ToolMessage(
content=str(result),
tool_call_id=tool_call["id"]
))
return "达到最大迭代次数,任务未完成"
# 使用:
answer = run_agent(
"Python 是谁发明的?他的生日是哪天?从发明 Python 到现在过了多少天?",
tools=[search_wikipedia, calculate]
)
print(answer)
常见误区:工具描述不清导致调用错误
LLM 完全依赖工具描述来决定何时调用工具以及如何传参。描述越清晰具体,调用越准确。描述中应包含:工具的用途、适用场景、不适用场景、参数格式示例。避免模糊描述如"处理数据"。
生产建议:使用 LangGraph 替代手写 ReAct
上面的手写 ReAct 循环是教学示例,生产环境应使用 LangGraph 构建 Agent(第8章),它提供持久化状态、错误重试、Human-in-the-loop 等企业级特性。
本章小结
- 工具(Tool)= Python 函数 + 描述 + 参数 Schema;描述质量决定调用准确性
- @tool 装饰器是定义工具的最简洁方式,docstring 成为描述,类型注解成为 Schema
- ReAct 循环:推理 → 调用工具 → 观察结果 → 再推理,直到完成任务
- 生产 Agent 推荐使用 LangGraph(下一章),而非手写 ReAct 循环