Day16:LangChain Tool与Toolkit完全指南:从零开始打造你的第一个多工具智能体

🌟 前言

在Day15的教程中,我们学习了LangChain的基础概念,并搭建了第一个问答Chain。很多同学在后台问我:“我已经会调用模型了,但如何让AI真正动手做事?比如帮我查天气、算数学题、操作文件?”

今天,我们就来深入探讨LangChain最强大的特性之一:Tool(工具)与Toolkit(工具包) 。学完本文,你将能够:

  • ✅ 理解Tool和Toolkit的核心概念

  • ✅ 掌握三种创建自定义Tool的方法

  • ✅ 将上周封装的天气和计算器函数转换为LangChain Tool

  • ✅ 构建Toolkit实现多工具协同调用

  • ✅ 使用ReAct代理让AI自主选择工具

让我们开始吧!🚀

🧐 一、什么是LangChain Tool?

1.1 为什么需要Tool?

大语言模型(LLM)本身存在两个天然缺陷:

  • 知识截止日期:不知道最新信息

  • 无法执行操作:只能生成文字,不能调用外部系统

Tool 就是为了解决这些问题而生的。在LangChain中,Tool是AI模型可以用来与世界交互的函数 。每个Tool都代表一个特定的能力:搜索、计算、查天气、发邮件等。

1.2 Tool的核心组件

一个完整的LangChain Tool由以下五个核心组件构成 :

组件 作用 示例
name 工具的唯一标识 "weather_search"
description 告诉AI什么时候用这个工具 "当用户需要查询天气时使用"
func 实际执行的函数 get_weather(city)
args_schema 输入参数的定义和验证 Pydantic模型
return_direct 是否直接返回结果 False(通常不直接返回)

通俗理解:Tool就像是给AI配的"瑞士军刀",每一把刀(工具)都有明确的用途和使用说明,AI会根据任务需要选择合适的工具。

🧩 二、什么是Toolkit?

Toolkit 是一组相关Tool的集合,用于完成特定领域的复杂任务 。

Tool vs Toolkit:一张图看懂

exported_image (1).png

Toolkit的主要作用

  1. 组织多个工具:将相关的工具组合在一起

  2. 简化接口:统一管理和调用

  3. 提高效率:一次性加载多个工具给代理使用

🛠️ 三、LangChain Tool的三种创建方式

LangChain提供了三种创建自定义Tool的方法 。我们从最简单到最复杂逐一介绍。

方法1:使用 **@tool** 装饰器(最简单)

这是最快速、最简洁的方式,只需在普通函数上添加 @tool 装饰器 。

from langchain.tools import tool

@tool
def multiply(first_int: int, second_int: int) -> int:
    """将两个整数相乘。"""
    return first_int * second_int

# 使用
result = multiply.invoke({"first_int": 5, "second_int": 7})
print(result)  # 35

优点:代码量最少,自动从函数签名生成schema
缺点:自定义程度有限

方法2:使用 **Tool** 数据类(最常用)

这是最平衡的方式,适合大多数场景 。

from langchain.tools import Tool

def get_weather(city: str) -> str:
    """获取指定城市的天气"""
    # 这里是实际逻辑
    return f"{city}今天晴朗,25℃"

weather_tool = Tool.from_function(
    func=get_weather,
    name="weather_checker",
    description="当需要查询某个城市的天气情况时使用"
)

# 或直接使用Tool类
weather_tool = Tool(
    name="weather_checker",
    func=get_weather,
    description="当需要查询某个城市的天气情况时使用"
)

优点:灵活、直观、支持参数验证
缺点:需要手动处理多参数(可用StructuredTool解决)

方法3:继承 **BaseTool** 基类(最强大)

当需要复杂初始化、异步操作或状态管理时,使用这种方法 。

from typing import Optional, Type
from langchain_core.tools import BaseTool
from langchain_core.callbacks import CallbackManagerForToolRun
from pydantic import BaseModel, Field
import random
from datetime import datetime
from dotenv import load_dotenv
from langchain_community.chat_models import ChatTongyi
from langchain.agents.react.agent import create_react_agent
from langchain.agents import AgentExecutor
from langchain_core.prompts import PromptTemplate

load_dotenv()  # 从 .env 读取 API 密钥

