本文零框架、零第三方AI工具库依赖,基于阿里云通义千问 OpenAI 兼容接口,从底层原理、对话流转、原生参数解析、手写代码实现四个维度,完整拆解 Function Calling 核心能力,理清楚大模型工具调用的本质
所有内容均为原生实现,不依赖任何封装框架,全程可追溯、可调试、可自定义改造
大模型使用阿里云百炼的qwen-plus:help.aliyun.com/zh/model-st…
Function Calling(工具调用)的本质并不是大模型自动执行代码,而是 在给大模型问题的时候,将可能用到的工具信息(函数)一起全部发给大模型。大模型根据用户问题,判断是否需要调用对应的工具(函数),如需要则大模型结构化输出工具名称和入参,由本地代码(例如你自己的Python项目)完成工具执行、然后将Python执行结果回传给大模型,大模型集合工具输出和问题返回最终答案 的流程。
整个流程分为三段核心逻辑:
本文基于阿里云通义千问 OpenAI 兼容接口实现(接口地址:),适配标准 OpenAI 格式的 Function Calling 参数。
以「查询深圳天气」场景为例,完整拆解第一轮请求、第一轮响应、第二轮请求、最终响应的路数据流转。
tools、tool_choice 两个核心参数,结合基础对话参数,完整必填参数说明如下:qwen-plusauto 表示大模型自主判断是否调用工具、调用哪个工具

第一轮核心目的:告诉大模型用户需求 + 可用工具,让大模型输出标准化工具调用指令。此时大模型不会直接回答问题,只会返回工具调用参数
{
"model": "qwen-plus",
"messages": [
{
"role": "system",
"content": "你是一位天气预报专家,能够预测给定城市的天气情况"
},
{
"role": "user",
"content": "深圳今天的天气怎么样"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather_for_location",
"description": "Get weather for a given city",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "需要查询天气的城市名称"
}
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
大模型识别到「天气查询需要外部工具」,返回 tool_calls 字段,代表需要进行工具调用(对应需要执行Python方法拿到结果)
{
"model": "qwen-plus",
"id": "chatcmpl-0ba4eb7c-101b-9da5-ac3b-aa09a0bbb089",
"choices": [
{
"message": {
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_734c63b9a3d44470937769",
"type": "function",
"function": {
"name": "get_weather_for_location",
"arguments": "{"city": "深圳"}"
}
}
],
"role": "assistant"
},
"index": 0,
"finish_reason": "tool_calls"
}
],
"created": 1779518579,
"object": "chat.completion",
"usage": {
"total_tokens": 168,
"completion_tokens": 21,
"prompt_tokens": 147,
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
关键标识:finish_reason: "tool_calls",代表模型判定需要调用外部工具,终止文本生成

拿到第一轮响应后,本地代码需要完成 3 件事:
tool_calls,提取工具名称 get_weather_for_location 和入参 city=深圳第二轮核心目的:将「用户问题+模型工具调用指令+本地工具执行结果」完整回传给大模型,让大模型整合信息,输出自然语言最终回答。
重点:messages 必须完整拼接上下文,不可截断,需要包含 system、user、assistant(工具调用指令)、tool(工具结果) 四类消息
{
"model": "qwen-plus",
"messages": [
{
"role": "system",
"content": "你是一位天气预报专家,能够预测给定城市的天气情况"
},
{
"role": "user",
"content": "深圳今天的天气怎么样"
},
{
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_734c63b9a3d44470937769",
"type": "function",
"function": {
"name": "get_weather_for_location",
"arguments": "{"city": "深圳"}"
}
}
],
"role": "assistant"
},
{
"role": "tool",
"content": "多云转阴",
"tool_call_id": "call_734c63b9a3d44470937769"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather_for_location",
"description": "Get weather for a given city",
"parameters": {
"city": "string"
}
}
}
],
"tool_choice": "auto"
}

