当前位置: 首页 > news >正文

无框架手写实现Function Calling:原理拆解+纯Python手写实现

文章目录

  • 零、文章基础说明
  • 一、Function Calling 核心本质
  • 二、通义千问 Function Calling 对话全流程(原生参数详解)
    • 2.1 核心请求参数详解(Function Calling 必备)
    • 2.2 第一轮对话:用户提问,模型触发工具调用
      • 第一轮完整请求体
      • 第一轮响应结果(核心)
    • 2.3 本地中间处理:解析参数、执行工具函数
    • 2.4 第二轮对话:工具结果回传,模型生成最终答案
      • 第二轮完整请求体
      • 第二轮最终响应(用户可见答案)
  • 三、原生Python函数参数解析原理(无框架)
    • 3.1 核心原理:inspect 库能力
    • 3.2 解析规则(适配通义千问工具格式)
    • 3.3 解析示例演示
  • 四、纯Python无框架 Function Calling 完整实现
    • 4.1 整体流程流程图
    • 4.2 完整可运行代码
  • 五、核心总结

零、文章基础说明

很多朋友在使用Function Calling时,都会依赖 LangChain、LlamaIndex 等框架,对function calling完全不清楚底层调用逻辑、对话流转机制、参数解析原理

  1. 本文零框架、零第三方AI工具库依赖,基于阿里云通义千问 OpenAI 兼容接口,从底层原理、对话流转、原生参数解析、手写代码实现四个维度,完整拆解 Function Calling 核心能力,理清楚大模型工具调用的本质

  2. 所有内容均为原生实现,不依赖任何封装框架,全程可追溯、可调试、可自定义改造

  3. 大模型使用阿里云百炼的qwen-plus:https://help.aliyun.com/zh/model-studio/qwen-api-via-openai-chat-completions?spm=a2c4g.11186623.0.0.69a16f58415OHe#b1320a1664b9a

  • 用DeepSeek或者别的任何模型都行,最核心的是理解执行原理

一、Function Calling 核心本质

Function Calling(工具调用)的本质并不是大模型自动执行代码,而是在给大模型问题的时候,将可能用到的工具信息(函数)一起全部发给大模型。大模型根据用户问题,判断是否需要调用对应的工具(函数),如需要则大模型结构化输出工具名称和入参,由本地代码(例如你自己的Python项目)完成工具执行、然后将Python执行结果回传给大模型,大模型集合工具输出和问题返回最终答案的流程。

整个流程分为三段核心逻辑:

  1. 第一轮对话:传入用户问题+工具描述,大模型判断需要调用工具,返回结构化的工具调用参数(不直接回答用户)
  2. 本地执行:解析大模型返回的工具参数,本地Python代码执行对应函数,获取真实结果
  3. 第二轮对话:将工具执行结果回传给大模型,大模型整合结果,生成最终自然语言回答

二、通义千问 Function Calling 对话全流程(原生参数详解)

这部分将通过普通的 http 请求完成function calling的演示,无论用postman/apifox/apipost都可以,此处使用的是 Reqable(即使使用CURL也完全OK)

  1. 本文基于阿里云通义千问 OpenAI 兼容接口实现(接口地址:https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions),适配标准 OpenAI 格式的 Function Calling 参数。

  2. 以「查询深圳天气」场景为例,完整拆解第一轮请求、第一轮响应、第二轮请求、最终响应的路数据流转。

2.1 核心请求参数详解(Function Calling 必备)

  1. 区别于普通对话,Function Calling 需要新增toolstool_choice两个核心参数,结合基础对话参数,完整必填参数说明如下:
  • model:必选,模型名称,本文使用qwen-plus
  • messages:必选,对话上下文数组,包含 system(角色设定)、user(用户提问)、assistant(模型回复)、tool(工具返回结果)四类消息
  • tools:必选,工具描述数组,告知大模型「有哪些工具、工具作用、需要什么参数」,是大模型判断调用逻辑的核心依据
  • tool_choice:可选,工具调用策略,auto表示大模型自主判断是否调用工具、调用哪个工具

2.2 第一轮对话:用户提问,模型触发工具调用

第一轮核心目的:告诉大模型用户需求 + 可用工具,让大模型输出标准化工具调用指令。此时大模型不会直接回答问题,只会返回工具调用参数

  • 重点:这里的function信息实际上就是对应一个Python函数的描述

第一轮完整请求体

