在AI大模型应用开发中,给语言模型绑定工具(Tool)是一个关键的进阶技能。这相当于给一位知识渊博但"手无缚鸡之力"的学者配上了各种实用工具,让他不仅能提供建议,还能实际动手解决问题。
想象这样一个场景:当用户询问"北京今天气温多少度"时,未经工具绑定的模型可能会根据训练数据中的历史信息给出一个大概的答案,而绑定天气API工具的模型则会实时查询最新数据,给出精确回答。这种能力差异正是工具绑定的价值所在。
工具绑定的本质是扩展模型的能力边界。通过这种方式,我们可以:
一个完整的工具绑定系统包含三个关键角色:
大模型:作为决策中枢,负责:
工具集:各种功能模块,例如:
执行引擎:负责:
意图识别阶段:
工具选择阶段:
执行反馈阶段:
首先确保已安装必要依赖:
bash复制pip install langchain langchain-openai
定义基础计算工具:
python复制from langchain_core.tools import tool
@tool
def add(a: int, b: int) -> int:
"""执行两个整数的加法运算
参数:
a: 第一个加数
b: 第二个加数
返回:
两数之和
"""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""执行两个整数的乘法运算
参数:
a: 被乘数
b: 乘数
返回:
两数之积
"""
return a * b
配置支持工具调用的语言模型:
python复制from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model_name="gpt-4",
temperature=0.3 # 降低随机性以保证工具调用的稳定性
)
# 绑定工具集
llm_with_tools = llm.bind_tools(
tools=[add, multiply],
tool_choice="auto" # 让模型自动决定是否使用工具
)
实现端到端的工具调用流水线:
python复制from langchain_core.messages import HumanMessage, ToolMessage
def execute_tool_call(tool_call):
"""执行单个工具调用"""
tool_map = {"add": add, "multiply": multiply}
selected_tool = tool_map[tool_call["name"].lower()]
return selected_tool.invoke(tool_call)
def process_query(user_query):
"""处理用户查询的完整流程"""
# 初始化消息历史
messages = [HumanMessage(content=user_query)]
# 第一步:获取模型生成的工具调用指令
ai_msg = llm_with_tools.invoke(messages)
messages.append(ai_msg)
# 第二步:执行所有工具调用
if hasattr(ai_msg, 'tool_calls') and ai_msg.tool_calls:
for tool_call in ai_msg.tool_calls:
tool_result = execute_tool_call(tool_call)
messages.append(ToolMessage(
content=str(tool_result),
tool_call_id=tool_call['id']
))
# 第三步:获取最终回复
final_response = llm_with_tools.invoke(messages)
return final_response.content
# 示例调用
print(process_query("请计算365乘以24的结果"))
构建更复杂的实时数据查询工具:
python复制import requests
from typing import Optional
from datetime import datetime
@tool
def get_weather(
location: str,
date: Optional[str] = None
) -> str:
"""查询指定地点的天气情况
参数:
location: 城市名称,如"北京"
date: 查询日期,格式YYYY-MM-DD,默认为当天
返回:
天气情况的自然语言描述
"""
base_url = "https://api.weatherapi.com/v1/forecast.json"
params = {
"key": "YOUR_API_KEY",
"q": location,
"days": 1,
"aqi": "no",
"alerts": "no"
}
try:
response = requests.get(base_url, params=params)
data = response.json()
forecast = data['forecast']['forecastday'][0]
return (
f"{location}天气:{forecast['day']['condition']['text']},"
f"最高温{forecast['day']['maxtemp_c']}℃,"
f"最低温{forecast['day']['mintemp_c']}℃,"
f"降水概率{forecast['day']['daily_chance_of_rain']}%"
)
except Exception as e:
return f"天气查询失败:{str(e)}"
配置模型同时使用多个工具:
python复制# 初始化模型并绑定多个工具
llm_with_multi_tools = ChatOpenAI(
model_name="gpt-4",
temperature=0.3
).bind_tools(
tools=[add, multiply, get_weather],
parallel_tool_calls=True # 允许并行调用多个工具
)
def process_complex_query(query):
"""处理可能涉及多个工具的复杂查询"""
messages = [HumanMessage(content=query)]
ai_msg = llm_with_multi_tools.invoke(messages)
messages.append(ai_msg)
if hasattr(ai_msg, 'tool_calls') and ai_msg.tool_calls:
for tool_call in ai_msg.tool_calls:
tool_name = tool_call["name"].lower()
if tool_name == "get_weather":
result = get_weather.invoke(tool_call)
elif tool_name == "add":
result = add.invoke(tool_call)
elif tool_name == "multiply":
result = multiply.invoke(tool_call)
messages.append(ToolMessage(
content=str(result),
tool_call_id=tool_call['id']
))
final_response = llm_with_multi_tools.invoke(messages)
return final_response.content
# 示例:混合计算和天气查询
print(process_complex_query(
"北京今天气温多少度?如果是这个温度的两倍是多少?"
))
工具的函数文档字符串(docstring)直接影响模型对工具功能的理解和使用准确性。好的文档应包含:
示例优化:
python复制@tool
def calculate_tip(
bill_amount: float,
service_quality: str,
party_size: int = 1
) -> dict:
"""计算餐厅小费金额
根据账单金额、服务质量评分和用餐人数计算建议小费
参数:
bill_amount: 账单总金额(美元)
service_quality: 服务质量评分,可选值:
'excellent' - 20%小费
'good' - 15%小费
'average' - 10%小费
party_size: 用餐人数,大于6人可能加收服务费
返回:
{
"tip_amount": 小费金额,
"total_amount": 总计应付金额,
"tip_percentage": 小费比例
}
"""
# 实现代码...
健壮的工具调用需要完善的错误处理:
python复制def safe_tool_execution(tool_call, max_retries=3):
"""带错误处理和重试的工具执行"""
attempts = 0
last_error = None
while attempts < max_retries:
try:
tool_name = tool_call["name"].lower()
if tool_name == "get_weather":
return get_weather.invoke(tool_call)
# 其他工具判断...
except Exception as e:
last_error = str(e)
attempts += 1
time.sleep(1) # 简单的退避策略
return f"工具执行失败:{last_error}"
通过提示工程优化模型对工具的选择:
python复制from langchain_core.prompts import ChatPromptTemplate
tool_selection_prompt = ChatPromptTemplate.from_messages([
("system", """你是一个智能助手,可以调用以下工具解决问题:
{tools}
请严格根据以下规则选择工具:
1. 只有当用户请求明确需要工具功能时才使用工具
2. 优先使用更简单、更直接的工具
3. 不确定时不要随意调用工具"""),
("human", "{input}")
])
llm_with_optimized_tools = (
tool_selection_prompt |
llm.bind_tools(tools=[...])
)
当工具调用不按预期工作时,可以按以下步骤排查:
验证工具描述:
检查模型输出:
测试工具本身:
验证执行流程:
使用LangChain的调试回调:
python复制from langchain_core.callbacks import ConsoleCallbackHandler
result = llm_with_tools.invoke(
"计算圆周率的前10位",
config={"callbacks": [ConsoleCallbackHandler()]}
)
分析工具调用日志:
python复制import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
@tool
def debug_example():
"""示例调试工具"""
logger.debug("工具开始执行")
try:
# 工具逻辑
logger.info("工具执行成功")
return "成功"
except Exception as e:
logger.error(f"工具执行失败:{e}")
raise
关键监控指标建议:
| 指标类别 | 具体指标 | 监控方法 |
|---|---|---|
| 工具调用准确性 | 工具选择正确率 | 记录模型选择的工具与人工判断的匹配度 |
| 执行效率 | 平均响应时间 | 测量从用户提问到最终回复的总耗时 |
| 错误率 | 工具调用失败率 | 统计工具执行失败的比例 |
| 用户体验 | 问题解决率 | 评估用户查询被满意解决的比例 |
实现示例:
python复制import time
from collections import defaultdict
class ToolMonitoring:
def __init__(self):
self.metrics = defaultdict(list)
def record(self, tool_name, success, duration):
self.metrics[tool_name].append({
"timestamp": time.time(),
"success": success,
"duration": duration
})
def get_stats(self):
return {
tool: {
"call_count": len(records),
"success_rate": sum(r["success"] for r in records)/len(records),
"avg_duration": sum(r["duration"] for r in records)/len(records)
}
for tool, records in self.metrics.items()
}
monitor = ToolMonitoring()
@tool
def monitored_tool():
start = time.time()
try:
result = some_operation()
monitor.record("some_tool", True, time.time()-start)
return result
except Exception:
monitor.record("some_tool", False, time.time()-start)
raise
实现基于角色的工具访问控制:
python复制from enum import Enum
class UserRole(Enum):
GUEST = 1
USER = 2
ADMIN = 3
def role_based_tool_filter(tools, user_role):
"""根据用户角色过滤可用工具"""
restricted_tools = {
UserRole.GUEST: ["get_weather"],
UserRole.USER: ["get_weather", "calculator"],
UserRole.ADMIN: ["get_weather", "calculator", "db_query"]
}
return [t for t in tools if t.name in restricted_tools[user_role]]
# 使用示例
available_tools = role_based_tool_filter(
[get_weather, add, multiply, db_query],
current_user.role
)
llm_with_tools = llm.bind_tools(available_tools)
对所有工具参数进行严格验证:
python复制from pydantic import BaseModel, validator
class WeatherParams(BaseModel):
location: str
date: Optional[str] = None
@validator("location")
def validate_location(cls, v):
if len(v) > 50:
raise ValueError("地点名称过长")
if not v.isalpha():
raise ValueError("地点名称应只包含字母")
return v.title()
@validator("date")
def validate_date(cls, v):
if v is None:
return v
try:
datetime.strptime(v, "%Y-%m-%d")
return v
except ValueError:
raise ValueError("日期格式应为YYYY-MM-DD")
@tool(args_schema=WeatherParams)
def safe_get_weather(params: WeatherParams) -> str:
"""安全版本的天气查询工具"""
return get_weather(params.location, params.date)
实施数据脱敏策略:
python复制import re
def sanitize_output(output: str) -> str:
"""对工具输出进行脱敏处理"""
# 移除信用卡号
output = re.sub(r"\b(?:\d[ -]*?){13,16}\b", "[CREDIT_CARD]", output)
# 移除电话号码
output = re.sub(r"\b(?:\+?\d{1,3}[ -]?)?\(?\d{3}\)?[ -]?\d{3}[ -]?\d{4}\b", "[PHONE]", output)
return output
@tool
def safe_user_lookup(user_id: str) -> str:
"""带输出脱敏的用户查询工具"""
raw_data = db_query_user(user_id)
return sanitize_output(json.dumps(raw_data))
将工具绑定与自动化工作流结合:
python复制from prefect import flow, task
from langchain.agents import AgentExecutor
@task
def prepare_tools():
"""准备工具集"""
return [get_weather, calculator, email_sender]
@flow
def automated_workflow(user_query):
"""自动化处理用户查询的工作流"""
tools = prepare_tools()
agent = AgentExecutor.from_agent_and_tools(
agent=create_react_agent(llm, tools),
tools=tools
)
result = agent.invoke({"input": user_query})
return result["output"]
# 触发工作流
automated_workflow("查询纽约天气并发送到我的邮箱")
开发医疗领域专业工具示例:
python复制@tool
def drug_interaction_check(
drug_a: str,
drug_b: str,
patient_age: int,
patient_weight: float
) -> dict:
"""检查两种药物之间的相互作用
参数:
drug_a: 第一种药物名称
drug_b: 第二种药物名称
patient_age: 患者年龄
patient_weight: 患者体重(kg)
返回:
{
"interaction_level": "无|轻度|中度|重度",
"description": "相互作用描述",
"recommendation": "用药建议"
}
"""
# 实现实际的药物相互作用检查逻辑
return {
"interaction_level": "中度",
"description": "可能增加中枢神经系统抑制",
"recommendation": "避免同时使用或在医生监督下使用"
}
实现多工具协同解决复杂问题:
python复制def solve_complex_problem(problem_description):
"""使用多工具协同解决复杂问题"""
tools = [calculator, calendar, email_sender, document_generator]
llm_with_tools = llm.bind_tools(tools)
# 第一轮:分解问题
decomposition = llm_with_tools.invoke(
f"请将以下问题分解为可单独解决的子任务:\n{problem_description}"
)
# 处理每个子任务
solutions = []
for subtask in extract_subtasks(decomposition.content):
solution = llm_with_tools.invoke(
f"请解决以下子任务,必要时使用工具:\n{subtask}"
)
solutions.append(solution.content)
# 综合最终解决方案
final_response = llm_with_tools.invoke(
f"基于以下子任务解决方案,给出完整回答:\n{solutions}"
)
return final_response.content
实现工具版本控制:
python复制from typing import Literal
@tool
def enhanced_calculator(
operation: Literal["add", "subtract", "multiply", "divide"],
numbers: list[float],
precision: int = 2
) -> float:
"""增强版计算器工具(v2.1)
支持更多运算类型和精度控制
变更记录:
v2.1 - 新增精度控制参数
v2.0 - 支持列表输入和多数字运算
v1.0 - 基础四则运算
参数:
operation: 运算类型
numbers: 操作数列表
precision: 结果小数位数
返回:
运算结果
"""
# 实现代码...
建立工具性能评估体系:
python复制import timeit
from statistics import mean
def benchmark_tool(tool_func, test_cases, runs=100):
"""工具性能基准测试"""
results = []
for case in test_cases:
timer = timeit.Timer(lambda: tool_func(*case["args"]))
times = timer.repeat(repeat=runs, number=1)
avg_time = mean(times) * 1000 # 转换为毫秒
# 验证结果正确性
try:
result = tool_func(*case["args"])
correct = case["validator"](result)
except Exception:
correct = False
results.append({
"case": case["name"],
"avg_time_ms": avg_time,
"success_rate": correct
})
return results
# 使用示例
calculator_benchmark = benchmark_tool(
add,
test_cases=[
{
"name": "small numbers",
"args": (2, 3),
"validator": lambda x: x == 5
},
# 更多测试用例...
]
)
实现工具的动态注册机制:
python复制class ToolRegistry:
def __init__(self):
self._tools = {}
def register(self, tool):
"""注册新工具"""
if tool.name in self._tools:
raise ValueError(f"工具{tool.name}已存在")
self._tools[tool.name] = tool
def get_tool(self, name):
"""获取工具实例"""
return self._tools.get(name)
def list_tools(self):
"""列出所有可用工具"""
return list(self._tools.values())
# 使用示例
registry = ToolRegistry()
registry.register(add)
registry.register(multiply)
# 动态绑定最新工具集
llm_with_dynamic_tools = llm.bind_tools(registry.list_tools())
探索自动化工具创建与优化的方向:
python复制from langchain_experimental.autonomous_agents import AutoGPT
auto_agent = AutoGPT.from_llm_and_tools(
llm=llm,
tools=[base_tools],
memory=memory,
max_iterations=10
)
# 让AI自动创建新工具
auto_agent.run(
"创建一个能根据食谱计算营养成分的工具,"
"然后用它计算'番茄炒蛋'的热量"
)
分析新兴的工具交互范式:
实现示例:
python复制def adaptive_tool_selection(user_input, conversation_history):
"""自适应工具选择策略"""
# 分析对话历史确定最佳工具
if "天气" in user_input:
return get_weather
elif "计算" in user_input:
return calculator
# 其他判断逻辑...
# 默认使用通用问答
return None
# 在对话循环中应用
current_tool = adaptive_tool_selection(user_input, history)
if current_tool:
result = current_tool.invoke(extract_parameters(user_input))
response = format_tool_result(result)
else:
response = llm.invoke(user_input)
构建全面的工具使用评估框架:
python复制from typing import List, Dict
from dataclasses import dataclass
@dataclass
class ToolEvaluationResult:
accuracy: float
efficiency: float
usability: float
safety: float
class ToolEvaluator:
def evaluate(
self,
tool_name: str,
test_cases: List[Dict],
user_feedback: List[Dict]
) -> ToolEvaluationResult:
"""综合评估工具性能"""
# 实现评估逻辑
pass
# 使用示例
evaluator = ToolEvaluator()
weather_tool_report = evaluator.evaluate(
"get_weather",
test_cases=[...],
user_feedback=[...]
)