# ========== 定义模拟天气工具 ==========
class WeatherInput(BaseModel):
    city: str = Field(description="城市名称,如'北京'")
    date: Optional[str] = Field(None, description="日期,格式 YYYY-MM-DD,默认为今天")

class SimulatedWeatherTool(BaseTool):
    name: str = "simulated_weather"
    description: str = "查询指定城市和日期的模拟天气数据(仅供演示)"
    args_schema: Type[BaseModel] = WeatherInput

    def _run(
        self,
        city: str,
        date: Optional[str] = None,
        run_manager: Optional[CallbackManagerForToolRun] = None
    ) -> str:
        if date is None:
            date = datetime.now().strftime("%Y-%m-%d")
        conditions = ["晴朗", "多云", "阴天", "小雨", "大雨", "雷阵雨"]
        temperature = random.randint(10, 35)
        humidity = random.randint(40, 90)
        condition = random.choice(conditions)
        return f"{date} {city} 天气:{condition}{temperature}℃,湿度 {humidity}%"

    async def _arun(self, city: str, date: Optional[str] = None, run_manager=None) -> str:
        return self._run(city, date, run_manager)

weather_tool = SimulatedWeatherTool()

# ========== 初始化模型 ==========
llm = ChatTongyi(model="qwen-plus")  # API 密钥从环境变量读取

# ========== 创建代理 ==========
tools = [weather_tool]

# ✅ 正确的提示模板:包含完整的 ReAct 格式说明
prompt = PromptTemplate.from_template(
    "你是一个可以使用工具的助手。\n\n"
    "你有以下工具可用:\n{tools}\n\n"
    "请严格按照以下格式输出:\n"
    "Thought: 你需要做什么\n"
    "Action: 工具名称,必须是 [{tool_names}] 中的一个\n"
    "Action Input: 工具的输入参数(必须是 JSON 格式)\n"
    "Observation: 工具返回的结果\n"
    "... (可以重复多次 Thought/Action/Action Input/Observation)\n"
    "Thought: 我现在可以给出最终答案\n"
    "Final Answer: 对用户的最终回答\n\n"
    "用户输入:{input}\n"
    "{agent_scratchpad}"
)

agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    handle_parsing_errors=True  # 允许解析错误时重试
)

# ========== 测试 ==========
response = agent_executor.invoke({"input": "明天上海的天气怎么样?"})
print(response['output'])

优点:完全控制,支持异步,可管理状态
缺点:代码量较大

三种方式对比

维度 @tool装饰器 Tool类 BaseTool子类
代码量 ⭐⭐⭐ 最少 ⭐⭐ 中等 ⭐ 最多
灵活性 ⭐⭐ 中等 ⭐⭐⭐ 高 ⭐⭐⭐ 最高
异步支持 ⭐ 不支持 ⭐ 不支持 ⭐⭐⭐ 支持
参数验证 ⭐⭐ 自动 ⭐⭐ 需配合Pydantic ⭐⭐⭐ 精细控制
适用场景 简单函数 大多数场景 复杂工具

🌤️ 四、实战:将天气和计算器封装为自定义Tool

现在,让我们将之前封装的天气和计算器函数转换为LangChain自定义Tool。

4.1 准备工作

首先,安装必要的库:版本:0.1.17

pip install langchain langchain-community python-dotenv dashscope

创建 .env 文件(如果还没有):

DASHSCOPE_API_KEY=你的阿里云百炼API密钥

4.2 封装天气查询Tool

我们将创建一个模拟的天气查询工具(实际项目中可替换为真实API)。

import random
from datetime import datetime
from typing import Optional
from langchain.tools import tool, StructuredTool
from langchain.pydantic_v1 import BaseModel, Field


# ---------- 方式1:使用@tool装饰器(最简洁)----------
@tool
def get_weather_simple(city: str) -> str:
    """
    查询指定城市的实时天气。

    Args:
        city: 城市名称,如'北京'、'上海'

    Returns:
        包含天气信息的字符串
    """
    # 模拟天气数据(实际应用中可调用真实API)
    weather_conditions = ["晴朗", "多云", "阴天", "小雨", "大雨", "雷阵雨"]
    temperatures = random.randint(15, 35)
    condition = random.choice(weather_conditions)

    return f"{city}当前天气:{condition}{temperatures}℃,湿度65%"


