mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
839 字
2 分钟
Agent 工具调用:Function Calling 与 Tool Use 实战
2024-12-31

如果 Agent 是「大脑」,那工具就是它的「双手」。

没有工具的 Agent 只能进行纯文本交互,无法访问外部信息,无法执行实际操作,无法真正改变世界。

工具调用是 Agent 从「聊天机器人」进化为「智能助手」的关键能力。

本文要点#

  • Function Calling 核心原理
  • OpenAI vs Anthropic 工具调用对比
  • Tool Schema 设计最佳实践
  • 工具选择与排序策略
  • 多工具协同模式
  • 常见工具类型与实现

一、工具调用的本质#

1.1 为什么需要工具调用?#

问题:大模型的局限性
1. 知识截止:训练数据有时效性,无法获取最新信息
2. 计算能力:复杂数学计算容易出错
3. 无外部访问:无法访问数据库、API、文件系统
4. 无实际操作:只能「说」,不能「做」
解决:工具调用
让大模型能够调用外部工具,获取信息、执行操作。

1.2 工具调用的基本流程#

sequenceDiagram participant U as 用户 participant A as Agent (LLM) participant T as 工具 U->>A: 提出请求 Note over A: 分析任务<br/>判断需要调用工具 A->>A: 生成工具调用参数 A->>T: 调用工具 T-->>A: 返回结果 Note over A: 处理工具结果<br/>生成最终回答 A-->>U: 返回结果

1.3 核心概念#

┌─────────────────────────────────────────────────────────────┐
│ 工具调用核心概念 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Tool(工具) │
│ ├── 定义:可供调用的外部功能 │
│ ├── 示例:搜索、数据库查询、API调用、代码执行 │
│ └── 属性:名称、描述、参数Schema、执行函数 │
│ │
│ Tool Schema │
│ ├── 定义:工具的结构化描述 │
│ ├── 内容:名称、功能描述、参数定义 │
│ └── 格式:JSON Schema │
│ │
│ Function Calling │
│ ├── 定义:模型生成工具调用参数的能力 │
│ ├── 过程:理解需求 → 选择工具 → 构造参数 │
│ └── 输出:结构化的工具调用请求 │
│ │
│ Tool Use │
│ ├── 定义:实际执行工具调用 │
│ ├── 过程:接收参数 → 执行函数 → 返回结果 │
│ └── 实现:开发者提供的执行逻辑 │
│ │
└─────────────────────────────────────────────────────────────┘

二、Function Calling 原理详解#

2.1 OpenAI Function Calling#

基本使用#

from openai import OpenAI
client = OpenAI()
# 1. 定义工具
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如:北京、上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,默认摄氏度"
}
},
"required": ["city"]
}
}
}
]
# 2. 发送请求
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "北京今天天气怎么样?"}
],
tools=tools,
tool_choice="auto" # auto: 自动决定是否调用工具
)
# 3. 处理响应
message = response.choices[0].message
if message.tool_calls:
# 模型决定调用工具
for tool_call in message.tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print(f"调用工具: {function_name}")
print(f"参数: {arguments}")
# 执行工具并获取结果
result = execute_tool(function_name, arguments)

完整的函数调用循环#

import json
from openai import OpenAI
class OpenAIFunctionCaller:
"""OpenAI Function Calling 完整实现"""
def __init__(self, model: str = "gpt-4o"):
self.client = OpenAI()
self.model = model
self.tools = []
self.tool_functions = {}
def register_tool(self, schema: dict, function: callable):
"""注册工具"""
self.tools.append(schema)
self.tool_functions[schema["function"]["name"]] = function
def run(self, user_message: str, max_iterations: int = 5) -> str:
"""执行对话,支持多轮工具调用"""
messages = [
{"role": "user", "content": user_message}
]
for _ in range(max_iterations):
# 调用模型
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=self.tools,
tool_choice="auto"
)
message = response.choices[0].message
# 检查是否需要调用工具
if not message.tool_calls:
# 无需调用工具,返回最终回答
return message.content
# 添加助手消息到历史
messages.append(message)
# 处理每个工具调用
for tool_call in message.tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# 执行工具
result = self._execute_tool(function_name, arguments)
# 添加工具结果到历史
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
return "达到最大迭代次数,任务未完成"
def _execute_tool(self, name: str, arguments: dict) -> any:
"""执行工具"""
if name not in self.tool_functions:
return f"错误:未知工具 {name}"
try:
return self.tool_functions[name](**arguments)
except Exception as e:
return f"工具执行错误:{str(e)}"
# 使用示例
caller = OpenAIFunctionCaller()
# 注册天气工具
caller.register_tool(
schema={
"type": "function",
"function": {
"name": "get_weather",
"description": "获取城市天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"}
},
"required": ["city"]
}
}
},
function=lambda city: f"{city}今天晴,温度15-25°C"
)
# 执行
result = caller.run("北京今天天气怎么样?")
print(result)

