2024 字
10 分钟
工具系统

工具系统#

在前面的章节中,我们学会了用 Chain 把 Prompt、Model、OutputParser 串成一条流水线(参见 01_LangChain概述与核心架构)。但现实世界的任务往往不是线性的——用户问”北京今天天气怎么样?“,模型需要先调用天气 API,拿到结果后再组织回答。这种”边思考、边行动”的能力,就是 Agent 的核心价值。

本篇从 Agent 的动机出发,依次讲解 Tool 定义、ReAct 推理循环、Tool Calling Agent 和实战案例,最终帮你构建出一个能自主调用多工具的智能体。


一、为什么需要 Agent#

1.1 Chain 的局限性#

02_LangChain底层原理 中我们学过,Chain(链)本质上是一条固定的管道:输入 -> 步骤 A -> 步骤 B -> 输出。这就像一条工厂流水线,产品从头走到尾,中间不会岔路。

这种模式在简单场景下运作良好,但面对以下情况就力不从心了:

局限示例
无法动态选择路径用户问天气走天气 API,问股价走股票 API——Chain 无法根据输入切换
无法根据中间结果调整搜索结果不满意时需要换个关键词再搜,Chain 做不到
无法处理多步推理”帮我找到最近一周涨幅最大的股票,然后查它的新闻”需要多轮工具调用
无法自主决定何时结束Chain 执行完固定步骤就结束,无法判断”信息是否已经足够”

1.2 Agent 的本质#

核心概念

Agent(智能体) = LLM(大脑)+ Tools(工具)+ 推理循环(决策机制)

LLM 负责思考(该用什么工具?参数是什么?结果是否满意?),Tools 负责执行(调 API、查数据库、跑代码),推理循环负责协调二者反复交互直到任务完成。

1.3 费曼类比#

费曼类比:有工具箱的工程师

把 Agent 想象成一位经验丰富的全栈工程师,面前摆着一个大工具箱(扳手、万用表、示波器……)。

当用户报修说”空调不制冷”时,这位工程师会:

  1. 思考(Thought):“可能是制冷剂不足,也可能是电路问题。”
  2. 行动(Action):先拿出压力表测制冷剂。
  3. 观察(Observation):读数正常,排除制冷剂问题。
  4. 再思考:“那可能是压缩机电路,用万用表测一下。”
  5. 再行动:用万用表测量。
  6. 再观察:发现电容坏了。
  7. 最终回答:“电容损坏,需要更换。”

Chain 就像一本固定的维修手册——不管什么故障,都从第 1 页翻到最后一页。 Agent 则是这位会灵活判断的工程师——根据每一步的观察动态调整策略。

1.4 Agent vs Chain 对比表#

维度ChainAgent
执行流程固定、线性动态、循环
决策方式开发者预定义LLM 实时决策
工具使用不主动调用工具根据需要选择工具
适用场景流程确定的任务(翻译、摘要)流程不确定的任务(问答、调研)
可控性高(行为可预测)较低(依赖 LLM 推理质量)
Token 消耗可预估不可预估(多轮推理)
调试难度较高(需追踪推理链)
注意

Agent 并不总是优于 Chain。如果你的任务流程明确、不需要动态决策,Chain 更简单、更可控、更省 Token。只有在需要动态决策时才引入 Agent。


二、Tool(工具)系统#

2.1 什么是 Tool#

在 LangChain 中,Tool 是 Agent 可以调用的外部能力单元。每个 Tool 由三部分组成:

组成部分作用示例
name工具的唯一标识符,LLM 用它来指定调用哪个工具"search_web"
description工具的功能描述,LLM 靠它来决定何时使用该工具"搜索互联网获取实时信息"
function工具的实际执行逻辑一个调用搜索 API 的 Python 函数
关键洞察

description 是 Tool 最重要的部分——LLM 完全依靠描述文本来理解工具的用途和使用时机。一个功能强大但描述模糊的工具,Agent 可能永远不会选择它。就像工具箱里的扳手如果没有标签,工程师可能会忽略它。

2.2 使用 @tool 装饰器定义工具#

@tool 装饰器是定义工具最简洁的方式,它自动从函数签名和文档字符串中提取元信息。

