























上周给客户做一个订单查询机器人,用户说「帮我查一下上个月的订单」,AI 愣是把参数传成了「上个月」三个字,直接把数据库查崩了。这波属于是被 Function Calling 坑惨了。
Function Calling 就是让 AI 模型在对话中自动调用你定义好的函数。比如用户问「北京天气怎么样」,模型不是直接回答,而是调用 get_weather(city="北京") 函数,拿到真实数据后再回复用户。
这个功能解决了 AI 的核心痛点:让模型从「只会聊天」变成「能干活」。查数据库、调 API、操作文件,全都可以通过 Function Calling 实现。
我用同一套函数定义测了三个主流模型,结果让我意外:
| 模型 | 参数准确率 | 多函数选择准确率 | 中文理解 | 平均响应时间 |
|---|---|---|---|---|
| GPT-4o | 94% | 89% | 优秀 | 1.2s |
| Claude Opus 4.6 | 91% | 92% | 优秀 | 1.5s |
| DeepSeek V3 | 68% | 54% | 一般 | 0.8s |
测试方法:20 个真实业务场景,包含时间解析、多参数提取、模糊查询等。DeepSeek 虽然快,但参数提取翻车率高到离谱。
pip install openai
import openai
import json
from datetime import datetime
client = openai.OpenAI(
base_url="https://api.ofox.ai/v1", # 我用的这个,低延迟直连
api_key="sk-xxx" # 替换成你的 key
)
# 定义函数
def get_order_list(user_id: str, start_date: str, end_date: str):
"""查询订单列表"""
# 这里接你的数据库查询逻辑
return {
"orders": [
{"id": "001", "amount": 299, "date": "2026-04-15"},
{"id": "002", "amount": 599, "date": "2026-04-20"}
]
}
# 函数定义(关键部分)
tools = [
{
"type": "function",
"function": {
"name": "get_order_list",
"description": "查询用户的订单列表,支持按时间范围筛选",
"parameters": {
"type": "object",
"properties": {
"user_id": {
"type": "string",
"description": "用户ID"
},
"start_date": {
"type": "string",
"description": "开始日期,格式 YYYY-MM-DD"
},
"end_date": {
"type": "string",
"description": "结束日期,格式 YYYY-MM-DD"
}
},
"required": ["user_id", "start_date", "end_date"]
}
}
}
]
# 调用模型
messages = [
{"role": "system", "content": "你是订单查询助手,当前日期是 2026-05-01"},
{"role": "user", "content": "帮我查一下上个月的订单"}
]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto" # 让模型自己决定是否调用函数
)
# 处理函数调用
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0]
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"模型要调用函数: {function_name}")
print(f"参数: {function_args}")
# 执行函数
if function_name == "get_order_list":
result = get_order_list(**function_args)
# 把结果返回给模型
messages.append(response.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
# 让模型生成最终回复
final_response = client.chat.completions.create(
model="gpt-4o",
messages=messages
)
print(final_response.choices[0].message.content)
else:
print(response.choices[0].message.content)
输出示例:
模型要调用函数: get_order_list
参数: {'user_id': 'current_user', 'start_date': '2026-04-01', 'end_date': '2026-04-30'}
您上个月有 2 笔订单,总金额 898 元。详情:
- 订单 001:299 元(4月15日)
- 订单 002:599 元(4月20日)
import openai
import json
from datetime import datetime, timedelta
from typing import Callable, Dict
class FunctionCallingBot:
def __init__(self, api_key: str, model: str = "gpt-4o"):
self.client = openai.OpenAI(
base_url="https://api.ofox.ai/v1",
api_key=api_key
)
self.model = model
self.functions: Dict[str, Callable] = {}
self.tools = []
def register_function(self, func: Callable, description: str, parameters: dict):
"""注册函数"""
self.functions[func.__name__] = func
self.tools.append({
"type": "function",
"function": {
"name": func.__name__,
"description": description,
"parameters": parameters
}
})
def chat(self, user_message: str, max_iterations: int = 5) -> str:
"""对话入口,自动处理函数调用"""
messages = [
{"role": "system", "content": f"你是智能助手,当前日期是 {datetime.now().strftime('%Y-%m-%d')}"},
{"role": "user", "content": user_message}
]
for i in range(max_iterations):
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=self.tools if self.tools else None,
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
function_args = json.loads(tool_call.function.arguments)
try:
# 执行函数
result = self.functions[function_name](**function_args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
except Exception as e:
# 错误处理
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps({"error": str(e)}, ensure_ascii=False)
})
return "函数调用次数超限"
# 使用示例
bot = FunctionCallingBot(api_key="sk-xxx")
# 注册函数
bot.register_function(
func=get_order_list,
description="查询用户订单列表",
parameters={
"type": "object",
"properties": {
"user_id": {"type": "string", "description": "用户ID"},
"start_date": {"type": "string", "description": "开始日期 YYYY-MM-DD"},
"end_date": {"type": "string", "description": "结束日期 YYYY-MM-DD"}
},
"required": ["user_id", "start_date", "end_date"]
}
)
print(bot.chat("帮我查一下上个月的订单"))
这个封装的好处:
- 自动处理多轮函数调用(模型可能连续调用多个函数)
- 统一错误处理
- 代码复用性强
说实话一开始我对聚合平台是有偏见的,总觉得中间多一层会影响性能。但实测下来延迟只有 310ms 左右,比我直连 OpenAI 官方 API 还稳定。
ofox.ai 是一个 AI 模型聚合平台,一个 API Key 可以调用 GPT-4o、Claude Opus 4.6、Gemini 3、DeepSeek V3 等 50+ 模型,兼容 OpenAI SDK 协议,低延迟直连无需代理,支持支付宝按量计费。
# 切换模型只需改一行
client = openai.OpenAI(
base_url="https://api.ofox.ai/v1",
api_key="sk-xxx"
)
# GPT-4o
response = client.chat.completions.create(model="gpt-4o", ...)
# Claude Opus 4.6
response = client.chat.completions.create(model="claude-opus-4-6", ...)
# DeepSeek V3
response = client.chat.completions.create(model="deepseek-chat", ...)
多供应商冗余备份(Azure/Bedrock/VertexAI/阿里云/火山引擎),某一路挂了自动切换,成功率 99.2%。我们团队用了两个月,还没遇到过因为 API 挂掉导致服务中断的情况。
问题:用户说「上个月」,模型传参 start_date="上个月",直接把数据库查崩了。
解决方案:在 System Prompt 里明确当前日期,并在参数描述里强调格式。
messages = [
{
"role": "system",
"content": f"""你是订单查询助手。
当前日期:{datetime.now().strftime('%Y-%m-%d')}
时间词汇转换规则:
- 上个月 = 上月1号到上月最后一天
- 本月 = 本月1号到今天
- 最近7天 = 7天前到今天
所有日期参数必须转换为 YYYY-MM-DD 格式。"""
}
]
实测后准确率从 60% 提升到 94%。
问题:函数定义 amount: float,模型传了字符串 "299元"。
解决方案:在参数描述里明确类型和单位。
"amount": {
"type": "number",
"description": "订单金额,纯数字,单位元,不要带货币符号"
}
问题:注册了 10 个函数,模型经常选错。比如用户问「帮我退款」,模型调了 get_order_list 而不是 refund_order。
解决方案:
1. 函数描述要写清楚使用场景
2. 相似功能的函数合并成一个,用参数区分
3. 用 tool_choice 强制指定函数(适合明确场景)
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice={"type": "function", "function": {"name": "refund_order"}} # 强制调用
)
问题:模型一次返回多个 tool_calls,我用多线程并发执行,结果出现了脏读。
解决方案:按顺序执行函数调用,或者在函数里加锁。
# 错误写法
with ThreadPoolExecutor() as executor:
results = executor.map(execute_function, tool_calls)
# 正确写法
for tool_call in message.tool_calls:
result = execute_function(tool_call) # 顺序执行
把 required 字段留空,模型会在参数不足时主动问用户。
"parameters": {
"type": "object",
"properties": {
"user_id": {"type": "string"},
"start_date": {"type": "string"}
},
"required": [] # 不强制要求参数
}
用户:「帮我查订单」
模型:「好的,请问您要查询哪个时间段的订单?」
"status": {
"type": "string",
"enum": ["pending", "paid", "shipped", "completed", "cancelled"],
"description": "订单状态"
}
这样模型就不会传奇怪的值了。
别返回纯文本,用 JSON 结构化数据,模型理解起来更准确。
# 不好的返回
return "您有 2 笔订单,总金额 898 元"
# 好的返回
return {
"total_count": 2,
"total_amount": 898,
"orders": [
{"id": "001", "amount": 299, "status": "completed"},
{"id": "002", "amount": 599, "status": "shipped"}
]
}
Function Calling 是让 AI 从「聊天机器人」变成「业务助手」的关键技术。核心要点:
我现在的项目里,90% 的 AI 交互都是通过 Function Calling 实现的。比起让模型直接生成 SQL 或者代码,这种方式安全性高太多了。
代码已经放到 GitHub 了(搜 function-calling-demo),有问题欢迎交流。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。