【自然语言处理与大模型】LangChainV1.0入门指南:核心组件Tools

本文介绍了创建工具的三种常用方法,并说明如何在大模型和智能体中有效使用这些工具。

一个标准的工具通常包含以下三个核心要素:

  • name:工具的唯一标识名称,用于在调用时被大模型识别和引用。
  • docstring:工具的功能描述,应包含简洁的首行摘要、参数说明及返回值信息,帮助大模型理解何时以及如何调用该工具。
  • args_schema :定义工具输入参数的数据结构(通常基于 Pydantic 的 BaseModel),明确指定每个参数的名称、类型和语义,确保 LLM 能生成合法的调用请求。

这三者共同构成了工具与大模型之间的"接口契约",是实现可靠工具调用的关键基础。

一、创建工具的三种方法

1、使用装饰器来创建工具(简单工具)

在智能体(Agent)开发中,工具(Tool)是其执行特定任务的核心组件。为确保代码可读性、可维护性和与其他模块(如 LLM 调用接口)的兼容性,工具函数的 docstring 书写应遵循一定的规范。以下是推荐的 docstring 书写规范(推荐使用Google风格)。

python 复制代码
def search_web(query: str) -> str:

    """根据用户提供的关键词在互联网上搜索最新信息。

    此工具用于获取实时或近期的网络信息,适用于回答时效性强的问题,
    如新闻、股价、天气等。不适用于需要深度推理或内部知识的问题。

    Args:
        query (str): 用户希望搜索的关键词或问题,应尽量简洁明确。

    Returns:
        str: 搜索结果摘要,包含最相关的1-3条信息,每条不超过100字。
             若无结果,返回"未找到相关信息。"
    """

docstring关键要素详解

要素 说明
首行摘要 一句话 概括工具的核心功能,以动词开头(例如:"查询用户订单状态"、"计算两个日期之间的天数"、"生成营销文案")。该摘要会被 LLM 用于快速理解工具用途。
详细描述(可选) 补充说明工具的典型使用场景、前置条件、数据范围限制、调用频率限制、依赖服务状态等。有助于 LLM 判断是否应调用此工具及如何正确使用。
Args 必须列出所有输入参数 ,每个参数需注明: • 参数名 • 类型(如 str, int, List[str]) • 语义说明(如"用户手机号,需为11位中国大陆号码") LLM 会严格依据此部分构造函数调用参数。
Returns 明确返回值的类型和结构(如 dict 包含哪些字段,str 的格式等)。帮助 LLM 理解结果含义,以便在后续推理或回答中正确引用。
Raises(可选) 若工具可能抛出特定异常(如 ValueErrorAPIError),可简要说明触发条件。但在 Agent 框架中,异常通常由运行时统一捕获处理,因此一般可省略
python 复制代码
from langchain_community.chat_models import ChatZhipuAI
from langchain.agents import create_agent
from langchain.tools import tool
from dotenv import load_dotenv
load_dotenv(override=True)


# 来个虚拟的查询天气工具
@tool
def search_weather(city: str) -> str:
    """查询指定城市的天气情况。

    Args:
        city: 待查询的城市名称

    Returns:
        查询到的天气信息。
    """

    return f"您需要查询的城市为{city},该城市的天气为晴天!"


model = ChatZhipuAI(
    model="glm-4.5-flash"
)

agent = create_agent(
    model=model,
    tools=[search_weather],
    system_prompt="你是一个善于助人的小助手。"
)

(1)非流式输出

python 复制代码
inputs = {"messages":[{"role": "user", "content": "帮我查询上海今天的天气。"}]}
response = agent.invoke(inputs)

# 返回的响应是一个字典,messages列表中可以取出最后一个,就是我们想要的答案
response['messages'][-1].pretty_print()

(2)流式输出

流式输出的两种常用的模式

  • values(默认):在每一步执行后,返回整个状态(state)的完整快照。