2.2 Anthropic Tool Use#

基本使用#

from anthropic import Anthropic
client = Anthropic()
# 1. 定义工具(与 OpenAI 略有不同)
tools = [
{
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
]
# 2. 发送请求
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": "北京今天天气怎么样?"}
],
tools=tools
)
# 3. 处理响应
for block in response.content:
if block.type == "tool_use":
# 工具调用
tool_name = block.name
tool_input = block.input
print(f"调用工具: {tool_name}")
print(f"参数: {tool_input}")
# 执行工具
result = execute_tool(tool_name, tool_input)
# 继续对话
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": "北京今天天气怎么样?"},
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
}
]
}
],
tools=tools
)

完整实现#

import anthropic
from typing import List, Dict, Any, Callable
class AnthropicToolUser:
"""Anthropic Tool Use 完整实现"""
def __init__(self, model: str = "claude-sonnet-4-20250514"):
self.client = anthropic.Anthropic()
self.model = model
self.tools = []
self.tool_functions: Dict[str, Callable] = {}
def register_tool(self, name: str, description: str,
input_schema: dict, function: Callable):
"""注册工具"""
self.tools.append({
"name": name,
"description": description,
"input_schema": input_schema
})
self.tool_functions[name] = function
def run(self, user_message: str, max_iterations: int = 5) -> str:
"""执行对话"""
messages = [
{"role": "user", "content": user_message}
]
for _ in range(max_iterations):
response = self.client.messages.create(
model=self.model,
max_tokens=4096,
messages=messages,
tools=self.tools
)
# 检查是否有工具调用
tool_use_blocks = [
block for block in response.content
if block.type == "tool_use"
]
if not tool_use_blocks:
# 没有工具调用,返回文本结果
text_blocks = [
block for block in response.content
if block.type == "text"
]
return "".join(block.text for block in text_blocks)
# 添加助手响应
messages.append({
"role": "assistant",
"content": response.content
})
# 处理工具调用
tool_results = []
for block in tool_use_blocks:
result = self._execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
})
# 添加工具结果
messages.append({
"role": "user",
"content": tool_results
})
return "达到最大迭代次数"
def _execute_tool(self, name: str, arguments: dict) -> Any:
"""执行工具"""
if name not in self.tool_functions:
return {"error": f"未知工具: {name}"}
try:
return self.tool_functions[name](**arguments)
except Exception as e:
return {"error": str(e)}

2.3 OpenAI vs Anthropic 对比#

┌─────────────────────────────────────────────────────────────┐
│ Function Calling 对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ OpenAI Anthropic │
│ ──────────────────────────── ──────────────────────── │
│ │
│ Schema 格式: Schema 格式: │
│ { { │
│ "type": "function", "name": "...", │
│ "function": { "description": "...", │
│ "name": "...", "input_schema": {...} │
│ "description": "...", } │
│ "parameters": {...} │
│ } │
│ } │ │
│ │
│ 工具调用格式: 工具调用格式: │
│ message.tool_calls[] response.content[] │
│ .function.name .type == "tool_use" │
│ .function.arguments .name │
│ .id .input │
│ │
│ 工具结果格式: 工具结果格式: │
│ { { │
│ "role": "tool", "type": "tool_result", │
│ "tool_call_id": "...", "tool_use_id": "...", │
│ "content": "..." "content": "..." │
│ } } │
│ │
│ 特点: 特点: │
│ • tool_choice 可控制 • 必须在 tools 中定义 │
│ • 支持并行调用 • 更严格的 Schema 验证 │
│ • 调用更简洁 • 错误处理更详细 │
│ │
└─────────────────────────────────────────────────────────────┘