# ---------- 方式2:使用Tool类(推荐)----------
def get_weather_with_date(city: str, date: Optional[str] = None) -> str:
    """
    查询指定城市在特定日期的天气。
    """
    if date is None:
        date = datetime.now().strftime("%Y-%m-%d")

    # 模拟天气数据
    weather_conditions = ["晴朗", "多云", "阴天", "小雨", "大雨", "雷阵雨"]
    temperatures = random.randint(10, 38)
    condition = random.choice(weather_conditions)

    return f"{date} {city}天气:{condition}{temperatures}℃"


# 创建带参数验证的输入模型
class WeatherInput(BaseModel):
    city: str = Field(description="城市名称,如'北京'")
    date: Optional[str] = Field(None, description="日期,格式YYYY-MM-DD,默认为今天")


# 使用Tool.from_function创建工具
weather_tool = StructuredTool .from_function(
    func=get_weather_with_date,
    name="weather_query",
    description="查询指定城市和日期的天气信息。输入城市名称,可选日期参数。",
    args_schema=WeatherInput  # 提供参数schema,让AI更好理解
)

print(get_weather_simple.invoke({"city": "北京"}))
print(weather_tool.invoke({"city": "上海", "date": "2026-03-16"}))

注意点:****针对多参数不适用Tool,官网推荐使用StructuredTool

关键点description 非常重要,它告诉AI何时使用这个工具。描述越清晰,AI选择越准确 。

返回结果:

北京当前天气:多云,31℃,湿度65%
2026-03-16 上海天气:小雨,20℃

4.3 封装计算器Tool

计算器工具支持加减乘除等基本运算 。

import math
import re
from typing import Union
from langchain.tools import tool
from langchain.tools import StructuredTool
from langchain.pydantic_v1 import BaseModel, Field


# ---------- 计算器核心逻辑 ----------
def calculate(expression: str) -> str:
    """
    计算数学表达式。

    支持的运算:+ - * / ** () sqrt() 等

    Args:
        expression: 数学表达式字符串,如"2 + 3 * 4"或"sqrt(16)"

    Returns:
        计算结果
    """
    # 安全检查:只允许数字、运算符、括号和数学函数
    if not re.match(r'^[0-9\s\+\-\*\/\(\)\.\,\s]+$', expression) and \
            not any(func in expression for func in ['sqrt', 'pow', 'abs']):
        return "错误:表达式包含非法字符"

    try:
        # 替换数学函数
        expr = expression.replace('sqrt', 'math.sqrt')
        expr = expr.replace('pow', 'math.pow')
        expr = expr.replace('abs', 'abs')

        # 安全计算(注意:eval有风险,生产环境建议使用numexpr或sympy)
        result = eval(expr)
        return f"{expression} = {result}"
    except Exception as e:
        return f"计算错误:{str(e)}"


# ---------- 使用StructuredTool(适合多参数)----------
class CalculatorInput(BaseModel):
    expression: str = Field(description="数学表达式,例如'2 * 3'、'sqrt(16)'")


calculator_tool = StructuredTool.from_function(
    func=calculate,
    name="calculator",
    description="用于数学计算。输入数学表达式,支持加减乘除、幂运算、平方根等。",
    args_schema=CalculatorInput
)


# ---------- 扩展:专门的幂运算工具 ----------
# ---------- 幂运算工具(也使用 StructuredTool)----------
def power(base: float, exponent: float) -> str:
    result = base ** exponent
    return f"{base}{exponent} 次方 = {result}"

class PowerInput(BaseModel):
    base: float = Field(description="底数")
    exponent: float = Field(description="指数")

power_tool = StructuredTool.from_function(
    func=power,
    name="power_tool",
    description="计算一个数的幂。例如:2的3次方等于8",
    args_schema=PowerInput
)

# 测试计算器工具
result1 = calculator_tool.invoke({"expression": "2 + 3 * 4"})
print(result1)  # 输出:2 + 3 * 4 = 14

result2 = calculator_tool.invoke({"expression": "sqrt(16)"})
print(result2)  # 输出:sqrt(16) = 4.0

# 测试幂运算工具
result3 = power_tool.invoke({"base": 5, "exponent": 3})
print(result3)  # 输出:5 的 3 次方 = 125