python 复制代码
inputs = {"messages": "北京的天气如何?"}
for step in agent.stream(inputs):
    print(step)


inputs = {"messages": "北京的天气如何?"}
for chunk in agent.stream(inputs, stream_mode='values'):
    for step, data in chunk.items():
        print(f"step: {step}")
        print(f"content: {data[-1].content}")
  • messages:逐 token 流式输出 LLM 的响应,并附带元数据。"打字机效果"
python 复制代码
inputs = {"messages": "长沙今天天气。"}
for chunk in agent.stream(inputs, stream_mode='messages'):
    print(chunk[0].content, end="")

2、继承BaseTool来创建工具(复杂工具)

(1)如何创建

python 复制代码
from langchain.tools import BaseTool


# 继承BaseTool来创建工具
class MyWeatherSearchTool(BaseTool):
    name: str = "weather_search"  # 定义工具的名字
    description: str = "查询指定城市的天气情况。"
    # args_schema: Type[BaseModel] = <自定义> # 工具参数的定义

    def _run(self, city: str) -> str:  # _run是同步的
        
        # 在这里面编写工具的业务逻辑
        
        return f"您需要查询的城市为{city},该城市的天气为晴天!"

    async def _arun(self, city: str) -> str:
        return self._run(city)


web_search = MyWeatherSearchTool()


result = web_search.invoke({"city": "上海"})

print(type(result))

print(result)

(2)使用Pydantic来定义工具参数

Pydantic 是一个功能强大的 Python 库,用于数据验证和序列化。它通过定义类(继承自 pydantic.BaseModel)来声明数据结构,支持运行时的类型验证、数据转换和序列化(如 JSON)。

python 复制代码
# 导入 LangChain 工具基类,用于自定义工具
from langchain.tools import BaseTool
# 导入 Pydantic 的 BaseModel 和 Field,用于定义结构化输入参数
from pydantic import BaseModel, Field
# 导入 Type 用于类型注解(args_schema 需要)
from typing import Type


# 定义工具的输入参数模型
class WeatherSearchInput(BaseModel):
    # 每个字段使用 Field 描述其含义,LLM 会据此理解如何构造参数
    city: str = Field(..., description="要查询天气的城市名称,例如 '北京'、'上海'")


# 自定义天气查询工具:继承 BaseTool
class MyWeatherSearchTool(BaseTool):

    # 工具的唯一标识名
    name: str = "weather_search"
    
    # 工具的描述,告诉 LLM 这个工具能做什么
    # 建议以动词开头,清晰说明功能和输入要求
    description: str = "查询指定城市的当前天气情况。输入应为城市名称(如 '广州')。"
    
    # 指定该工具接收的参数结构,必须是 BaseModel 的子类
    args_schema: Type[BaseModel] = WeatherSearchInput

    def _run(self, city: str) -> str:
        """
        同步执行逻辑(由 invoke() 调用)。
        参数 `city` 会自动从输入中解析并传入(基于 args_schema)。
        """
        # 这里可以替换为真实 API 调用(如和心知天气、OpenWeather 等对接)
        # 当前为模拟返回
        return f"您需要查询的城市为 {city},该城市的天气为晴天!"

    async def _arun(self, city: str) -> str:
        """
        异步执行逻辑(由 ainvoke() 调用)。
        在本例中直接复用同步逻辑;实际项目中可实现真正的异步请求。
        """
        return self._run(city)


# 实例化工具对象
web_search = MyWeatherSearchTool()

# 调用工具(传递参数字典,键名需与 WeatherSearchInput 中的字段一致)
result = web_search.invoke({"city": "上海"})

# 打印结果类型(通常是 str,但某些情况下可能是 ToolResponse 等)
print(type(result))  # <class 'str'>

# 打印工具返回的具体内容
print(result)        # 您需要查询的城市为 上海,该城市的天气为晴天!

3、从MCP中获取工具

需要安装必要的库(langchain提供的MCP客户端的库)

bash 复制代码
pip install langchain-mcp-adapters