三、Tool Schema 设计最佳实践#

3.1 Schema 设计原则#

flowchart TD A[Tool Schema 设计] --> B[清晰命名] A --> C[详细描述] A --> D[合理参数] A --> E[类型安全] B --> B1[动词+名词] B --> B2[避免缩写] C --> C1[说明功能] C --> C2[使用场景] C --> C3[返回内容] D --> D1[必填/可选] D --> D2[默认值] D --> D3[参数校验] E --> E1[使用枚举] E --> E2[设置范围] E --> E3[格式约束]

3.2 好的 Schema 示例#

# 好的设计
good_weather_tool = {
"type": "function",
"function": {
"name": "get_current_weather",
"description": """获取指定城市的实时天气信息。
使用场景:
- 用户询问某地天气时调用
- 需要天气数据做决策时使用
返回信息:温度、湿度、天气状况、风向风速""",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,支持中英文,如:北京、Beijing"
},
"country": {
"type": "string",
"description": "国家代码(ISO 3166),如:CN、US。可选,用于区分同名城市"
},
"units": {
"type": "string",
"enum": ["metric", "imperial"],
"description": "温度单位:metric(摄氏度),imperial(华氏度)。默认 metric"
}
},
"required": ["city"]
}
}
}
# 不好的设计
bad_weather_tool = {
"type": "function",
"function": {
"name": "weather", # 不清晰
"description": "获取天气", # 太简单
"parameters": {
"type": "object",
"properties": {
"c": { # 缩写不明确
"type": "string"
# 缺少描述
},
"u": { # 缩写不明确
"type": "string"
# 没有限制值范围
}
}
}
}
}

3.3 参数设计技巧#

# 1. 使用枚举限制选项
status_tool = {
"type": "function",
"function": {
"name": "update_order_status",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"status": {
"type": "string",
"enum": ["pending", "processing", "shipped", "delivered", "cancelled"],
"description": "订单状态"
}
}
}
}
}
# 2. 使用默认值减少必填参数
search_tool = {
"type": "function",
"function": {
"name": "search_products",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"},
"page": {
"type": "integer",
"description": "页码,从1开始",
"default": 1
},
"page_size": {
"type": "integer",
"description": "每页数量",
"default": 20
},
"sort_by": {
"type": "string",
"enum": ["relevance", "price_asc", "price_desc", "rating"],
"default": "relevance"
}
},
"required": ["query"]
}
}
}
# 3. 复杂对象使用嵌套结构
order_tool = {
"type": "function",
"function": {
"name": "create_order",
"parameters": {
"type": "object",
"properties": {
"customer": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string", "format": "email"},
"phone": {"type": "string"}
},
"required": ["name", "email"]
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product_id": {"type": "string"},
"quantity": {"type": "integer", "minimum": 1}
},
"required": ["product_id", "quantity"]
}
},
"shipping_address": {
"type": "object",
"properties": {
"street": {"type": "string"},
"city": {"type": "string"},
"zip_code": {"type": "string"}
}
}
},
"required": ["customer", "items"]
}
}
}

四、工具选择与排序策略#

4.1 tool_choice 参数#

# OpenAI 的 tool_choice 选项
options = {
"auto": "模型自动决定是否调用工具",
"none": "强制不调用任何工具",
"required": "强制必须调用至少一个工具",
{"type": "function", "function": {"name": "xxx"}}: "强制调用指定工具"
}
# 示例:强制调用搜索工具
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "搜索最新的AI新闻"}],
tools=[search_tool],
tool_choice={"type": "function", "function": {"name": "web_search"}}
)

4.2 工具选择策略#

