4. 自定义工具开发

任务目标

本案例将开发一个自定义天气查询工具,模拟调用第三方天气API获取实时天气数据,并将该工具集成到LangChain Agent中,使Agent能够根据用户问题自动决定是否调用此工具。最终通过自然语言对话验证Agent具备工具调用能力。

环境依赖

需要安装以下Python库:

pip install langchain langchain-

同时需要配置 (或使用其他兼容的LLM),本案例使用作为底层模型。

完整实现代码

import json
from typing import Type
from datetime import datetime
import requests
from pydantic import BaseModel, Field
from langchain.tools import BaseTool
from langchain.agents import create__functions_agent, AgentExecutor
from langchain_ import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder

# ========== 1. 定义工具输入Schema ==========
class WeatherInput(BaseModel):
    city: str = Field(description="城市名称,如:北京、上海")

# ========== 2. 实现自定义天气查询工具 ==========
class WeatherTool(BaseTool):
    name: str = "weather_query"
    description: str = "查询指定城市的实时天气信息,输入城市名称,返回温度、天气状况和湿度。"
    args_schema: Type[BaseModel] = WeatherInput

    def _run(self, city: str) -> str:
        """模拟调用第三方天气API(真实场景替换为实际API请求)"""
        # 模拟API响应(实际开发中替换为真实请求)
        mock_data = {
            "北京": {"temp": 25, "condition": "晴", "humidity": 40},
            "上海": {"temp": 28, "condition": "多云", "humidity": 65},
            "广州": {"temp": 30, "condition": "小雨", "humidity": 80},
        }
        # 模拟网络延迟
        import time; time.sleep(0.5)
        if city in mock_data:
            data = mock_data[city]
            return json.dumps({
                "city": city,
                "temperature": data["temp"],
                "condition": data["condition"],
                "humidity": data["humidity"],
                "time": datetime.now().strftime("%Y-%m-%d %H:%M")
            }, ensure_ascii=False)
        return json.dumps({"error": f"未找到城市 {city} 的天气数据"}, ensure_ascii=False)

    async def _arun(self, city: str) -> str:
        """异步版本(本案例不实现异步调用)"""
        raise NotImplementedError("异步调用暂不支持")

# ========== 3. 创建Agent并集成工具 ==========
def create_weather_agent():
    # 初始化LLM
    llm = ChatOpenAI(model="-mini", temperature=0)
    
    # 创建工具实例
    tools = [WeatherTool()]
    
    # 构建Prompt模板
    prompt = ChatPromptTemplate.from_messages([
        ("system", "你是一个天气助手,当用户询问天气时,必须使用weather_query工具获取数据。"),
        ("human", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ])
    
    # 创建Agent
    agent = create__functions_agent(llm, tools, prompt)
    executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
    return executor

# ========== 4. 主程序入口 ==========
if __name__ == "__main__":
    agent_executor = create_weather_agent()
    
    # 测试对话
    test_queries = [
        "北京今天天气怎么样?",
        "上海适合穿什么衣服?",
        "广州会下雨吗?"
    ]
    
    for query in test_queries:
        print(f"\n用户提问: {query}")
        response = agent_executor.invoke({"input": query})
        print(f"Agent回答: {response['output']}")

运行方式

# 设置(Linux/Mac)
export _="your-"

# 运行程序
python weather_agent.py

验证方法与判定标准

程序运行后,观察以下验证点:

  1. 工具调用触发:Agent在收到天气相关问题时,verbose输出中应显示Invoking: weather_query,表明Agent正确选择了自定义工具。
  2. 参数传递正确:工具接收到的城市参数应与用户问题中的城市一致。
  3. 回答质量:Agent最终回答应包含温度、天气状况等从工具返回的数据,而非模型自行编造。
  4. 边界处理:当查询未收录城市(如"深圳")时,Agent应基于工具返回的错误信息给出合理回复,而非虚构数据。

预期输出示例(非实测结果):

用户提问: 北京今天天气怎么样?
Agent回答: 北京今天天气晴朗,气温25°C,湿度40%,适合户外活动。

关键实现说明

本案例的核心在于通过继承BaseTool并实现_run方法完成自定义工具开发。工具的描述(description)和参数Schema(args_schema)是Agent决定何时调用及如何传参的关键依据。实际生产环境中,将_run方法中的模拟数据替换为真实的HTTP请求即可接入第三方天气API,例如:

# 真实API调用示例(替换_run中的模拟逻辑)
resp = requests.get(f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid=YOUR_KEY")
data = resp.json()
return json.dumps({"temperature": data["main"]["temp"] - 273.15})

注意:真实API调用需要处理网络异常、超时和管理等工程问题,但核心工具封装逻辑与本案例完全一致。