LangChain 预定义工具与 Toolkits 详解:从单独使用到混合编排

一、引言

在上一篇博客中,我们深入探讨了 LangChain 中自定义工具(Custom Tools)的实现方式,包括如何使用 @tool 装饰器和 BaseTool 类来封装业务逻辑。然而,LangChain 的强大之处不仅在于支持自定义扩展,更在于它提供了一套开箱即用的预定义工具(Prebuilt Tools)工具集(Toolkits),能够让我们快速构建具备搜索、计算、数据库操作等能力的智能 Agent。

本文将围绕以下几个核心问题展开:

  • LangChain 提供了哪些常用的预定义工具?
  • 如何单独使用预定义工具?
  • 如何将预定义工具与自定义工具混合使用?
  • Toolkit 是什么?它如何简化复杂场景的工具管理?

二、预定义工具 vs 自定义工具

在 LangChain 的 Agent 架构中,工具是 Agent 执行外部操作的"手脚"。从来源上看,工具可以分为两大类:

维度 预定义工具 自定义工具
开发成本 低,开箱即用 高,需要自行实现逻辑
灵活性 受限于官方实现 完全可控,可对接内部系统
适用场景 通用任务(搜索、计算、数据库等) 特定业务(内部 API、私有数据等)
使用方式 load_tools() 或直接实例化 @tool 装饰器或继承 BaseTool

最佳实践:先用预定义工具满足通用需求,再通过自定义工具补充业务特定能力,两者相辅相成。


三、预定义工具的分类与典型代表

LangChain 的预定义工具覆盖了大量常见场景,以下是主要分类:

3.1 搜索类工具

工具名 功能说明
DuckDuckGoSearchRun 通过 DuckDuckGo 进行网页搜索
SerpAPI 通过 SerpAPI 调用 Google 搜索
WikipediaQueryRun 查询维基百科词条
ArxivQueryRun 检索 arXiv 学术论文

3.2 数据操作与计算类

工具名 功能说明
PythonREPL / PythonAstREPLTool 执行 Python 代码(后者使用 AST 更安全)
MathTool 执行数学计算
JsonListKeysTool 从 JSON 中提取键名

3.3 文件与系统类

工具名 功能说明
ReadFileTool 读取文件内容
WriteFileTool 写入文件内容
ListDirectoryTool 列出目录文件

3.4 数据库类

工具名 功能说明
QuerySQLDataBaseTool 执行 SQL 查询
SQLDatabaseToolkit 一整套数据库操作工具集

3.5 API 集成类

工具名 功能说明
OpenWeatherMapQueryRun 查询天气数据
RequestsWrapper 发送 HTTP 请求

3.6 向量存储类

工具名 功能说明
VectorStoreQATool 基于向量存储的问答
VectorStoreQAWithSourcesTool 带来源信息的向量问答

四、预定义工具的使用方式

4.1 方式一:通过 load_tools 快速加载(传统方式)

这是最简单的方式,适合快速上手和单独使用预定义工具:

from langchain_community.agent_toolkits.load_tools import load_tools

# 加载单个或多个工具
tools = load_tools(["arxiv", "llm-math"], llm=llm)

# 查看所有可用工具名
from langchain_community.agent_toolkits.load_tools import get_all_tool_names
print(get_all_tool_names())

4.2 方式二:直接导入具体工具类(推荐,混用时必需)

更灵活,可以精细配置参数,也是混用自定义工具时的必要方式

from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper

# 初始化维基百科工具(指定中文、返回1条结果)
wiki_tool = WikipediaQueryRun(
    api_wrapper=WikipediaAPIWrapper(lang="zh", top_k_results=1)
)

# 直接调用
result = wiki_tool.invoke("马斯克")
print(result[:200])

五、核心差异:load_tools vs 直接实例化

很多开发者困惑:为什么单独使用时可以用 load_tools,混用时却要实例化对象?

对比项 load_tools 直接实例化
返回值 工具列表(黑盒) 单个工具对象
配置灵活性 低,只能传字符串名 高,可自定义构造参数
混用支持 不方便与自定义工具组合 可与自定义工具统一放入列表
本质 封装了 import + new 的过程 手动 import 类并实例化

核心原理load_tools 本质上也是根据字符串名字去 import 对应的类然后实例化。混用时跳过这个"中介",直接拿到对象,就能和自定义工具平起平坐了。

一句话总结

  • 单独用预定义工具 -> load_tools 一键加载
  • 混用自定义+预定义 -> 预定义工具也要实例化对象,统一组装列表

六、重点详解:Arxiv 工具与科研助理

