为什么 AI 需要工具调用能力
大语言模型虽然强大,但存在天然的局限性:它们无法获取实时信息、不能执行代码、无法操作外部系统。Function Calling(函数调用)正是为解决这些问题而生——它让 AI 能够像人类使用工具一样,自主选择并调用外部 API 和函数,从而突破模型的边界。
想象一个场景:用户问「今天北京天气怎么样?」。纯文本模型无法回答,因为它的训练数据截止于某个日期。但具备了 Function Calling 能力的 AI 会这样做:识别出需要调用天气 API → 构造正确的 API 请求 → 解析返回的 JSON 数据 → 用自然语言回复用户。这就是工具调用循环的魅力。
Function Calling 的工作原理
Function Calling 不是 AI 真的去调用函数,而是一个精心设计的协议:
- 定义工具:开发者向模型描述可用的函数(名称、参数、用途)
- 模型决策:AI 分析用户输入,决定是否需要调用工具、调用哪个工具、传什么参数
- 开发者执行:开发者代码执行实际的函数调用
- 结果反馈:将执行结果返回给模型,模型据此生成最终回复
这个协议的精妙之处在于:AI 负责「决定做什么」,但实际执行权始终在开发者手中,保证了安全性和可控性。
技能链拆解:工具编排链
我们以「工具编排链」技能链为例,拆解每个节点在工具调用流程中的作用:
节点 1:需求理解(sp-188)——AI 首先需要准确理解用户的意图。用户说「帮我查一下最近的航班」,AI 需要识别出这是一个需要调用外部工具的任务,而非简单的文本问答。
节点 2:工具选择(sp-179)——从可用工具集中选择最合适的工具。如果用户要查航班,应该调用航班查询 API 而非天气 API。这个节点需要 AI 具备「工具匹配」的能力。
节点 3:工具调用(sp-181)——构造正确的参数并执行调用。AI 需要将用户模糊的需求(如「最近的航班」)转化为精确的 API 参数(如 date=2026-07-13, from=北京, to=上海)。
节点 4:结果解析(sp-187)——将 API 返回的结构化数据转化为用户友好的自然语言回复。一个航班查询可能返回几十条 JSON 数据,但用户只需要最相关的 2-3 个选项。
实战:构建一个多工具 Agent
以下代码展示了一个具备多个工具的 Agent Loop 实现:
import json
from openai import OpenAI
import requests
client = OpenAI(
api_key="your-deepseek-api-key",
base_url="https://api.deepseek.com"
)
# 定义可用工具
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的实时天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称,如北京"}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "search_web",
"description": "在互联网上搜索最新信息",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"},
"num_results": {"type": "integer", "description": "返回结果数量"}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "执行数学计算",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "数学表达式"}
},
"required": ["expression"]
}
}
}
]
# 工具实现函数
def execute_tool(tool_name, arguments):
if tool_name == "get_weather":
city = arguments.get("city")
# 实际项目中调用天气 API
return f"{city}今天晴,气温 25°C,湿度 60%"
elif tool_name == "search_web":
query = arguments.get("query")
return f"关于'{query}'的搜索结果:找到 3 条相关信息..."
elif tool_name == "calculate":
expression = arguments.get("expression")
try:
result = eval(expression)
return f"计算结果:{expression} = {result}"
except:
return "计算表达式无效"
return "未知工具"
class ToolCallingAgent:
def __init__(self, max_iterations=5):
self.max_iterations = max_iterations
self.messages = []
def run(self, user_input):
self.messages = [{
"role": "system",
"content": "你是一个智能助手,可以使用工具来完成任务。请一步步思考并合理使用工具。"
}, {
"role": "user",
"content": user_input
}]
for i in range(self.max_iterations):
print(f"=== 第 {i+1} 轮 ===")
response = client.chat.completions.create(
model="deepseek-chat",
messages=self.messages,
tools=tools
)
msg = response.choices[0].message
# 如果模型决定直接回复
if msg.content and not msg.tool_calls:
return msg.content
# 如果模型决定调用工具
if msg.tool_calls:
self.messages.append(msg)
for tool_call in msg.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
print(f"调用工具:{func_name}({func_args})")
result = execute_tool(func_name, func_args)
self.messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
return "达到最大循环次数"
# 使用示例
agent = ToolCallingAgent(max_iterations=5)
result = agent.run("北京今天天气怎么样?另外帮我算一下 156 * 23 + 89")
print(f"最终回复:{result}")工具调用的最佳实践
工具描述要精准:模型的工具选择完全依赖于你的工具描述。描述越清晰,选择越准确。建议每个工具描述包含:功能说明、适用场景、参数详解、返回值格式。
参数校验不可少:模型可能生成不合理的参数。在 execute_tool 中始终进行参数校验,防止因参数错误导致工具调用失败。
错误要反馈给模型:当工具调用失败时,不要简单返回错误信息,而是将结构化的错误描述返回给模型,让它有机会调整策略。例如:「调用天气 API 失败:城市名'北惊'不存在,建议使用'北京'」。
控制工具数量:可用工具过多会增加模型的选择难度。建议将工具数量控制在 10 个以内,或将工具分组,根据上下文动态提供。
工具调用与 Agent Loop 的深度结合
工具调用不是一次性操作,而是融入 Agent Loop 的持续过程。一轮工具调用的结果可能触发下一轮的工具调用——比如先搜索「AI 领域最新论文」,然后根据搜索结果中的论文标题,再调用 PDF 解析工具获取论文详情。这种链式工具调用正是 Agent Loop 强大能力的体现。
想亲手编排这个技能链?
在技能链中打开 →