返回结果:

2 + 3 * 4 = 14
sqrt(16) = 4.0
5.0 的 3.0 次方 = 125.0

🧰 五、创建Toolkit实现多工具调用

5.1 什么是Toolkit?

Toolkit就是一组相关工具的集合 。LangChain内置了很多Toolkit,如:

  • FileManagementToolkit:文件操作工具包

  • SQLDatabaseToolkit:数据库查询工具包

  • RequestsToolkit:HTTP请求工具包

5.2 将多个Tool组合成Toolkit

我们将刚才创建的天气工具、计算器工具组合成一个Toolkit 。

import random
from datetime import datetime
from typing import Optional
from langchain.tools import tool, StructuredTool
from langchain.pydantic_v1 import BaseModel, Field
import re

# ---------- 方式1:使用@tool装饰器(最简洁)----------
@tool
def get_weather_simple(city: str) -> str:
    """
    查询指定城市的实时天气。

    Args:
        city: 城市名称,如'北京'、'上海'

    Returns:
        包含天气信息的字符串
    """
    # 模拟天气数据(实际应用中可调用真实API)
    weather_conditions = ["晴朗", "多云", "阴天", "小雨", "大雨", "雷阵雨"]
    temperatures = random.randint(15, 35)
    condition = random.choice(weather_conditions)

    return f"{city}当前天气:{condition}{temperatures}℃,湿度65%"


# ---------- 方式2:使用Tool类(推荐)----------
def get_weather_with_date(city: str, date: Optional[str] = None) -> str:
    """
    查询指定城市在特定日期的天气。
    """
    if date is None:
        date = datetime.now().strftime("%Y-%m-%d")

    # 模拟天气数据
    weather_conditions = ["晴朗", "多云", "阴天", "小雨", "大雨", "雷阵雨"]
    temperatures = random.randint(10, 38)
    condition = random.choice(weather_conditions)

    return f"{date} {city}天气:{condition}{temperatures}℃"


# 创建带参数验证的输入模型
class WeatherInput(BaseModel):
    city: str = Field(description="城市名称,如'北京'")
    date: Optional[str] = Field(None, description="日期,格式YYYY-MM-DD,默认为今天")


# 使用Tool.from_function创建工具
weather_tool = StructuredTool .from_function(
    func=get_weather_with_date,
    name="weather_query",
    description="查询指定城市和日期的天气信息。输入城市名称,可选日期参数。",
    args_schema=WeatherInput  # 提供参数schema,让AI更好理解
)

# ---------- 计算器核心逻辑 ----------
def calculate(expression: str) -> str:
    """
    计算数学表达式。

    支持的运算:+ - * / ** () sqrt() 等

    Args:
        expression: 数学表达式字符串,如"2 + 3 * 4"或"sqrt(16)"

    Returns:
        计算结果
    """
    # 安全检查:只允许数字、运算符、括号和数学函数
    if not re.match(r'^[0-9\s\+\-\*\/\(\)\.\,\s]+$', expression) and \
            not any(func in expression for func in ['sqrt', 'pow', 'abs']):
        return "错误:表达式包含非法字符"

    try:
        # 替换数学函数
        expr = expression.replace('sqrt', 'math.sqrt')
        expr = expr.replace('pow', 'math.pow')
        expr = expr.replace('abs', 'abs')

        # 安全计算(注意:eval有风险,生产环境建议使用numexpr或sympy)
        result = eval(expr)
        return f"{expression} = {result}"
    except Exception as e:
        return f"计算错误:{str(e)}"

# ---------- 使用StructuredTool(适合多参数)----------
class CalculatorInput(BaseModel):
    expression: str = Field(description="数学表达式,例如'2 * 3'、'sqrt(16)'")

calculator_tool = StructuredTool.from_function(
    func=calculate,
    name="calculator",
    description="用于数学计算。输入数学表达式,支持加减乘除、幂运算、平方根等。",
    args_schema=CalculatorInput
)

# ---------- 扩展:专门的幂运算工具 ----------
def power(base: float, exponent: float) -> str:
    result = base ** exponent
    return f"{base}{exponent} 次方 = {result}"

class PowerInput(BaseModel):
    base: float = Field(description="底数")
    exponent: float = Field(description="指数")