{"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方法拿到结果)

  • 特别注意:tool_calls里面有对应的Id,也就是函数调用Id:call_734c63b9a3d44470937769
{"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",代表模型判定需要调用外部工具,终止文本生成

2.3 本地中间处理:解析参数、执行工具函数

拿到第一轮响应后,本地代码需要完成 3 件事:

  1. 解析tool_calls,提取工具名称get_weather_for_location和入参city=深圳
  2. 匹配本地同名Python函数,传入参数执行,获取工具结果(模拟天气接口返回:多云转阴)
  3. 拼接 tool 类型消息,用于第二轮对话回传

2.4 第二轮对话:工具结果回传,模型生成最终答案

第二轮核心目的:将「用户问题+模型工具调用指令+本地工具执行结果」完整回传给大模型,让大模型整合信息,输出自然语言最终回答。

重点: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的核心原理就是这样的交互方式

三、原生Python函数参数解析原理(无框架)

  1. 上文的tools结构体是固定的大模型工具格式
  2. 了解了对应的交互原理后,我们只需要将http请求转成Python即可
  3. 生产环境中,我们需要自动解析本地Python函数,生成标准tools参数,核心依赖Python内置inspect库,无需任何第三方库

3.1 核心原理:inspect 库能力

inspect是Python内置反射库,可实现对函数的逆向解析,获取三大核心信息,完美适配大模型工具参数格式:

  1. 函数名称:对应 tool function 的 name 字段
  2. 函数文档注释:对应 tool function 的 description 字段(工具功能描述)
  3. 函数参数签名:获取参数名、参数类型、是否必填,对应 tool function 的 parameters 字段

3.2 解析规则(适配通义千问工具格式)

我们自定义一套原生解析规则,将Python原生函数,标准化转为大模型可识别的 tools 结构体:

  • 函数名 →function.name
  • 函数首行docstring →function.description
  • 函数参数名/类型注解 →parameters.properties
  • 无默认值的参数 →required必填列表
  • 统一参数类型映射:str→string、int→integer、float→number、bool→boolean

3.3 解析示例演示

本地原生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结构体

四、纯Python无框架 Function Calling 完整实现

基于上述原理,我们手写完整可运行代码,包含:函数自动解析、两轮对话流转、工具参数解析、本地函数执行、结果回传全流程,零框架依赖。

4.1 整体流程流程图

整体闭环流程:

定义本地工具函数 → inspect自动解析生成tools参数 → 第一轮API请求(触发工具调用)→ 解析tool_calls参数 → 本地执行工具函数 → 拼接完整对话上下文 → 第二轮API请求 → 输出最终自然语言答案

结构化流程图:可以使用 https://www.jyshare.com/front-end/9729/ 在线的 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

4.2 完整可运行代码

  1. 依赖安装:仅需安装官方 openai 基础SDK(仅用于请求封装,无框架逻辑),
    pip install openaiuv add openai,此处使用uv,还需要添加 dotenv 用于加载环境变量
uvaddopenai dotenv
  1. 代码实现
importosimportjsonimportinspectfromtypingimportCallable,Dict,AnyfromopenaiimportOpenAI# 加载环境变量(兼容本地.env配置)fromdotenvimportload_dotenv load_dotenv()# 1. 初始化通义千问兼容客户端(无框架,原生请求)client=OpenAI(api_key=os.getenv("DASHSCOPE_API_KEY"),base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")# 2. 定义本地工具函数(可自定义扩展)defget_weather_for_location(city:str)->str:"""Get weather for a given city Args: city: 需要查询天气的城市名称 """weather_map={"深圳":"多云转阴","北京":"晴","上海":"小雨","广州":"多云"}returnweather_map.get(city,"暂无该城市天气数据")# 3. 核心工具:inspect自动解析函数,生成大模型标准tools格式defparse_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"}# 遍历解析每个参数forparam_name,paraminparams.items():# 获取参数类型,默认stringparam_type=type_map.get(param.annotation,"string")# 获取参数描述(从docstring简易解析)param_desc=""iff"{param_name}:"indoc:param_desc=doc.split(f"{param_name}:")[-1].split("\n")[0].strip()# 写入参数属性tool["function"]["parameters"]["properties"][param_name]={"type":param_type,"description":param_desc}# 无默认值的参数为必填ifparam.defaultisparam.empty:tool["function"]["parameters"]["required"].append(param_name)returntool# 4. 工具调用执行器:【修复兼容问题】解析模型返回的tool_calls对象defexecute_tool_call(tool_calls:list,func_map:Dict[str,Callable])->str:"""执行模型触发的工具调用,返回工具执行结果 兼容新版OpenAI SDK对象取值,杜绝下标报错 """forcallintool_calls:# 修复:新版SDK返回实体对象,不支持字典下标取值,需用属性调用func_name=call.function.name func_args=json.loads(call.function.arguments)# 匹配本地函数并执行iffunc_nameinfunc_map:returnfunc_map[func_name](**func_args)return"工具调用失败,未匹配到本地函数"# 5. 完整Function Calling闭环流程deffunction_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__:funcforfuncinlocal_functions}# 自动解析生成标准tools参数tools=[parse_function_to_tool(func)forfuncinlocal_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# 判断是否需要调用工具ifnotmsg.tool_calls:returnmsg.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})returnfinal_response.choices[0].message.content# 6. 测试运行if__name__=="__main__":result=function_calling_chat("深圳今天的天气怎么样")print("最终回答:",result)

五、核心总结

  1. Function Calling 核心不是模型执行代码,而是模型决策调用、本地执行、结果回传的两轮对话闭环;

  2. 所有工具参数均可通过Python内置inspect反射自动解析,无需手动维护,彻底解耦;

  3. 无框架实现的核心价值:掌握底层对话流转、参数规范、调用逻辑,为后续学习如LangChain等实现原理才能触类旁通

http://www.jsqmd.com/news/874382/

相关文章:

  • Claude API文档版本管理生死线:v2.1→v3.0迁移实录,12个breaking change的文档同步策略
  • 别再乱格式化!一文搞懂NTFS、exFAT等磁盘格式区别与DiskGenius格式化实操
  • Super IO Blender插件:终极批量导入导出指南,工作效率提升300%
  • 储能 PACK 与 BMS:怎么识别有真实出货的系统集成厂,避开组装贴牌
  • 从纸质报表到Excel:PaddleOCR+Python自动化识别复杂表格(附完整代码)
  • 2026郑州柔性腻子优质品牌推荐指南:河南金刚沙腻子、河南防水抗裂砂浆、河南防水砂浆、郑州儿童房腻子、郑州内墙漆腻子选择指南 - 优质品牌商家
  • 别再死记ResNet结构了!用Python手搓一个ResUnet,从代码里真正搞懂残差连接
  • 觅健AI病程管理系统入选2026中国医疗健康产业最具创新力产品技术50强
  • P2WPKH:比特币的「见证革命」与比特鹰的技术解析
  • 照亮虚拟世界:神经渲染中的神经光照技术全解析
  • 【Lovable高阶开发者私藏技巧】:绕过平台限制实现自定义CSS/JS注入与第三方SDK深度对接
  • 2026徐闻装修公司推荐:徐闻别墅装修/徐闻办公楼装修/徐闻商铺装修/徐闻奶茶店装修/徐闻精装修/徐闻装修公司/选择指南 - 优质品牌商家
  • 计算机视觉与贝叶斯优化驱动的粉末饮料智能制备系统
  • 《论三生原理》对《周易》《道德经》的一次根本性重写?
  • C++:内存管理
  • 2026年5月更新:上海大平层价值锚点,为何聚焦古北国际住区? - 2026年企业推荐榜
  • 神经渲染革命:一文读懂可微分渲染的核心原理与产业未来
  • 信创运维实战:用PXE批量部署银河麒麟V10桌面版,我踩过的坑都帮你填平了
  • 2026南京娱乐许可证办理优质服务商推荐:南京农药兽药许可证办理/南京出版物许可证办理/南京危化品许可证办理/南京增值电信许可证办理/选择指南 - 优质品牌商家
  • 别再死记硬背CRF公式了!用Python手写一个BIO命名实体识别Demo,带你直观理解发射与转移矩阵
  • 神经渲染“加速器”:一文读懂哈希编码的原理、应用与未来
  • 自制靶机--Believe
  • 1000个文件重命名,1秒完成!批量文件重命名软件
  • Hexo 排坑记:删除所有文章后首页无法访问(Cannot GET)
  • 芯片设计与流片:关键流程解析
  • 类和对象概括
  • 2026年4月全国冷库回收优质服务商推荐榜:无尘车间回收、无尘车间拆除、木工设备回收、松下贴片机回收、气动配件回收选择指南 - 优质品牌商家
  • 鸿蒙electron跨端框架PC导出管家实战:把交付前的检查、复制和导出做成一个工坊
  • 2026无腻子钣金培训权威厂家推荐指南:冰雹车无痕修复、凹陷修复培训、凹陷修复工具、局部喷漆、挡风玻璃修复、数据复原培训选择指南 - 优质品牌商家
  • 自动化业务通报系统实现