自定义tool工具使用
@too装饰器
@tool装饰器最简单、最直观的工具创建方式。
概述
- 自动参数推断:基于函数签名自动生成工具的参数schema
- 简化配置:只需提供工具名称和描述即可快速创建
- 同步执行:默认支持同步函数调用,异步需要单独定义
- 快速验证:适合概念验证和快速迭代开发
from langchain_core.tools import tool
from langchain.agents import create_agent
from base import load_chat_model
# 1. 定义一个简单的 Tool (Runnable)
@tool
def multiply(a: int, b: int) -> int:
"""Multiplies a and b."""
return a * b
# 2.导入模型
model = load_chat_model(
model="glm-4.7", # 指定OpenAI的gpt-4o-mini模型
provider="openai", # 指定模型提供商为openai
)
# 3.创建Agent
agent = create_agent(model=model,tools=[multiply])
# 4. 调用Agent
response = agent.invoke({
"messages": [{
"role": "user",
"content": "帮我计算12乘以6等于多少?"
}]
})
result = response["messages"]
print(response)
StructuredTool
StructuredTool.from_function()方法提供了更强大的工具创建能力,支持完整的参数校验和异步执行,适合生产环境使用。
概述
- 强类型校验:支持Pydantic模型进行参数验证
- 异步支持:通过
coroutine参数支持异步函数 - 完整元数据:支持name、description、return_direct等完整配置
- 生产就绪:内置错误处理和参数校验机制
from pydantic import BaseModel, Field
from langchain_core.tools import StructuredTool
"""
1. 通过 Pydantic BaseModel 定义参数,提供:
- 参数描述(description)
- 必填/可选约束
- 更清晰的 Schema 文档
"""
class DivideInput(BaseModel):
"""除法工具输入参数"""
dividend: float = Field(description="被除数")
divisor: float = Field(description="除数,不能为零")
def divide(dividend: float, divisor: float) -> float:
"""执行除法运算,支持浮点数"""
if divisor == 0:
raise ValueError("除数不能为零")
return dividend / divisor
# 2. 创建带参数校验的工具
division_tool = StructuredTool.from_function(
func=divide,
name="DivisionTool",
description="安全执行除法运算,自动处理除零错误",
args_schema=DivideInput, # 显式指定参数模式
return_direct=False, # 是否直接返回工具结果(不经过 LLM 再次处理)
)
# 3. 测试参数校验(触发 Pydantic 验证)
try:
division_tool.invoke({"a": 10, "b": 2}) # 错误:参数名不匹配
except Exception as e:
print(f"参数校验失败:{e}")
# 4. 正确调用
result = division_tool.invoke({"dividend": 10, "divisor": 2})
print(f"除法结果:{result}")
继承structuredTool
继承
StructuredTool类创建工具提供了最大的灵活性和控制力,适合复杂业务逻辑和状态管理需求。
import os
from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from pydantic import BaseModel, Field
from langchain_core.tools import StructuredTool
from typing import Type
# 1. 定义包含业务逻辑的工具
class OrderQueryInput(BaseModel): # 此处继承BaseModel
"""订单查询参数"""
order_id: str = Field(description="订单编号,格式:ORD-2024-XXXX")
include_details: bool = Field(default=False, description="是否包含商品明细")
class OrderQueryTool(StructuredTool): # 此处继承StructuredTool
"""订单查询工具"""
name: str = "query_order"
description: str = "查询电商平台订单状态和物流信息"
args_schema: Type[BaseModel] = OrderQueryInput
return_direct: bool = False
# 实现父类方法_run()
def _run(self, order_id: str, include_details: bool = False) -> dict:
# 模拟数据库查询
order_db = {
"ORD-2024-1234": {"status": "已发货", "express": "顺丰", "amount": 299},
"ORD-2024-5678": {"status": "待付款", "express": "", "amount": 149},
}
# 处理查询逻辑
if order_id not in order_db:
return {"error": f"订单 {order_id} 不存在"}
result = order_db[order_id]
# 处理包含明细的情况
if include_details:
result["items"] = ["商品A × 2", "商品B × 1"]
return result
# 2. 初始化模型
model = init_chat_model(
model="openai:gpt-4o-mini",
temperature=0,
api_key=os.getenv("OPENAI_API_KEY")
)
# 3. 创建 ReAct Agent(自动处理工具调用)
agent = create_agent(
model=model,
tools=[OrderQueryTool()], # 直接传入 StructuredTool 实例
system_prompt="你是一个电商客服助手,使用工具查询订单信息,回答要友好且准确",
checkpointer=InMemorySaver()
)
# 4. 执行并观察 ReAct 过程
async def run_agent():
config = {"configurable": {"thread_id": "customer_001"},"recursion_limit": 15}# 最大 15 次迭代
query = "请帮我查订单 ORD-2024-1234 的详细状态,包括商品明细"
async for step in agent.astream(
{"messages": [{"role": "user", "content": query}]},
config=config,
stream_mode="values" # 流式输出模式,返回每一步的完整状态
):
message = step["messages"][-1]
message.pretty_print()
print("-" * 50)
# 运行
await run_agent()
MCP
远程连接MCP服务器
魔搭社区高德地图mcp服务器:www.modelscope.cn/mcp/servers…
申请高德地图的api地址:console.amap.com/dev/key/app
- Stdio
{
"mcpServers": {
"amap-maps": {
"args": [
"-y",
"@amap/amap-maps-mcp-server"
],
"command": "npx",
"env": {
"AMAP_MAPS_API_KEY": ""
}
}
}
}
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain.agents import create_agent
# 1. 正确的 MCP 配置格式(适用于 langchain_mcp_adapters)
# MultiServerMCPClient 需要的是扁平的字典结构,每个服务器是一个键值对
mcp_config = {
# 本地 Python MCP 服务器
"math": {
"transport": "stdio",
"command": "python",
"args": ["mcp_server.py"]
},
# 高德地图 MCP 服务器
"amap-maps": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "@amap/amap-maps-mcp-server"],
"env": {
"AMAP_MAPS_API_KEY": os.getenv("AMAP_MAPS_API_KEY"),
}
}
}
# 2. 创建 MCP 客户端
client = MultiServerMCPClient(mcp_config)
print("正在连接 MCP 服务器...")
# 3. client.get_tools() 会自动:
# 1. 调用所有服务器的 list_tools 接口
# 2. 将 MCP Tool Schema 转换为 LangChain StructuredTool
tools = await client.get_tools()
print(f"成功加载 {len(tools)} 个工具: {[t.name for t in tools]}")
# 4. 创建 Agent
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 直接将转换好的 tools 传给 create_agent
agent = create_agent(llm, tools,system_prompt="你是会调用工具进行天气查询、地图查询、网页部署的智能助手")
# 5. 运行 Agent
print("\n--- 开始测试 Agent ---")
# 6. 这里我们模拟一个请求(具体 prompt 取决于你的工具功能)
query = "请帮我搜索查询一下北京市今天的天气,并计算一下最大温差是多少度?"
inputs = {"messages": [HumanMessage(content=query)]}
async for chunk in agent.astream(inputs, stream_mode="values"):
last_msg = chunk["messages"][-1]
print(f"\n[{type(last_msg).__name__}]:")
print(last_msg.content)
if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
print(f">>> 调用工具详情: {last_msg.tool_calls}")
工具调用错误或者乱调情况
Tool Router (最有效)
Tool Router是解决工具调用混乱的最有效方法,它通过专门的工具路由机制来精确匹配用户意图与可用工具。
实现
- 意图识别:使用专门的分类器识别用户意图
- 工具匹配:基于意图选择最合适的工具
- 参数验证:在调用前验证参数有效性
- 错误处理:提供优雅的降级策略
# 定义意图分类系统提示
INTENT_SYSTEM_PROMPT = """
你是一个专业的意图分类器,请只返回以下类别之一:
- search
- pdf
- database
- math
- none
并严格只返回类别名,不要输出其它内容。
"""
引入“意图分类模型” (工程最佳方案)
意图分类模型通过机器学习方法识别用户请求的真实意图,从根本上解决工具误用问题。
# 创建一个意图识别模型
intent_llm = load_chat_model(
model="gpt-4o-mini", # 指定OpenAI的gpt-4o-mini模型
provider="openai", # 指定模型提供商为openai
)
动态加载工具(避免上下过长)
动态工具加载机制根据当前对话上下文和用户意图,按需加载相关工具,避免一次性加载所有工具导致的上下文过长问题。
模型根据"意图"动态读取特定工具,不把所有工具一次性喂给模型。
# 通过Tool 工具分组
TOOL_GROUPS = {
"search": [search_web],
"pdf": [extract_pdf_text],
"database": [query_database],
"math": [calculate],
}
统一工具规范(提高准确率)
通过强制化Schema和规范化提示词,建立统一的工具使用规范。
@tool
def query_database(sql: str) -> str:
"""
执行 SQL 查询,仅限内部业务数据库。
参数:sql Sql语句。
示例:如 select * from users limit 5
"""
return f"模拟 SQL 执行:{sql}"
采用“工具过滤Prompt”修饰模型行为(成本最低)
通过系统Prompt显式指导模型行为,设置工具使用边界。
Prompt示例
你必须严格根据工具描述选择工具。 不能猜测工具功能。 如果没有合适的工具,请回答"无合适工具"。
agent = create_agent(
model=model,
tools=tools,
system_prompt="你是一个 helpful assistant,可以使用工具回答问题。你必须严格根据工具描述选择工具!如果没有合适的工具,请回答“无合适工具”"
)