一文读懂 LangChain Tools:从 @tool 装饰器到 StructuredTool,让大模型真正'动手'
写在前面
大语言模型(LLM)很强,但"只会说话"是它最大的短板。Tools(工具) 就是 LangChain 给出的解法——通过标准化的接口让模型能够调用外部能力,从而真正"动手"。
本文从一张概念图出发,系统讲清楚:
@tool装饰器如何把一个普通函数变成工具- 模型发出
tool_calls后发生了什么 - 怎样用 Pydantic 精细控制入参
@tool装饰后的StructuredTool类有什么属性和方法
目录
核心概念图
💡 核心思想:Tools 是 Model 与外部世界交互的接口,扩展了大语言模型的能力边界——搜索网页、执行代码、访问数据库、调用其它服务都可以。
什么是 Tools
Tools 是 model 与外部世界交互的接口,扩展模型能力边界,使大语言模型能够执行用户定义的动作。
常见可被封装为工具的能力:
- 外部 API 调用(天气、翻译、支付……)
- 数据查找(数据库、本地文件、向量库……)
- 环境交互(Shell、文件系统、浏览器……)
- 网络搜索(Tavily、Google、SerpAPI……)
工具的定义
最简定义:@tool 装饰器
from langchain_core.tools import tool
@tool
def calculate(expression: str) -> str:
"""Perform mathematical calculations and return the result.
Args:
expression: Mathematical expression to evaluate
(e.g., "2 + 3 * 4", "sqrt(16)", "sin(pi/2)")
Returns:
The calculated result as a string
"""
return str(eval(expression))
要点:
- 使用
@tool装饰器将普通函数转化为工具 - 入参类型必须明确标注(决定生成的 JSON Schema)
- docstring 即工具描述,告诉模型何时/如何使用该工具
工具的调用流程
from langchain.chat_models import init_chat_model
model = init_chat_model("openai:gpt-4.1")
model_with_tool = model.bind_tools([calculate])
response = model_with_tool.invoke("7的6次方等于多少?")
# AIMessage(
# content='',
# tool_calls=[{'name': 'calculate', 'args': {'expression': '7^6'},
# 'id': 'call_lscBZ8kO6nDlu3dp3wz29bGh', 'type': 'tool_call'}],
# ...
# )
多种工具函数的定义
工具的本质是函数,所以可以是任何业务能力。
本地数据浏览
import csv
import json
from typing import List, Dict
@tool
def search_csv(query: str, csv_path: str, limit: int = 10) -> str:
"""在本地 csv 文件中按关键词简单搜索,返回前 limit 条匹配记录(JSON 字符串)。"""
query_lower = query.lower()
results: List[Dict[str, str]] = []
with open(csv_path, "r", encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
if any(query_lower in str(v).lower() for v in row.values()):
results.append(row)
if len(results) >= limit:
break
return json.dumps(results, ensure_ascii=False, indent=2)
网络搜索 API
from typing import Literal
@tool
def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
include_raw_content: bool = False,
):
"""使用 Tavily 搜索引擎在互联网上搜索信息。"""
return tavily_client.search(
query,
max_results=max_results,
include_raw_content=include_raw_content,
topic=topic,
)
其它常见形态:数据库查询、Shell 命令、HTTP 接口、文件系统读写、子 Agent 调用等。
@tool 装饰器的属性自定义
LangChain 遵循「约定优于配置」,但也提供精细化控制。
自定义名称与描述
@tool("calculator", description="执行算术计算。用于解决任何数学问题。")
def calculate(expression: str) -> str:
"""评估数学表达式。"""
return str(eval(expression))
用 Pydantic 定义 args_schema
from pydantic import BaseModel, Field
from typing import Literal
class WeatherInput(BaseModel):
"""天气查询的输入参数。"""
location: str = Field(description="城市名称或坐标")
units: Literal["celsius", "fahrenheit"] = Field(
default="celsius", description="温度单位偏好"
)
将 schema 注入工具
@tool(args_schema=WeatherInput)
def get_weather(location: str, units: str = "celsius") -> str:
"""获取当前天气和可选预报。"""
temp = 22 if units == "celsius" else 72
return f"{location}当前天气: {temp}度{units[0].upper()}"
批量绑定到模型
model = init_chat_model("openai:gpt-4.1")
model_with_tool = model.bind_tools([calculate, internet_search, get_weather])
小结:
| 手段 | 作用 |
|---|---|
@tool("name", description="...") | 自定义工具名 / 描述 |
BaseModel + Field | 精确控制入参类型、默认值、描述 |
@tool(args_schema=...) | 把 Pydantic 模型挂到工具上 |
model.bind_tools([...]) | 一次性注册多个工具 |
StructuredTool 类
@tool 装饰后的函数本质上是 StructuredTool 实例。
关键属性
| 属性 | 作用 | 备注 |
|---|---|---|
name: str | 工具唯一名称 | 命名要直观、便于模型理解 |
description: str | 告诉模型如何/何时/为何使用 | 可附 few-shot 示例提升命中率 |
response_format | "content" 或 "content_and_artifact" | 默认 "content" |
关键方法
invoke/ainvoke:直接同步 / 异步调用工具get_input_schema:获取入参的 JSON Schemaget_output_schema:获取工具声明的输出 Pydantic 模型
整体知识地图
工具小结
- 工具的定义 — 使用
@tool装饰器定义工具,并bind_tools给模型。模型在需要时会发出tool_calls请求。 - 多类型工具函数 — 通过不同的函数承载各类能力:外部 API、数据查找、环境交互、网络搜索……
@tool的属性配置 — 遵循「约定优于配置」,同时也支持自定义名称、描述、参数 schema。StructuredTool类 —@tool装饰的函数最终转换为StructuredTool对象,拥有name/description/response_format属性以及invoke、get_input_schema、get_output_schema方法。
写在最后
Tools 是 LLM 从「对话」走向「Agent」的关键一步。真正让模型"动手"的不是更大的参数量,而是更规范的工具契约。 当你能稳定地定义、绑定、调用工具时,Agent、ReAct、Function Calling 这些概念才会真正落地。
下一步可以关注:
- Tool Calling 错误处理:
invalid_tool_calls字段怎么用 - ToolMessage 与消息历史:多轮工具调用的状态管理
- 动态工具选择:根据上下文给模型暴露不同工具集
📌 配图作者:KennethCheng
📚 推荐阅读:LangChain Tools 官方文档
许可协议:
CC BY 4.0