{
"model": "qwen-plus",
"id": "chatcmpl-9affd19c-da42-9516-a028-70ef17f90d17",
"choices": [
{
"message": {
"content": "深圳今天的天气是多云转阴。",
"role": "assistant"
},
"index": 0,
"finish_reason": "stop"
}
],
"created": 1779523575,
"object": "chat.completion",
"usage": {
"total_tokens": 195,
"completion_tokens": 9,
"prompt_tokens": 186,
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
恭喜,function calling的核心原理就是这样的交互方式
tools 结构体是固定的大模型工具格式inspect 库,无需任何第三方库inspect 是Python内置反射库,可实现对函数的逆向解析,获取三大核心信息,完美适配大模型工具参数格式:
我们自定义一套原生解析规则,将Python原生函数,标准化转为大模型可识别的 tools 结构体:
function.namefunction.descriptionparameters.propertiesrequired 必填列表本地原生Python工具函数:
def get_weather_for_location(city: str) -> str:
"""Get weather for a given city
Args:
city: 需要查询天气的城市名称
"""
# 模拟天气查询接口
weather_map = {"深圳": "多云转阴", "北京": "晴", "上海": "小雨"}
return weather_map.get(city, "未知天气")
通过inspect自动解析后,自动生成前文标准的tools结构体
基于上述原理,我们手写完整可运行代码,包含:函数自动解析、两轮对话流转、工具参数解析、本地函数执行、结果回传全流程,零框架依赖。
整体闭环流程:
结构化流程图:可以使用 www.jyshare.com/front-end/9… 在线的 Mermaid 渲染功能实现
graph TD
%% 样式定义:区分不同类型节点,适配博客可视化
classDef init fill:#e6f7ff,stroke:#1890ff,stroke-width:1px;
classDef parse fill:#f0f8ff,stroke:#40a9ff,stroke-width:1px;
classDef api fill:#fff7e6,stroke:#faad14,stroke-width:1px;
classDef local fill:#f6ffed,stroke:#52c41a,stroke-width:1px;
classDef endnode fill:#f0f2f5,stroke:#8c8c8c,stroke-width:1px; %% 流程节点
A[初始化全局配置]:::init --> B[定义本地Python工具函数]:::init
B --> C[inspect反射自动解析生成标准Tools参数]:::parse
C --> D[第一轮API请求传入用户问题+工具列表]:::api
D --> E{模型判断是否需要调用工具?} %% 分支流程
E -->|无需调用工具| F[模型直接生成自然语言答案]:::api
E -->|需要调用工具| G[解析Tool_Calls参数提取工具名+入参]:::local
G --> H[本地执行对应Python函数获取真实业务结果]:::local
H --> I[拼接完整对话上下文挂载Tool执行结果]:::local
I --> J[第二轮API请求回传全部对话数据]:::api
J --> K[模型整合数据输出最终回答]:::api
%% 流程收尾
F --> L[流程结束]:::endnode
K --> L

pip install openai 或 uv add openai,此处使用uv,还需要添加 dotenv 用于加载环境变量uv add openai dotenv
import os
import json
import inspect
from typing import Callable, Dict, Any
from openai import OpenAI# 加载环境变量(兼容本地.env配置)
from dotenv import load_dotenvload_dotenv()# 1. 初始化通义千问兼容客户端(无框架,原生请求)
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 2. 定义本地工具函数(可自定义扩展)
def get_weather_for_location(city: str) -> str:
"""Get weather for a given city
Args:
city: 需要查询天气的城市名称
"""
weather_map = {"深圳": "多云转阴", "北京": "晴", "上海": "小雨", "广州": "多云"}
return weather_map.get(city, "暂无该城市天气数据")
# 3. 核心工具:inspect自动解析函数,生成大模型标准tools格式
def parse_function_to_tool(func: Callable) -> Dict[str, Any]:
"""
原生解析Python函数,转为通义千问Function Calling标准tool结构
无任何框架依赖,纯内置inspect实现
"""
# 获取函数签名、文档、参数
sig = inspect.signature(func)
doc = inspect.getdoc(func) or ""
params = sig.parameters # 基础工具结构
tool = {
"type": "function",
"function": {
"name": func.__name__,
"description": doc.split("n")[0],
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
} # 类型映射:Python类型 -> 大模型参数类型
type_map = {str: "string", int: "integer", float: "number", bool: "boolean"} # 遍历解析每个参数
for param_name, param in params.items():
# 获取参数类型,默认string
param_type = type_map.get(param.annotation, "string")
# 获取参数描述(从docstring简易解析)
param_desc = ""
if f"{param_name}:" in doc:
param_desc = doc.split(f"{param_name}:")[-1].split("n")[0].strip() # 写入参数属性
tool["function"]["parameters"]["properties"][param_name] = {
"type": param_type,
"description": param_desc
} # 无默认值的参数为必填
if param.default is param.empty:
tool["function"]["parameters"]["required"].append(param_name) return tool
# 4. 工具调用执行器:【修复兼容问题】解析模型返回的tool_calls对象
def execute_tool_call(tool_calls: list, func_map: Dict[str, Callable]) -> str:
"""执行模型触发的工具调用,返回工具执行结果
兼容新版OpenAI SDK对象取值,杜绝下标报错
"""
for call in tool_calls:
# 修复:新版SDK返回实体对象,不支持字典下标取值,需用属性调用
func_name = call.function.name
func_args = json.loads(call.function.arguments)
# 匹配本地函数并执行
if func_name in func_map:
return func_map[func_name](**func_args)
return "工具调用失败,未匹配到本地函数"
# 5. 完整Function Calling闭环流程
def function_calling_chat(user_query: str):
# 5.1 初始化对话上下文、工具列表、函数映射
messages = [
{"role": "system", "content": "你是一位天气预报专家,能够预测给定城市的天气情况"},
{"role": "user", "content": user_query}
]
# 注册本地工具函数
local_functions = [get_weather_for_location]
# 函数名-函数实体映射,用于快速调用
func_map = {func.__name__: func for func in local_functions}
# 自动解析生成标准tools参数
tools = [parse_function_to_tool(func) for func in local_functions] # 5.2 第一轮对话:触发工具调用
response = client.chat.completions.create(
model="qwen-plus",
messages=messages,
tools=tools,
tool_choice="auto",
stream=False,
extra_body={"enable_thinking": False}
) msg = response.choices[0].message
# 判断是否需要调用工具
if not msg.tool_calls:
return msg.content # 5.3 本地执行工具函数
tool_result = execute_tool_call(msg.tool_calls, func_map) # 5.4 拼接第二轮对话上下文
# 适配新版SDK:对象转字典,避免上下文格式报错
messages.append(msg.model_dump())
# 加入工具执行结果
messages.append({
"role": "tool",
"tool_call_id": msg.tool_calls[0].id,
"content": tool_result
}) # 5.5 第二轮对话:生成最终答案
final_response = client.chat.completions.create(
model="qwen-plus",
messages=messages,
tools=tools,
tool_choice="auto",
stream=False,
extra_body={"enable_thinking": False}
) return final_response.choices[0].message.content
# 6. 测试运行
if __name__ == "__main__":
result = function_calling_chat("深圳今天的天气怎么样")
print("最终回答:", result)

Function Calling 核心不是模型执行代码,而是模型决策调用、本地执行、结果回传的两轮对话闭环;
所有工具参数均可通过Python内置 inspect 反射自动解析,无需手动维护,彻底解耦;
无框架实现的核心价值:掌握底层对话流转、参数规范、调用逻辑,为后续学习如LangChain等实现原理才能触类旁通