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
验证方法与判定标准
程序运行后,观察以下验证点:
- 工具调用触发:Agent在收到天气相关问题时,verbose输出中应显示
Invoking: weather_query,表明Agent正确选择了自定义工具。 - 参数传递正确:工具接收到的城市参数应与用户问题中的城市一致。
- 回答质量:Agent最终回答应包含温度、天气状况等从工具返回的数据,而非模型自行编造。
- 边界处理:当查询未收录城市(如"深圳")时,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调用需要处理网络异常、超时和管理等工程问题,但核心工具封装逻辑与本案例完全一致。