(1)创建MCP服务器

关于MCP服务怎么搭建,工具怎么编写,在这篇文章中只简单给出示例

python 复制代码
from fastmcp import FastMCP

mcp = FastMCP()

@mcp.tool
def add(a: int, b:int) -> int:
    """当输入两个数字的时候,用这个工具将两个数据相加。

    Args:
        a: 第一个数
        b: 第二个数
    Returns:
        两数之和
    """
    return a+b


if __name__ == "__main__":
    # 创建MCP Server
    mcp.run(transport="streamable-http", port=3210)

(2)创建MCP客户端

用MultiServerMCPClient 创建的客户端默认是无状态的。每次调用工具都会创建一个新的 MCP ClientSession ,执行工具,然后进行清理。

python 复制代码
# 导入 LangChain MCP(Model Context Protocol)适配器中的多服务器客户端
# 用于连接符合 MCP 协议的远程工具服务器(如自定义的工具微服务)
from langchain_mcp_adapters.client import MultiServerMCPClient


# 创建一个 MultiServerMCPClient 实例,用于管理多个 MCP 工具服务器
# 参数是一个字典,key 是工具组/服务的逻辑名称(如 "add"),value 是连接配置
client = MultiServerMCPClient(
    {
        "add": {
            # 指定传输协议类型:这里使用基于 HTTP 流式响应的协议(streamable-http)
            "transport": "streamable-http",
            
            # MCP 服务的完整 URL(必须包含 /mcp 路径,这是 MCP 协议的标准 endpoint)
            # 注意:确保本地 3210 端口的服务已启动且支持 MCP
            "url": "http://127.0.0.1:3210/mcp",
        }
    }
)

# 异步获取该 MCP 服务器暴露的所有可用工具(返回一个 Tool 列表)
# 【注意】:此行必须在 async 函数中执行,或在支持 await 的环境(如 Jupyter Notebook)中运行
tools = await client.get_tools()


# 查看 tools 变量内容(调试用)
print(tools)

# 查看 tools 的类型
print(type(tools))

# 查看第一个工具的详细信息
print(tools[0])

# 获取第一个工具的名称
tools[0].name

# 获取第一个工具的功能描述
tools[0].description

# 获取工具的参数结构(Pydantic BaseModel 子类),定义了输入字段及类型
tools[0].args_schema

# 异步调用第一个工具,传入符合其参数 schema 的字典
# 工具接受两个参数 a 和 b(如加法工具)
result = await tools[0].ainvoke({"a": 10, "b": 20})

# 打印调用结果
print("调用结果:", result)

二、在Model中使用工具

使用模型的bind_tools方法来注册工具

python 复制代码
# 导入 LangChain 提供的便捷函数,用于快速初始化聊天模型(如 DeepSeek、OpenAI 等)
from langchain.chat_models import init_chat_model

# 导入 dotenv 模块,用于从 .env 文件加载环境变量(如 API 密钥)
from dotenv import load_dotenv


# 加载项目根目录下的 .env 文件中的环境变量
# override=True 表示:即使系统中已存在同名环境变量,也强制使用 .env 中的值
# 这确保了使用正确的 DEEPSEEK_API_KEY
load_dotenv(override=True)


# 使用 init_chat_model 初始化 DeepSeek 的对话模型
# 注意:该函数会自动读取环境变量 DEEPSEEK_API_KEY,无需显式传入
# 模型名称 "deepseek-chat" 是 DeepSeek 官方支持的标准聊天模型标识
model = init_chat_model(model="deepseek-chat")


# 将自定义工具 `search_weather` 绑定到模型上,启用"工具调用"(Tool Calling)能力
# - `search_weather` 必须是一个符合 LangChain 工具规范的对象(如 BaseTool 子类实例)
# - 绑定后,模型在回答问题时,若需要外部信息(如天气),会主动请求调用此工具
model = model.bind_tools([search_weather])