flowchart TD A[用户请求] --> B{分析意图} B --> C[识别关键实体] B --> D[理解任务类型] C --> E{匹配工具} D --> E E --> F{单工具?} F -->|是| G[调用对应工具] F -->|否| H[工具排序] H --> I[按相关性排序] I --> J[按历史成功率] J --> K[返回排序结果]

4.3 智能工具选择器#

from typing import List, Dict
import re
class ToolSelector:
"""智能工具选择器"""
def __init__(self, tools: List[Dict]):
self.tools = tools
self.tool_stats = {} # 工具统计信息
def select_tools(self, query: str, max_tools: int = 5) -> List[Dict]:
"""选择最相关的工具"""
scored_tools = []
for tool in self.tools:
score = self._calculate_relevance(tool, query)
scored_tools.append((tool, score))
# 按分数排序
scored_tools.sort(key=lambda x: x[1], reverse=True)
return [t[0] for t in scored_tools[:max_tools]]
def _calculate_relevance(self, tool: Dict, query: str) -> float:
"""计算工具与查询的相关性"""
function = tool.get("function", {})
name = function.get("name", "").lower()
description = function.get("description", "").lower()
score = 0.0
# 1. 名称匹配
name_words = name.split("_")
for word in name_words:
if word in query.lower():
score += 0.3
# 2. 描述关键词匹配
query_words = set(query.lower().split())
desc_words = set(description.split())
overlap = query_words & desc_words
score += len(overlap) * 0.1
# 3. 正则模式匹配
patterns = self._extract_patterns(function)
for pattern in patterns:
if re.search(pattern, query, re.IGNORECASE):
score += 0.2
# 4. 历史成功率
stats = self.tool_stats.get(name, {})
success_rate = stats.get("success_rate", 0.5)
score *= (0.5 + success_rate * 0.5)
return score
def _extract_patterns(self, function: Dict) -> List[str]:
"""从描述中提取模式"""
description = function.get("description", "")
patterns = []
# 简单的模式提取逻辑
# 实际中可以使用更复杂的 NLP 技术
weather_keywords = ["天气", "温度", "weather", "temperature"]
search_keywords = ["搜索", "查找", "search", "find"]
for keyword in weather_keywords:
if keyword in description.lower():
patterns.append(r"天气|温度|weather")
for keyword in search_keywords:
if keyword in description.lower():
patterns.append(r"搜索|查找|search|find")
return patterns
def record_result(self, tool_name: str, success: bool):
"""记录工具调用结果"""
if tool_name not in self.tool_stats:
self.tool_stats[tool_name] = {
"calls": 0,
"successes": 0,
"success_rate": 0.5
}
stats = self.tool_stats[tool_name]
stats["calls"] += 1
if success:
stats["successes"] += 1
stats["success_rate"] = stats["successes"] / stats["calls"]

五、多工具协同模式#

5.1 并行调用#

class ParallelToolCaller:
"""并行工具调用"""
def __init__(self, caller):
self.caller = caller
self.executor = ThreadPoolExecutor(max_workers=10)
async def call_parallel(self, tool_calls: List[Dict]) -> List[Any]:
"""并行执行多个工具调用"""
futures = []
for call in tool_calls:
future = self.executor.submit(
self.caller._execute_tool,
call["name"],
call["arguments"]
)
futures.append(future)
results = [f.result() for f in futures]
return results
# 使用示例
async def process_complex_query(query: str):
"""处理需要多个工具的复杂查询"""
# 假设模型决定需要调用多个工具
tool_calls = [
{"name": "get_weather", "arguments": {"city": "北京"}},
{"name": "get_news", "arguments": {"topic": "科技", "limit": 5}},
{"name": "get_stock", "arguments": {"symbol": "AAPL"}}
]
# 并行执行
results = await parallel_caller.call_parallel(tool_calls)
# 整合结果
return {
"weather": results[0],
"news": results[1],
"stock": results[2]
}

5.2 工具链式调用#