Arxiv 是一个免费的学术论文预印本平台。ArxivQueryRun 让 Agent 能够按关键词搜索论文、获取标题摘要等信息,是构建科研助理的核心工具。

6.1 单独使用 Arxiv

import os
from langchain.chat_models import ChatOpenAI
from langchain.agents import load_tools, initialize_agent, AgentType

os.environ['OPENAI_API_KEY'] = 'Your Key'
llm = ChatOpenAI(temperature=0.0)

# 单独加载
tools = load_tools(["arxiv"])

agent_chain = initialize_agent(
    tools,
    llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)

agent_chain.run("介绍一下2005.14165这篇论文的创新点")

6.2 混用 Arxiv + 自定义工具

from langchain_community.tools import ArxivQueryRun
from langchain.tools import tool
from langchain.agents import initialize_agent, AgentType
from langchain.chat_models import ChatOpenAI

llm = ChatOpenAI(temperature=0.0)

# 预定义工具:实例化对象
arxiv = ArxivQueryRun()

# 自定义工具:论文摘要翻译
@tool
def translate_abstract(text: str, target_lang: str = 'zh') -> str:
    """将论文摘要翻译成目标语言。输入:英文摘要文本"""
    # 实际项目中调用翻译 API
    return f"[翻译结果] {text[:100]}..."

# 混用:统一放入列表
all_tools = [arxiv, translate_abstract]

agent = initialize_agent(
    all_tools,
    llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)

agent.run("搜索最近关于 LLM Agent 的论文,并把摘要翻译成中文")

七、Toolkit:工具的组合包

7.1 什么是 Toolkit?

Toolkit 是 LangChain 中针对特定领域预打包的一组工具集合。与单个工具相比,Toolkit 提供了更完整的解决方案。

7.2 最经典的 Toolkit:SQLDatabaseToolkit