# 向模型发送用户问题:"长沙今天天气如何?"
# 由于模型本身无法获取实时天气,但已绑定天气查询工具,
# 因此它不会直接编造答案,而是返回一个"工具调用请求"
response = model.invoke("长沙今天天气如何?")


# 查看模型返回的工具调用指令
print(response.tool_calls)

三、在Agent中使用工具

python 复制代码
from langchain.agents import create_agent
from langchain_community.chat_models import ChatZhipuAI

# 导入 LangGraph 提供的内存检查点存储器(InMemorySaver)
# 用于在对话中保存状态(如消息历史),实现多轮对话记忆(基于 thread_id 区分会话)
from langgraph.checkpoint.memory import InMemorySaver

from dotenv import load_dotenv
load_dotenv(override=True)


# 初始化智谱 AI 的聊天模型实例
# API Key 会自动从环境变量 ZHIPUAI_API_KEY 中读取
model = ChatZhipuAI(
    model='glm-4-flash'
)


# 创建一个基于内存的检查点存储器(checkpoint saver)
# 作用:在 Agent 执行过程中保存中间状态(如对话历史、工具调用记录)
# 注意:InMemorySaver 是临时存储,程序重启后数据丢失;生产环境可替换为 Redis、SQLite 等
checkpointer = InMemorySaver()


# 配置会话线程 ID(用于区分不同用户的对话上下文)
# - "thread_id": "222" 表示当前对话属于 ID 为 "222" 的会话线程
# - 同一线程内的多次调用会共享记忆(通过 checkpointer 自动加载历史)
config = {
    "configurable": {"thread_id": "222"}
}


# 使用指定模型、工具列表和检查点存储器创建智能体(Agent)
# - model: 决策核心(LLM)
# - tools: Agent 可调用的工具列表(如计算器、搜索等)
# - checkpointer: 启用状态持久化,支持多轮对话
agent = create_agent(
    model=model,
    tools=tools,          # 注意:此处 `tools` 需已在前面定义(如 [CalculatorTool(), ...])
    checkpointer=checkpointer
)


# 构造用户输入消息
# 格式必须为 {"messages": [...]},且每条消息包含 role 和 content
# 这是 LangGraph Agent 的标准输入格式
inputs = {"messages": [{"role": "user", "content": "我想计算三千五百加七万等于多少?"}]}


# 异步调用智能体(ainvoke 用于异步执行)
# - input: 用户输入
# - config: 传入会话配置(含 thread_id),用于加载/保存该会话的状态
# 返回值 resp 是一个包含完整对话状态的字典(含最终 AI 回复)
resp = await agent.ainvoke(
    input=inputs,
    config=config
)


# 打印智能体的完整响应(包含 messages 列表)
# 最后一条消息通常是 AI 的最终回答
print(resp)
相关推荐
亿元程序员2 小时前
你知道三国志战略版是怎么实现横竖屏动态切换的吗?
前端
BD_Marathon2 小时前
Vue3_双向绑定
前端·javascript·vue.js
小白学大数据2 小时前
Temu 商品历史价格趋势爬虫与分析
开发语言·javascript·爬虫·python
霍理迪2 小时前
CSS复合、关系、属性、伪类选择器
前端·javascript·css
棒棒的唐2 小时前
Avue2图片上传使用object对象模式时,axios的请求模式用post还是get?
开发语言·前端·javascript·avue
OnlyEasyCode2 小时前
Linux部署Nginx前后端web教程
linux·前端·nginx
亮子AI2 小时前
【Typescript】未知类型如何处理?
linux·javascript·typescript
梵得儿SHI2 小时前
Vue Router 路由管理从入门到精通:基础、导航与参数传递实战(含避坑指南)
前端·javascript·vue.js·路由基础配置·版本适配·路由实例创建·路由规则定义
IT_陈寒2 小时前
JavaScript 性能优化实战:7 个让你的应用提速 50%+ 的 V8 引擎技巧
前端·人工智能·后端