power_tool = StructuredTool.from_function(
    func=power,
    name="power_tool",
    description="计算一个数的幂。例如:2的3次方等于8",
    args_schema=PowerInput
)

# 收集所有工具
my_tools = [
    weather_tool,
    calculator_tool,
    power_tool
]

# 方式1:简单列表(推荐,因为Agent直接接受工具列表)
# 在LangChain最新版本中,Agent直接接受tools参数,不需要显式Toolkit类

# 方式2:自定义Toolkit类(便于管理)
class MyToolkit:
    """我的个人工具包"""

    @property
    def tools(self):
        return [
            weather_tool,
            calculator_tool,
            power_tool
        ]

    def get_tools(self):
        return self.tools
# 使用
my_toolkit = MyToolkit()
all_tools = my_toolkit.get_tools()
print(f"工具包包含 {len(all_tools)} 个工具:{[t.name for t in all_tools]}")

输出结果:

工具包包含 3 个工具:['weather_query', 'calculator', 'power_tool']

5.3 使用ReAct代理实现智能调用

现在,我们将工具包交给AI代理,让它能够根据用户问题自主选择工具

**注:**通过千问文字解析之后的计算规则与之前的计算规则存在冲突,各种校验无法通过,故以下代码校验项完全更改

import random
import math
import json
from datetime import datetime, timedelta
from typing import List
from dotenv import load_dotenv
from langchain_core.tools import StructuredTool, BaseTool
from langchain_community.chat_models import ChatTongyi
from langchain.agents import create_react_agent, AgentExecutor
from langchain_core.prompts import PromptTemplate


# ========== 工具函数实现(接收原始输入字符串,内部解析JSON)==========

def weather_query_tool(input_str: str) -> str:
    """天气查询工具,输入应为JSON字符串,例如 {"city":"北京","date":"明天"}"""
    try:
        params = json.loads(input_str)
        city = params.get("city")
        date = params.get("date")
        if not city:
            return "错误:必须提供城市名称"

        # 处理日期
        if date is None or date == "今天":
            target_date = datetime.now()
        elif date == "明天":
            target_date = datetime.now() + timedelta(days=1)
        else:
            try:
                target_date = datetime.strptime(date, "%Y-%m-%d")
            except ValueError:
                return f"错误:日期格式不正确,请使用YYYY-MM-DD、'今天'或'明天'"

        date_str = target_date.strftime("%Y-%m-%d")
        conditions = ["晴朗", "多云", "阴天", "小雨", "大雨", "雷阵雨"]
        temp = random.randint(10, 38)
        condition = random.choice(conditions)
        return f"{date_str} {city}天气:{condition}{temp}℃"
    except json.JSONDecodeError:
        return f"错误:输入不是有效的JSON格式。输入内容:{input_str}"
    except Exception as e:
        return f"天气查询错误:{str(e)}"


def calculator_tool(input_str: str) -> str:
    """计算器工具,输入应为JSON字符串,例如 {"expression":"125*16"} 或 {"expression":"28*1.8+32"}"""
    try:
        params = json.loads(input_str)
        expression = params.get("expression")
        if not expression:
            return "错误:必须提供数学表达式"

        # 清理表达式
        expr = expression.replace(" ", "").replace("×", "*").replace("÷", "/")
        # 安全过滤
        if "__" in expr or "[" in expr or "]" in expr or "{" in expr or "}" in expr:
            return "错误:表达式包含非法操作"
        # 允许的数学函数
        allowed_names = {
            "sqrt": math.sqrt, "pow": math.pow, "abs": abs,
            "sin": math.sin, "cos": math.cos, "tan": math.tan,
            "log": math.log, "exp": math.exp,
            "pi": math.pi, "e": math.e
        }
        result = eval(expr, {"__builtins__": {}}, allowed_names)
        return f"{expression} = {result}"
    except ZeroDivisionError:
        return "错误:除数不能为零"
    except SyntaxError:
        return "错误:表达式语法不正确"
    except json.JSONDecodeError:
        return f"错误:输入不是有效的JSON格式。输入内容:{input_str}"
    except Exception as e:
        return f"计算错误:{str(e)}"