当 Agent 需要操作数据库时,单个 SQL 执行工具远远不够。Agent 需要:

  1. 查看有哪些表(sql_db_list_tables
  2. 查看表结构(sql_db_schema
  3. 检查 SQL 语法(sql_db_query_checker
  4. 执行 SQL 查询(sql_db_query

SQLDatabaseToolkit 正好打包了这全套能力:

from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit
from langchain_community.utilities.sql_database import SQLDatabase
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langchain import hub

# 连接数据库
db = SQLDatabase.from_uri("sqlite:///Chinook.db")
llm = ChatOpenAI(temperature=0)

# 初始化 Toolkit
toolkit = SQLDatabaseToolkit(db=db, llm=llm)

# 获取工具列表(包含4个SQL工具)
tools = toolkit.get_tools()

# 创建 Agent
prompt_template = hub.pull("langchain-ai/sql-agent-system-prompt")
system_message = prompt_template.format(dialect="SQLite", top_k=5)

agent_executor = create_react_agent(
    llm, tools, state_modifier=system_message
)

# 查询
events = agent_executor.stream(
    {"messages": [("user", "Which country's customers spent the most?")]},
    stream_mode="values",
)
for event in events:
    event["messages"][-1].pretty_print()

7.3 SQLDatabaseToolkit 工具清单

工具 作用
sql_db_list_tables 列出数据库中所有表
sql_db_schema 查看指定表的结构(字段、类型、关系)
sql_db_query_checker 检查 SQL 语句的语法正确性
sql_db_query 执行 SQL 查询并返回结果

这种"组合拳"让 Agent 能够自主探索数据库结构、生成并验证 SQL,最终完成复杂的数据查询任务。


八、完整实战:预定义工具与自定义工具混用

假设我们要构建一个电商智能助手,它需要:

  • ArxivQueryRun 查最新的 AI 技术论文
  • SQLDatabaseToolkit 查订单数据库
  • 自定义工具 调用公司内部的价格计算 API
import os
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.tools import tool, BaseTool
from langchain_community.tools import ArxivQueryRun
from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit
from langchain_community.utilities.sql_database import SQLDatabase
from pydantic import BaseModel, Field

# ========== 1. 初始化 LLM ==========
os.environ["OPENAI_API_KEY"] = "your-api-key"
llm = ChatOpenAI(temperature=0, model="gpt-4")

# ========== 2. 预定义工具:Arxiv 论文搜索 ==========
arxiv_tool = ArxivQueryRun()

# ========== 3. 预定义工具:SQL 数据库工具集 ==========
db = SQLDatabase.from_uri("sqlite:///orders.db")
sql_toolkit = SQLDatabaseToolkit(db=db, llm=llm)
sql_tools = sql_toolkit.get_tools()

# ========== 4. 自定义工具:公司内部价格计算 API ==========
class PriceInput(BaseModel):
    product_id: str = Field(description="商品ID")
    quantity: int = Field(description="购买数量")
    coupon_code: str = Field(description="优惠券代码,没有则填空字符串")

class PriceCalculatorTool(BaseTool):
    name = "price_calculator"
    description = "计算商品最终价格,支持优惠券折扣。输入格式:product_id, quantity, coupon_code"
    args_schema = PriceInput
    
    def _run(self, product_id: str, quantity: int, coupon_code: str):
        base_price = 100
        discount = 0.8 if coupon_code == "SAVE20" else 1.0
        final_price = base_price * quantity * discount
        return f"商品 {product_id} 购买 {quantity} 件,使用优惠券 {coupon_code},最终价格:{final_price}元"
    
    async def _arun(self, **kwargs):
        raise NotImplementedError("不支持异步")

price_tool = PriceCalculatorTool()

# ========== 5. 自定义工具:查询物流状态 ==========
@tool
def logistics_tracker(order_id: str) -> str:
    """查询订单物流状态。输入:订单号"""
    return f"订单 {order_id} 当前状态:已发货,预计明天送达"

# ========== 6. 混合所有工具(核心!)==========
all_tools = [
    arxiv_tool,           # 预定义:Arxiv
    *sql_tools,           # 预定义:SQL Toolkit(展开4个工具)
    price_tool,           # 自定义:价格计算
    logistics_tracker,    # 自定义:物流查询
]

print(f"共加载 {len(all_tools)} 个工具:")
for t in all_tools:
    print(f"  - {t.name}: {t.description[:50]}...")

# ========== 7. 创建 Agent ==========
agent = initialize_agent(
    all_tools,
    llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
    handle_parsing_errors=True,
)

# ========== 8. 测试不同场景 ==========

# 场景1:查论文(触发 Arxiv)
agent.run("最近有没有关于大语言模型微调的论文?")

# 场景2:查数据库(触发 SQL Toolkit)
agent.run("上个月销售额最高的商品是什么?请查一下数据库")

# 场景3:算价格(触发自定义 price_calculator)
agent.run("商品ID为PHONE-001,买3个,用优惠券SAVE20,最终多少钱?")

# 场景4:查物流(触发自定义 logistics_tracker)
agent.run("帮我查一下订单 ORD-2024-001 的物流状态")

# 场景5:复杂问题(可能触发多个工具)
agent.run("帮我查一下订单 ORD-2024-002 买了什么商品,然后算一下如果再加2件用SAVE20优惠券要多少钱")

九、混用时的关键要点

要点 说明
统一列表 自定义和预定义工具放在同一个 tools 列表里
描述要清晰 Agent 靠 description 决定调用哪个工具,描述写不好会选错工具
输入规范 自定义工具建议用 args_schema(Pydantic)定义参数,和预定义工具保持一致
命名不冲突 确保 name 唯一,不要和预定义工具重名
权限控制 自定义工具可能涉及敏感操作,注意权限和异常处理

十、使用 LangGraph 的 ReAct Agent 混用

如果你使用新版 LangGraph,混用方式完全一致:

from langgraph.prebuilt import create_react_agent

# 同样的 all_tools 列表
agent = create_react_agent(llm, all_tools)

# 流式输出
for chunk in agent.stream({"messages": [("user", "查一下订单表有哪些字段")]}):
    print(chunk)

LangGraph 的 create_react_agent 会自动处理工具调用,混用逻辑和传统 Agent 完全一致。


十一、总结

LangChain 的预定义工具和 Toolkits 是 Agent 能力的重要基石:

  1. 预定义工具覆盖搜索、计算、文件、数据库、API 等常见场景,通过 load_tools() 或直接实例化即可使用。
  2. 单独使用预定义工具时,可以用 load_tools(["arxiv"]) 一键加载。
  3. 混用自定义+预定义工具时,预定义工具也需要实例化为对象,然后统一放入工具列表。
  4. Arxiv 工具是科研类 Agent 的典型代表,让 LLM 具备文献检索能力。
  5. Toolkit 是工具的"组合拳",如 SQLDatabaseToolkit 提供完整的数据库操作能力,让 Agent 能自主探索 schema、生成 SQL、执行查询。

核心心法:Agent 不区分工具的来源,只关心每个工具是否有 namedescriptioninvoke() 方法。只要满足这个契约,预定义工具和自定义工具就能无缝协作。


介绍了自定义工具的完整实现方式。后续还将深入探讨工具调用链的调试与优化,敬请期待!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