# pip install langchain langchain-openai
from langchain_core.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""将两个整数相乘。当用户需要计算乘法时使用此工具。
Args:
a: 第一个整数
b: 第二个整数
"""
return a * b
# 查看工具的元信息
print(multiply.name) # "multiply"
print(multiply.description) # "将两个整数相乘。当用户需要计算乘法时使用此工具。"
print(multiply.args_schema.model_json_schema())
# {'properties': {'a': {'title': 'A', 'type': 'integer'},
# 'b': {'title': 'B', 'type': 'integer'}},
# 'required': ['a', 'b'],
# 'title': 'multiply',
# 'type': 'object'}
幕后原理

@tool 装饰器做了三件事:

  1. 将函数名作为 name
  2. 将 docstring 的第一段作为 description
  3. 利用类型注解自动生成 args_schema(基于 Pydantic)

2.3 使用 StructuredTool 定义复杂输入工具#

当工具的输入参数较复杂,或需要更精细的校验时,可以使用 StructuredTool.from_function 配合 Pydantic 模型。

# pip install langchain pydantic
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
"""搜索工具的输入参数。"""
query: str = Field(description="搜索关键词")
max_results: int = Field(default=5, description="最大返回结果数", ge=1, le=20)
language: str = Field(default="zh", description="搜索语言,如 'zh'、'en'")
def search_web(query: str, max_results: int = 5, language: str = "zh") -> str:
"""搜索互联网获取实时信息。当用户询问最新事件、实时数据时使用。"""
# 这里替换为实际的搜索 API 调用
return f"搜索 '{query}' 的前 {max_results} 条结果({language})..."
search_tool = StructuredTool.from_function(
func=search_web,
name="search_web",
description="搜索互联网获取实时信息。当用户询问最新事件、实时数据时使用。",
args_schema=SearchInput,
)
print(search_tool.name) # "search_web"
print(search_tool.args_schema.model_json_schema())
# 输出包含 query、max_results、language 的完整 JSON Schema

2.4 内置工具与社区工具#

LangChain 提供了大量开箱即用的内置工具和社区集成:

工具用途
Tavily 搜索langchain-community互联网搜索,为 AI Agent 优化
Wikipedialangchain-community查询维基百科
PythonREPLlangchain-experimental执行 Python 代码
Shelllangchain-community执行 Shell 命令
Requestslangchain-community发送 HTTP 请求
SQL Databaselangchain-community查询 SQL 数据库
# pip install langchain-community tavily-python
from langchain_community.tools.tavily_search import TavilySearchResults
# 需要设置环境变量 TAVILY_API_KEY
search = TavilySearchResults(max_results=3)
results = search.invoke("LangChain 最新版本")
print(results)
安全提示

PythonREPL 和 Shell 工具允许执行任意代码,在生产环境中务必做好沙箱隔离,切勿直接暴露给终端用户。

2.5 工具描述的写作指南#

由于 LLM 完全依赖 description 来选择工具,写好描述至关重要:

原则差的描述好的描述
说清楚做什么"计算工具""计算数学表达式的结果,支持加减乘除和幂运算"
说清楚何时用"搜索""当用户询问实时信息、最新新闻或需要联网查询时使用"
说清楚不能做什么(省略)"仅支持文本搜索,不支持图片搜索"
说清楚参数格式(省略)"日期参数格式为 YYYY-MM-DD"

2.6 工具的异步支持#

在高并发场景下(如 Web 服务),工具需要支持异步执行以避免阻塞:

# pip install langchain aiohttp
import aiohttp
from langchain_core.tools import tool
@tool
async def async_search(query: str) -> str:
"""异步搜索工具,用于高并发场景下的互联网搜索。"""
async with aiohttp.ClientSession() as session:
async with session.get(
"https://api.example.com/search",
params={"q": query}
) as resp:
data = await resp.json()
return str(data["results"][:3])
# 异步调用
# result = await async_search.ainvoke({"query": "LangChain Agent"})
同步 / 异步兼容

如果你用 @tool 装饰一个普通同步函数,LangChain 会自动在 ainvoke 时将其包装为异步执行(通过线程池)。但如果你的工具本身涉及 I/O 操作(网络请求、文件读写),建议直接编写 async 版本以获得更好的性能。


工具定义好之后,下一步是让 Agent 学会使用它们。详见 02_Agent架构与实战