flowchart LR A[用户请求] --> B[工具1: 搜索] B --> C[结果1] C --> D[工具2: 分析] D --> E[结果2] E --> F[工具3: 生成] F --> G[最终结果]
class ChainedToolCaller:
"""链式工具调用"""
def __init__(self, caller):
self.caller = caller
def execute_chain(self, query: str, chain: List[Dict]) -> Any:
"""执行工具链"""
context = {"query": query, "results": []}
for step in chain:
# 准备参数(可以使用前一步的结果)
arguments = self._prepare_arguments(step, context)
# 执行工具
result = self.caller._execute_tool(
step["tool"],
arguments
)
context["results"].append({
"tool": step["tool"],
"arguments": arguments,
"result": result
})
return context
def _prepare_arguments(self, step: Dict, context: Dict) -> Dict:
"""准备工具参数,支持引用前序结果"""
arguments = {}
arg_config = step.get("arguments", {})
for key, value in arg_config.items():
if isinstance(value, str) and value.startswith("$"):
# 引用前序结果
ref_path = value[1:] # 去掉 $
value = self._resolve_reference(ref_path, context)
arguments[key] = value
return arguments
def _resolve_reference(self, path: str, context: Dict) -> Any:
"""解析引用路径"""
# 支持格式:$results[0].result.data
parts = path.split(".")
value = context
for part in parts:
if "[" in part:
# 数组索引
key = part.split("[")[0]
index = int(part.split("[")[1].rstrip("]"))
value = value[key][index]
else:
value = value[part]
return value
# 使用示例
chain = [
{
"tool": "web_search",
"arguments": {"query": "$query"}
},
{
"tool": "extract_content",
"arguments": {"url": "$results[0].result.url"}
},
{
"tool": "summarize",
"arguments": {"content": "$results[1].result.content"}
}
]
result = chain_caller.execute_chain("AI最新进展", chain)

5.3 条件分支#

class ConditionalToolCaller:
"""条件分支工具调用"""
def execute_with_conditions(self, query: str, rules: List[Dict]) -> Any:
"""根据条件选择执行路径"""
for rule in rules:
if self._evaluate_condition(rule["condition"], query):
return self._execute_action(rule["action"])
return None
def _evaluate_condition(self, condition: str, query: str) -> bool:
"""评估条件"""
conditions = {
"contains_weather": lambda q: any(
word in q for word in ["天气", "温度", "weather"]
),
"contains_search": lambda q: any(
word in q for word in ["搜索", "查找", "search"]
),
"is_question": lambda q: "?" in q or "?" in q
}
evaluator = conditions.get(condition, lambda q: False)
return evaluator(query)
# 规则配置
rules = [
{
"condition": "contains_weather",
"action": {"tool": "get_weather", "arguments": {"city": "北京"}}
},
{
"condition": "contains_search",
"action": {"tool": "web_search", "arguments": {"query": "$query"}}
},
{
"condition": "is_question",
"action": {"tool": "answer_question", "arguments": {"question": "$query"}}
}
]

六、常见工具类型与实现#

6.1 搜索工具#

def web_search_tool(query: str, limit: int = 5) -> list:
"""网络搜索工具"""
tools_schema = {
"type": "function",
"function": {
"name": "web_search",
"description": "搜索互联网获取信息",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"limit": {
"type": "integer",
"description": "返回结果数量",
"default": 5
}
},
"required": ["query"]
}
}
}
# 实现搜索逻辑
# 可以使用 SerperAPI、Bing Search API 等
import requests
API_KEY = "your-api-key"
url = "https://api.serper.dev/search"
response = requests.post(
url,
headers={"X-API-KEY": API_KEY},
json={"q": query, "num": limit}
)
results = response.json().get("organic", [])
return [
{
"title": r["title"],
"link": r["link"],
"snippet": r.get("snippet", "")
}
for r in results[:limit]
]

6.2 数据库工具#

import sqlite3
from typing import List, Dict
class DatabaseTool:
"""数据库查询工具"""
def __init__(self, db_path: str):
self.conn = sqlite3.connect(db_path)
self.conn.row_factory = sqlite3.Row
def get_schema(self) -> dict:
"""返回工具 Schema"""
return {
"type": "function",
"function": {
"name": "query_database",
"description": "执行 SQL 查询获取数据库信息",
"parameters": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "SQL 查询语句(仅支持 SELECT)"
}
},
"required": ["sql"]
}
}
}
def execute(self, sql: str) -> List[Dict]:
"""执行查询"""
# 安全检查:只允许 SELECT
if not sql.strip().upper().startswith("SELECT"):
return {"error": "仅支持 SELECT 查询"}
try:
cursor = self.conn.execute(sql)
rows = cursor.fetchall()
return [dict(row) for row in rows]
except Exception as e:
return {"error": str(e)}
# 使用示例
db_tool = DatabaseTool("sales.db")
# 注册到 Agent
caller.register_tool(
schema=db_tool.get_schema(),
function=db_tool.execute
)