def power_tool(input_str: str) -> str:
    """幂运算工具,输入应为JSON字符串,例如 {"base":2,"exponent":10}"""
    try:
        params = json.loads(input_str)
        base = float(params.get("base"))
        exponent = float(params.get("exponent"))
        result = base ** exponent
        return f"{base}{exponent} 次方 = {result}"
    except (ValueError, TypeError):
        return "错误:底数和指数必须是数字"
    except json.JSONDecodeError:
        return f"错误:输入不是有效的JSON格式。输入内容:{input_str}"
    except Exception as e:
        return f"幂运算错误:{str(e)}"


# ========== 使用 StructuredTool 包装(不提供 args_schema) ==========
weather_tool = StructuredTool.from_function(
    func=weather_query_tool,
    name="weather_query",
    description="查询指定城市和日期的天气信息。输入必须是JSON字符串,例如:{\"city\":\"北京\",\"date\":\"明天\"}"
)

calculator_tool = StructuredTool.from_function(
    func=calculator_tool,
    name="calculator",
    description="用于数学计算。输入必须是JSON字符串,例如:{\"expression\":\"125*16\"} 或 {\"expression\":\"28*1.8+32\"}"
)

power_tool = StructuredTool.from_function(
    func=power_tool,
    name="power",
    description="计算一个数的幂。输入必须是JSON字符串,例如:{\"base\":2,\"exponent\":10}"
)


# ========== 自定义 Toolkit 类 ==========
class MyToolkit:
    def get_tools(self) -> List[BaseTool]:
        return [weather_tool, calculator_tool, power_tool]


toolkit = MyToolkit()
tools = toolkit.get_tools()
print(f"工具包包含 {len(tools)} 个工具:{[t.name for t in tools]}")

# ========== 初始化模型 ==========
load_dotenv()  # 确保 .env 文件中有 DASHSCOPE_API_KEY
llm = ChatTongyi(model="qwen-plus", temperature=0.1)

# ========== 创建 ReAct 代理 ==========
prompt = PromptTemplate.from_template(
    """你是一个有用的AI助手,可以使用工具来回答问题。

可用工具:
{tools}

工具名称列表:{tool_names}

严格按照以下格式执行:
Thought: 分析问题,决定使用哪个工具
Action: 工具名称(必须是工具名称列表中的一个)
Action Input: 严格的JSON格式参数,例如:{{"city":"北京","date":"明天"}} 或 {{"expression":"125*16"}} 或 {{"base":2,"exponent":10}}
Observation: 工具返回的结果
...(可重复多次)
Thought: 可以回答用户问题了
Final Answer: 最终答案

注意:
1. Action Input必须是有效的JSON字符串,不要加任何额外字符或换行
2. 计算器支持小数点和四则运算,例如"28*1.8+32"是合法的
3. 日期参数支持"明天"或YYYY-MM-DD格式

开始处理用户问题:
用户输入:{input}
{agent_scratchpad}"""
)

agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    handle_parsing_errors=True,
    max_iterations=5
)


def ask_agent(question):
    print(f"\n👤 用户: {question}")
    print("-" * 50)
    response = agent_executor.invoke({"input": question})
    print(f"🤖 助手: {response['output']}")
    print("=" * 50)


if __name__ == "__main__":
    ask_agent("明天北京的天气怎么样?")
    ask_agent("计算 125 乘以 16 等于多少?")
    ask_agent("如果北京今天温度是28度,那么华氏度是多少?(提示:华氏度=摄氏度×1.8+32)")
    ask_agent("计算 2 的 10 次方,然后开平方根")

运行效果verbose=True会显示完整思考过程):


工具包包含 3 个工具:['weather_query', 'calculator', 'power']

👤 用户: 明天北京的天气怎么样?
--------------------------------------------------


> Entering new AgentExecutor chain...
Thought: 需要查询明天北京的天气信息,使用weather_query工具
Action: weather_query
Action Input: {"city":"北京","date":"明天"}2026-03-18 北京天气:晴朗,38℃Final Answer: 明天北京的天气晴朗,气温为38℃。

> Finished chain.
🤖 助手: 明天北京的天气晴朗,气温为38℃。
==================================================

👤 用户: 计算 125 乘以 16 等于多少?
--------------------------------------------------


