让大模型真正“动手”:用 Function Calling 接入外部 API 的完整实战(附可运行代
让大模型真正“动手”:用 Function Calling 接入外部 API 的完整实战(附可运行代码)
大模型只会“说话”在真实业务里是不够的。用户问“北京今天适合跑步吗”,模型既不知道实时天气,也不懂你库存里还剩几台机器。2026 年落地的 AI 应用,几乎都有一个共同点:让模型在需要时主动去调你的代码、查你的数据库、打你的接口。这条能力的入口,就是 Function Calling(函数调用)。本文用 OpenAI 兼容接口,从定义工具到自动调度外部 API,给你一份能直接跑通的实战代码,不堆概念,只看怎么落地。

一、为什么模型自己答不了,非得“调工具”
模型的知识有截止日期,而且它进不了你的私有系统。天气、订单、物流、内部知识库——这些信息既实时又在墙内。Function Calling 的本质,是让模型根据对话自己判断:这件事我该不该调用某个外部函数,以及该传什么参数。模型不负责执行函数,它只负责“决定调谁、传什么”,真正的执行和结果回填在你这边的代码里。这样既守住了安全边界,又把模型从“纯文本生成器”变成了“业务调度入口”。
二、第一步:把工具定义成一份 Schema
模型看不懂你的 Python 函数,它只认 JSON Schema。你要告诉它:有哪些函数可用、每个函数干什么、需要哪些参数。下面用“查天气”举例,把一份工具描述写出来:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市当前天气,返回温度、天气状况和风力",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名,例如 北京、上海"
}
},
"required": ["city"]
}
}
}]
关键点:description 写得越清楚,模型选对工具、填对参数的概率越高。模糊的描述只会换来瞎猜的参数。
三、第二步:让模型决定“调哪个、传什么”
把工具列表随消息一起发给模型,模型会在回复里给出 tool_calls——也就是它想调用的函数名和参数。这一步不需要你写任何判断逻辑:
from openai import OpenAI
# 接任意 OpenAI 兼容服务:vLLM / Ollama / 云端都行
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
messages = [{"role": "user", "content": "北京今天适合出门跑步吗?"}]
resp = client.chat.completions.create(
model="qwen2.5-7b",
messages=messages,
tools=tools,
tool_choice="auto" # auto = 让模型自己决定要不要调
)
msg = resp.choices[0].message
tool_choice="auto" 表示“能调就调、不能调就直接回答”。如果你强制必须调某个函数,可以改成 {"type": "function", "function": {"name": "get_weather"}}。
四、第三步:本地执行并把结果“喂回去”
模型只给出了调用意图,真正执行在你的代码里。执行完,要把结果以 role: "tool" 的消息回填,模型才能基于真实数据作答:
import json
def get_weather(city: str) -> str:
# 这里可换成真实天气 API;示例返回模拟数据,保证离线可跑
return json.dumps({"city": city, "temp": 24, "cond": "晴", "wind": "3级"})
if msg.tool_calls:
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_weather(**args)
messages.append(msg) # 先回写模型的调用意图
messages.append({ # 再回填执行结果
"role": "tool",
"tool_call_id": call.id,
"content": result
})
# 模型基于真实天气数据生成最终回答
final = client.chat.completions.create(model="qwen2.5-7b", messages=messages)
print(final.choices[0].message.content)
这就是一个最小可跑闭环:用户提问 → 模型决定调 get_weather("北京") → 你的代码执行 → 结果回填 → 模型结合数据回答“24 度、晴、3 级风,适合跑步”。把 get_weather 换成查订单、查库存、发短信,逻辑一模一样。
五、工程落地必须踩平的几个坑
真上生产,裸奔代码会出问题。结合我们线上踩过的雷,给你几条硬经验:
- 参数先校验再执行:模型偶尔会漏填
required字段或类型错,执行前用 pydantic 或手写校验兜底,别直接**args进业务函数。 - 给外部调用加超时:天气、库存接口可能挂,务必
requests设timeout,否则一次慢响应拖垮整轮对话。 - 结果要可序列化:回填给模型的
content必须是字符串(JSON 字符串即可),别直接塞 Python 对象,否则第二轮请求直接报错。 - 权限与副作用:凡是会“改数据”的工具(下单、删记录),要么加二次确认,要么只允许只读,别让模型一句话把库改了。

总结
Function Calling 不是花活,它是 Agent 的基石:多工具并行调用、失败重试、把结果拼回上下文,全部建立在这套“模型决策 + 代码执行”的闭环上。今天的代码已经能跑通单工具调用,下一步你可以加并行调用、调用失败自动重试、以及把多个工具编排成一条流水线。先把这个最小闭环跑顺,再谈更复杂的智能体,才不会一上来就被各种边界情况淹没。
更多推荐
所有评论(0)