6.3 API 调用工具#

import requests
from typing import Dict, Any
class APICallTool:
"""通用 API 调用工具"""
def __init__(self, base_url: str, headers: Dict = None):
self.base_url = base_url.rstrip("/")
self.headers = headers or {}
def get_schema(self) -> dict:
return {
"type": "function",
"function": {
"name": "api_call",
"description": f"调用 {self.base_url} 的 API 接口",
"parameters": {
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"],
"default": "GET"
},
"endpoint": {
"type": "string",
"description": "API 端点路径"
},
"params": {
"type": "object",
"description": "查询参数"
},
"body": {
"type": "object",
"description": "请求体(POST/PUT)"
}
},
"required": ["endpoint"]
}
}
}
def execute(self, method: str = "GET", endpoint: str = "",
params: Dict = None, body: Dict = None) -> Any:
url = f"{self.base_url}/{endpoint.lstrip('/')}"
try:
response = requests.request(
method=method,
url=url,
headers=self.headers,
params=params,
json=body
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": str(e)}

6.4 代码执行工具#

import subprocess
import tempfile
import os
class CodeExecutionTool:
"""代码执行工具"""
def get_schema(self) -> dict:
return {
"type": "function",
"function": {
"name": "execute_code",
"description": "执行 Python 代码并返回结果",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "要执行的 Python 代码"
},
"timeout": {
"type": "integer",
"description": "执行超时时间(秒)",
"default": 30
}
},
"required": ["code"]
}
}
}
def execute(self, code: str, timeout: int = 30) -> dict:
"""安全执行代码"""
# 使用临时文件执行
with tempfile.NamedTemporaryFile(
mode='w', suffix='.py', delete=False
) as f:
f.write(code)
temp_file = f.name
try:
result = subprocess.run(
['python', temp_file],
capture_output=True,
text=True,
timeout=timeout
)
return {
"success": result.returncode == 0,
"stdout": result.stdout,
"stderr": result.stderr
}
except subprocess.TimeoutExpired:
return {
"success": False,
"error": f"执行超时({timeout}秒)"
}
finally:
os.unlink(temp_file)

七、工具调用最佳实践#

7.1 错误处理#

class RobustToolCaller:
"""健壮的工具调用"""
def execute_with_retry(self, tool_name: str, arguments: dict,
max_retries: int = 3) -> dict:
last_error = None
for attempt in range(max_retries):
try:
result = self._execute_tool(tool_name, arguments)
if isinstance(result, dict) and "error" in result:
last_error = result["error"]
# 尝试修复参数
fixed_args = self._try_fix_arguments(
tool_name, arguments, last_error
)
if fixed_args != arguments:
arguments = fixed_args
continue
return {"success": True, "result": result}
except Exception as e:
last_error = str(e)
time.sleep(2 ** attempt) # 指数退避
return {
"success": False,
"error": last_error,
"suggestion": self._suggest_alternative(tool_name, arguments)
}
def _try_fix_arguments(self, tool_name: str, arguments: dict,
error: str) -> dict:
"""尝试修复参数"""
# 使用 LLM 分析错误并建议修复
prompt = f"""
工具调用失败,请分析错误并建议修复:
工具:{tool_name}
参数:{json.dumps(arguments)}
错误:{error}
请返回修复后的参数(JSON格式)。
"""
fixed = self.llm.generate(prompt)
return json.loads(fixed)

7.2 安全控制#