> Entering new AgentExecutor chain...
Thought: 这是一个简单的数学乘法计算问题,应该使用calculator工具
Action: calculator
Action Input: {"expression":"125*16"}125*16 = 2000Final Answer: 125 乘以 16 等于 2000> Finished chain.
🤖 助手: 125 乘以 16 等于 2000==================================================

👤 用户: 如果北京今天温度是28度,那么华氏度是多少?(提示:华氏度=摄氏度×1.8+32--------------------------------------------------


> Entering new AgentExecutor chain...
Thought: 需要计算华氏度,使用计算器工具进行数学运算:28×1.8+32
Action: calculator
Action Input: {"expression":"28*1.8+32"}28*1.8+32 = 82.4Final Answer: 北京今天28摄氏度对应的华氏度是82.4°F。

> Finished chain.
🤖 助手: 北京今天28摄氏度对应的华氏度是82.4°F。
==================================================

👤 用户: 计算 210 次方,然后开平方根
--------------------------------------------------


> Entering new AgentExecutor chain...
Thought: 需要先计算210次方,然后对结果开平方根。可以使用power工具计算210次方,然后用calculator工具计算平方根。
Action: power
Action Input: {"base":2,"exponent":10}2.010.0 次方 = 1024.0现在需要对1024开平方根,可以使用calculator工具计算sqrt(1024)
Action: calculator
Action Input: {"expression":"sqrt(1024)"}sqrt(1024) = 32.0Final Answer: 32.0

> Finished chain.
🤖 助手: 32.0
==================================================

Process finished with exit code 0

🎯 六、进阶:使用StructuredTool处理复杂输入

当工具需要多个参数时,StructuredTool是最佳选择。它支持复杂的输入结构 。

from langchain.tools import StructuredTool
from langchain.pydantic_v1 import BaseModel, Field
from typing import List, Optional

# 定义复杂的输入模型
class TravelPlanInput(BaseModel):
    destination: str = Field(description="旅行目的地")
    days: int = Field(description="旅行天数", ge=1, le=30)
    interests: List[str] = Field(description="兴趣点列表,如['美食', '历史', '购物']")
    budget: Optional[float] = Field(None, description="预算(元)")

def generate_travel_plan(
    destination: str,
    days: int,
    interests: List[str],
    budget: Optional[float] = None
) -> str:
    """生成旅行计划(模拟)"""
    plan = f"【{destination} {days}天旅行计划】\n"
    plan += f"兴趣点:{', '.join(interests)}\n"
    if budget:
        plan += f"预算:{budget}元\n"
    plan += f"Day 1: 抵达{destination},入住酒店\n"
    plan += f"Day 2-{days-1}: 根据兴趣安排景点\n"
    plan += f"Day {days}: 返程"
    return plan

# 创建StructuredTool
travel_tool = StructuredTool.from_function(
    func=generate_travel_plan,
    name="travel_planner",
    description="生成个性化旅行计划。需要目的地、天数、兴趣点等",
    args_schema=TravelPlanInput
)

# 测试
result = travel_tool.invoke({
    "destination": "杭州",
    "days": 3,
    "interests": ["西湖", "美食", "茶文化"],
    "budget": 3000
})
print(result)

返回 结果:

【杭州 3天旅行计划】
兴趣点:西湖, 美食, 茶文化
预算:3000.0元
Day 1: 抵达杭州,入住酒店
Day 2-2: 根据兴趣安排景点
Day 3: 返程

📝 七、总结与最佳实践

通过今天的学习,我们掌握了:

  1. Tool的概念:AI与外部世界交互的接口

  2. Toolkit的概念:相关工具的集合

  3. 三种创建Tool的方法@tool装饰器、Tool类、BaseTool子类

  4. 实战封装:将天气和计算器转为Tool

  5. 多工具调用:使用ReAct代理智能选择工具

Tool设计黄金法则

原则 说明 示例
1. 命名清晰 使用动词开头的命名 search_web 优于 web
2. 描述精准 说清楚什么时候用 ❌ “计算东西” → ✅ “执行数学计算,支持加减乘除、幂运算”
3. 参数验证 使用Pydantic模型验证 防止错误输入导致崩溃
4. 错误处理 返回友好错误信息 不要抛出异常中断流程
5. 保持简单 一个工具只做一件事 单一职责原则
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