GPT-5 Function Calling 实战:我踩过的 8 个坑和解决方案
GPT-5 的 Function Calling 功能比前代强了不少,但实际用起来还是有不少坑。
这篇文章记录我在生产环境中踩过的 8 个坑,以及对应的解决方案。
坑 1:函数描述不够清晰
问题
{
"name": "get_user",
"description": "获取用户",
"parameters": {
"type": "object",
"properties": {
"id": { "type": "string" }
}
}
}GPT-5 经常调用错误,或者传错参数。
解决方案
{
"name": "get_user_by_id",
"description": "根据用户 ID 获取用户详细信息,包括姓名、邮箱、注册时间等。如果用户不存在会返回 null。",
"parameters": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "用户的唯一标识符,格式为 UUID,例如:550e8400-e29b-41d4-a716-446655440000"
}
},
"required": ["id"]
}
}关键点:
- 函数名要有描述性
- 描述要说明返回什么
- 参数要有示例值
坑 2:参数类型不明确
问题
{
"name": "search_products",
"parameters": {
"type": "object",
"properties": {
"query": { "type": "string" },
"limit": { "type": "number" }
}
}
}GPT-5 有时传字符串 "10" 而不是数字 10。
解决方案
{
"name": "search_products",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,至少 2 个字符"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20,
"description": "返回结果数量,1-100 之间的整数"
},
"sort_by": {
"type": "string",
"enum": ["relevance", "price_asc", "price_desc", "newest"],
"description": "排序方式"
}
},
"required": ["query"]
}
}关键点:
- 使用 integer 而不是 number
- 添加 minimum/maximum 约束
- 使用 enum 限制可选值
坑 3:没有处理函数调用失败
问题
response = client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": "查询用户 123 的信息"}],
tools=[get_user_tool]
)
# 直接执行函数,没有错误处理
result = execute_function(response.choices[0].message.tool_calls[0])解决方案
def handle_tool_calls(tool_calls, available_functions):
results = []
for tool_call in tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
if function_name not in available_functions:
results.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps({
"error": f"Unknown function: {function_name}"
})
})
continue
try:
function = available_functions[function_name]
result = function(**function_args)
results.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps(result)
})
except Exception as e:
results.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps({
"error": str(e),
"error_type": type(e).__name__
})
})
return results坑 4:函数调用循环
问题
GPT-5 有时会陷入函数调用循环,反复调用同一个函数。
解决方案
def chat_with_loop_detection(messages, tools, max_iterations=5):
iteration = 0
call_history = []
while iteration < max_iterations:
response = client.chat.completions.create(
model="gpt-5",
messages=messages,
tools=tools
)
if not response.choices[0].message.tool_calls:
return response.choices[0].message.content
for tool_call in response.choices[0].message.tool_calls:
call_key = f"{tool_call.function.name}:{tool_call.function.arguments}"
if call_key in call_history:
# 检测到循环,强制结束
messages.append({
"role": "system",
"content": "你已经获取了足够的信息,请直接回答用户的问题。"
})
break
call_history.append(call_key)
iteration += 1
return "抱歉,我无法完成这个请求。"坑 5:并行函数调用处理不当
问题
GPT-5 可能一次返回多个函数调用,但代码只处理了第一个。
解决方案
import asyncio
async def handle_parallel_tool_calls(tool_calls, available_functions):
tasks = []
for tool_call in tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
if function_name in available_functions:
task = asyncio.create_task(
execute_function_async(
tool_call.id,
available_functions[function_name],
function_args
)
)
tasks.append(task)
results = await asyncio.gather(*tasks, return_exceptions=True)
formatted_results = []
for result in results:
if isinstance(result, Exception):
formatted_results.append({
"role": "tool",
"content": json.dumps({"error": str(result)})
})
else:
formatted_results.append(result)
return formatted_results坑 6:没有验证函数参数
问题
GPT-5 传的参数可能不符合预期,直接执行会出错。
解决方案
from pydantic import BaseModel, ValidationError
class GetUserParams(BaseModel):
id: str
include_details: bool = False
def safe_execute_function(function_name, arguments, available_functions):
# 定义每个函数的参数模型
param_models = {
"get_user": GetUserParams,
# ...
}
if function_name not in available_functions:
return {"error": f"Unknown function: {function_name}"}
try:
# 验证参数
if function_name in param_models:
validated_args = param_models[function_name](**arguments)
arguments = validated_args.model_dump()
# 执行函数
result = available_functions[function_name](**arguments)
return result
except ValidationError as e:
return {
"error": "Invalid parameters",
"details": e.errors()
}
except Exception as e:
return {"error": str(e)}坑 7:Token 消耗过高
问题
函数定义太长,每次调用都消耗大量 Token。
解决方案
# 优化前:冗长的描述
verbose_tool = {
"name": "search",
"description": "这个函数用于搜索数据库中的产品信息,它支持多种搜索方式,包括按名称搜索、按分类搜索、按价格范围搜索等等...",
}
# 优化后:精简的描述
concise_tool = {
"name": "search_products",
"description": "搜索产品。支持 name/category/price_range 过滤。",
}Token 优化技巧:
1. 描述控制在 50 字以内
2. 参数描述用关键词
3. 使用 enum 替代长描述
4. 按需加载工具(不是每次都传所有工具)
坑 8:没有缓存函数结果
问题
同一个会话中,GPT-5 可能多次请求相同的数据。
解决方案
from functools import lru_cache
import hashlib
class FunctionCallCache:
def __init__(self, ttl=300):
self.cache = {}
self.ttl = ttl
def get_cache_key(self, function_name, arguments):
args_str = json.dumps(arguments, sort_keys=True)
return hashlib.md5(f"{function_name}:{args_str}".encode()).hexdigest()
def get(self, function_name, arguments):
key = self.get_cache_key(function_name, arguments)
if key in self.cache:
result, timestamp = self.cache[key]
if time.time() - timestamp < self.ttl:
return result
del self.cache[key]
return None
def set(self, function_name, arguments, result):
key = self.get_cache_key(function_name, arguments)
self.cache[key] = (result, time.time())
# 使用示例
cache = FunctionCallCache(ttl=300)
def cached_function_call(function_name, arguments):
# 先查缓存
cached = cache.get(function_name, arguments)
if cached is not None:
return cached
# 执行函数
result = execute_function(function_name, arguments)
# 存入缓存
cache.set(function_name, arguments, result)
return result完整的生产级实现
class GPT5FunctionCaller:
def __init__(self, client, tools, available_functions):
self.client = client
self.tools = tools
self.available_functions = available_functions
self.cache = FunctionCallCache()
self.max_iterations = 5
async def call(self, messages):
iteration = 0
call_history = set()
while iteration < self.max_iterations:
response = await self.client.chat.completions.create(
model="gpt-5",
messages=messages,
tools=self.tools
)
message = response.choices[0].message
if not message.tool_calls:
return message.content
messages.append(message)
results = await self._handle_tool_calls(
message.tool_calls, call_history
)
messages.extend(results)
iteration += 1
return "处理超时,请简化问题后重试。"
async def _handle_tool_calls(self, tool_calls, call_history):
results = []
for tool_call in tool_calls:
call_key = f"{tool_call.function.name}:{tool_call.function.arguments}"
if call_key in call_history:
results.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps({"error": "Duplicate call detected"})
})
continue
call_history.add(call_key)
result = await self._execute_tool_call(tool_call)
results.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps(result)
})
return results
async def _execute_tool_call(self, tool_call):
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# 检查缓存
cached = self.cache.get(function_name, arguments)
if cached is not None:
return cached
# 执行函数
if function_name not in self.available_functions:
return {"error": f"Unknown function: {function_name}"}
try:
result = await self.available_functions[function_name](**arguments)
self.cache.set(function_name, arguments, result)
return result
except Exception as e:
return {"error": str(e)}总结
GPT-5 Function Calling 的 8 个坑:
1. 函数描述不清晰 → 详细描述 + 示例值
2. 参数类型不明确 → 使用 integer + enum + 约束
3. 没有处理失败 → 统一错误处理
4. 函数调用循环 → 循环检测 + 强制结束
5. 并行调用处理不当 → asyncio.gather
6. 没有验证参数 → Pydantic 验证
7. Token 消耗过高 → 精简描述 + 按需加载
8. 没有缓存结果 → 函数调用缓存
实践时间:2026年7月
调用次数:50,000+ 次
节省成本:约 $2,000/月
#GPT5 #FunctionCalling #OpenAI #最佳实践
读者评论 5