class SecureToolManager:
"""安全的工具管理"""
def __init__(self):
self.tools = {}
self.permissions = {} # 工具权限配置
self.audit_log = [] # 审计日志
def register_tool(self, name: str, function: callable,
permission_level: str = "normal"):
"""注册工具并设置权限级别"""
self.tools[name] = function
self.permissions[name] = permission_level
def execute(self, tool_name: str, arguments: dict,
user_permission: str = "normal") -> any:
"""执行工具(带权限检查)"""
# 1. 权限检查
required = self.permissions.get(tool_name, "normal")
if not self._check_permission(user_permission, required):
self._log(tool_name, arguments, "PERMISSION_DENIED")
raise PermissionError(
f"权限不足:需要 {required} 级别"
)
# 2. 参数校验
validated_args = self._validate_arguments(tool_name, arguments)
# 3. 执行并记录
try:
result = self.tools[tool_name](**validated_args)
self._log(tool_name, validated_args, "SUCCESS", result)
return result
except Exception as e:
self._log(tool_name, validated_args, "ERROR", str(e))
raise
def _check_permission(self, user_level: str, required: str) -> bool:
"""检查权限"""
levels = ["guest", "normal", "admin", "super"]
return levels.index(user_level) >= levels.index(required)
def _validate_arguments(self, tool_name: str, arguments: dict) -> dict:
"""校验和清理参数"""
# 实现 SQL 注入防护、XSS 过滤等
validated = {}
for key, value in arguments.items():
if isinstance(value, str):
# 清理危险字符
value = self._sanitize_string(value)
validated[key] = value
return validated
def _log(self, tool_name: str, arguments: dict,
status: str, result: any = None):
"""记录审计日志"""
self.audit_log.append({
"timestamp": datetime.now().isoformat(),
"tool": tool_name,
"arguments": arguments,
"status": status,
"result": str(result)[:500] # 截断
})

常见问题 FAQ#

Q1:如何决定何时使用工具调用?

A:考虑以下因素:

  • 任务是否需要外部数据?(如搜索、数据库)
  • 是否需要执行实际操作?(如发送邮件、创建文件)
  • 纯文本推理是否足够?
  • 工具调用的收益是否大于成本?

Q2:工具数量有限制吗?

A:

  • OpenAI:建议不超过 128 个工具
  • Anthropic:建议不超过 64 个工具
  • 工具过多会增加选择难度,建议按场景分组

Q3:如何处理工具调用失败?

A:

  1. 重试机制:自动重试 2-3 次
  2. 参数修复:使用 LLM 分析错误并修正
  3. 降级策略:尝试替代工具或方法
  4. 用户通知:明确告知失败原因

Q4:如何提高工具调用的准确性?

A:

  • 优化工具描述,提供详细的使用场景
  • 使用枚举限制参数值
  • 提供示例调用
  • 记录成功案例用于 Few-shot

Q5:工具调用有安全风险吗?

A:主要风险包括:

  • 恶意参数注入
  • 未授权的敏感操作
  • 数据泄露

建议措施:

  • 实施权限控制
  • 参数校验和清理
  • 操作审计日志
  • 敏感操作需人工确认

小结#

工具调用是 Agent 的核心能力,让 Agent 从「会说」变成「会做」。

核心要点回顾:

┌─────────────────────────────────────────────────────────────┐
│ 工具调用核心总结 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 核心原理:LLM 生成结构化参数 → 执行函数 → 返回结果 │
│ │
│ Schema 设计:清晰命名、详细描述、合理参数、类型安全 │
│ │
│ 选择策略:语义匹配、历史成功率、用户权限 │
│ │
│ 协同模式:并行调用、链式调用、条件分支 │
│ │
│ 最佳实践:错误处理、安全控制、审计日志 │
│ │
└─────────────────────────────────────────────────────────────┘

下一步学习:


下篇预告#

《Agent 多智能体协作:从单体到群体智能》

深入解析:

  • 多 Agent 协作的必要性
  • 流水线、层级、对等三种协作模式
  • CrewAI 多 Agent 框架实战
  • Agent 间通信与协调

参考资料#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

Agent 工具调用:Function Calling 与 Tool Use 实战
https://blog.souloss.com/posts/machine-learning/agent-guide/tool-use/
作者
Souloss
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时