FastAPI + OpenAI 兼容协议 + DeepSeek 实战:大模型 Function Calling 工具调用全拆解
FastAPI OpenAI 兼容协议 DeepSeek 实战大模型 Function Calling 工具调用全拆解功能概览功能核心内容天气查询工具声明tools、单轮发起、解析tool_calls打印函数名参数不执行学历查询多工具声明、get_xueli外部调用 Redis 缓存、工具结果回灌第二轮合成调用链路总览用户问题 → messages tools 发给模型 → 模型返回 tool_calls函数名 JSON 参数 → 本地执行对应函数天气/学历 → 把 函数结果 以 roletool 追加回 messages → 再次请求模型 → 模型生成最终自然语言回答一、环境准备OpenAI 依赖下载与配置单列工具调用依赖 OpenAI 官方 SDK配置单独拎出来讲不与业务代码混在一起。1.1 依赖下载pipinstallopenai代码顶部都是from openai import OpenAI装好这个包即可。redis与requests是学历查询里缓存和调用外部 API 用到的按需安装pip install redis requests。1.2 密钥配置环境变量raw_keyos.getenv(DASHSCOPE_API_KEY)api_keyraw_key.strip()代码通过DASHSCOPE_API_KEY读取阿里云百炼的 API Key需提前在系统或.env中导出该变量。1.3 base_url 兼容端点clientOpenAI(api_keyapi_key,base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1,)关键是base_url必须带/compatible-mode/v1后缀——百炼把 OpenAI 协议做成了兼容模式漏写这一截会直接 404。二、知识点讲解2.1 什么是 Function Calling模型本身不能直接查天气、查数据库。我们告诉模型你有这些函数可用模型在觉得需要时返回我想调用某个函数、参数是这样真正的函数由我们本地执行再把结果交还给模型整理成话。这就是模型决策 本地执行的混合模式。2.2 tools 工具声明结构每个工具是一个 JSON-Schematype: functionfunction.name函数名function.description给模型看的功能描述function.parameters入参的 JSON Schema。description写得好不好直接决定模型会不会在该调用时调用。2.3 tool_calls 是什么模型认为需要调用工具时返回的消息里message.tool_calls不为None里面是列表每项含function.name函数名、function.argumentsJSON 字符串参数、id该次调用的标识回灌结果时必须带上。2.4 单轮识别 vs 完整闭环单轮拿到tool_calls就结束仅解析函数名/参数闭环执行函数 → 把结果以roletool追加 → 再请求一次模型由模型产出最终回答。少了回灌这步用户永远看不到自然语言结果。三、代码逻辑拆解3.1 客户端初始化与模型选择两个功能的顶部完全一致raw_keyos.getenv(DASHSCOPE_API_KEY)api_keyraw_key.strip()clientOpenAI(api_keyapi_key,base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1,)第 1 行从环境变量取密钥原始值第 2 行.strip()去掉首尾空白避免复制粘贴带换行导致鉴权失败第 3–5 行构造 OpenAI 客户端base_url指向百炼兼容端点。模型名不在客户端里写而是在每次create时指定见 3.3。3.2 第一个功能天气查询3.2.1 工具声明与本地函数tools[{type:function,function:{name:get_current_weather,description:当你想查询指定城市的天气时非常有用。,parameters:{type:object,properties:{location:{type:string,description:城市或县区比如北京市、杭州市、余杭区等。,}},required:[location],},},},]defget_current_weather(arguments):weather_conditions[晴天,多云,雨天]random_weatherrandom.choice(weather_conditions)locationarguments[location]returnf{location}今天是{random_weather}。name函数标识必须与本地真实函数同名模型返回时原样带回description模型的使用说明书决定何时触发parameters.properties.location入参 schemarequired声明该参数必填模型被要求必须产出它get_current_weather是本地实现从参数里取location随机返回一个天气演示用真实场景替换为气象 API。3.2.2 发起请求与单轮解析defget_response(messages):completionclient.chat.completions.create(modeldeepseek-v4-flash-0731,messagesmessages,toolstools,)returncompletion user_message[{role:user,content:你是谁}]messages.extend(user_message)completionget_response(messages)messages.append(completion.choices[0].message)ifcompletion.choices[0].message.tool_callsisNone:print(f无需调用工具直接回复{completion.choices[0].message.content})else:print(需要调用工具:)tool_callscompletion.choices[0].message.tool_callsforiintool_calls:f_namei.function.name f_argi.function.argumentsprint(f调用工具是{f_name}参数{f_arg})modeldeepseek-v4-flash-0731具体模型名写在每次请求里toolstools把工具清单一并提交模型才会返回tool_callsmessages.extend把用户问题追加进对话列表模块级messages []completion.choices[0].message模型原始消息先整体append进messages保证多轮上下文连续if ... tool_calls is None判断是否触发工具——None说明模型直接回答了否则进入工具分支工具分支里i.function.name取函数名、i.function.arguments取参数字符串是字符串不是字典要用json.loads解析注意这一段到这里只print没真正执行函数、也没回灌所以它只是识别演示。3.3 第二个功能学历查询3.3.1 Redis 客户端与外部学历查询工具rRedis(host127.0.0.1,port6379,db11,decode_responsesTrue)defget_xueli(arguments):vcodearguments[vcode]keyfboos:llm:academic_credential_verification:{vcode}redis_vreifr.get(key)ifredis_vreifisNone:API_KEYMY_KEY_le0KRXAsCh6cNphDEURqJCs02jt3x1BASE_URLhttps://www.apimy.cn/api/xxw/bgcxparams{key:API_KEY,vcode:arguments[vcode]}headers{Content-Type:application/json}responserequests.get(BASE_URL,paramsparams,timeout30)response.raise_for_status()dataresponse.json()r.set(key,json.dumps(data,ensure_asciiFalse))returnjson.dumps(data,ensure_asciiFalse)else:returnredis_vreif第 1–4 行Redis 客户端db11与多轮对话的db10区分开decode_responsesTrue让取出的 value 直接是字符串key用学历验证码拼键同一验证码只查一次if redis_vreif is None缓存未命中才真打外部 API命中直接返回省额度requests.get(..., timeout30)timeout必带外部接口抽风时不会把连接挂死r.set(key, json.dumps(data))把结果以 JSON 字符串写回 Redis返回时json.dumps(data)与缓存写入格式保持一致工具结果对模型而言都是字符串即可。3.3.2 多工具声明tools[{type:function,function:{name:get_current_weather,description:当你想查询指定城市的天气时非常有用。,parameters:{type:object,properties:{location:{type:string,description:城市或县区比如北京市、杭州市、余杭区等。,}},required:[location],},},},{type:function,function:{name:get_xueli,description:当你想查询学历或验证学历时非常有用。,parameters:{type:object,properties:{vcode:{type:string,description:学历验证码,}},required:[vcode],},},},]在天气工具之外还声明了get_xueli工具结构一致参数换成了vcode学历验证码required: [vcode]。两个工具并列放在同一个tools列表里模型可自由选其一或都用。3.3.3 完整闭环执行工具 结果回灌user_message[{role:user,content:帮我查下一下学历验证码是:你自己的学历吗}]messages.extend(user_message)completionget_response(messages)messages.append(completion.choices[0].message)ifcompletion.choices[0].message.tool_callsisNone:print(f无需调用工具直接回复{completion.choices[0].message.content})else:print(需要调用工具:)tool_callscompletion.choices[0].message.tool_calls fun{get_current_weather:get_current_weather,get_xueli:get_xueli}foriintool_calls:f_namei.function.name f_argi.function.argumentsprint(f调用工具是{f_name}参数{f_arg})tool_resultfun[f_name](json.loads(f_arg))print(f工具返回结果是{tool_result})# 把工具结果追加消息再次请求模型生成自然文本messages.append({role:tool,tool_call_id:i.id,content:tool_result})final_respget_response(messages)print(模型整理后的文本回答,final_resp.choices[0].message.content)fun {...}函数名到本地函数对象的映射表模型返回函数名后据此分发执行fun[f_name](json.loads(f_arg))用json.loads把参数字符串还原成字典后调用messages.append({role: tool, ...})这是闭环的关键——role必须是tooltool_call_id填模型给的i.idcontent填工具返回值get_response(messages)再次发起此时模型已拿到工具结果会生成最终自然语言回答。源码注释也点明把工具结果追加消息再次请求模型生成自然文本就是回灌 二次请求。

相关新闻

从《绝地求生》逆向提取虚幻引擎4 SDK:IDA Pro与Cheat Engine实战指南

从《绝地求生》逆向提取虚幻引擎4 SDK:IDA Pro与Cheat Engine实战指南

1. 项目概述:从游戏到引擎的逆向之路最近在逆向分析圈里,一个经久不衰的话题就是如何从热门游戏中提取出游戏引擎的软件开发工具包。这听起来像是个黑魔法,但实际上,它是一套结合了静态分析与动态调试的严谨技术流程。今天要聊的&…

2026/10/13 20:19:57 阅读更多 →
为什么你在公司能成事,独处时却一事无成?

为什么你在公司能成事,独处时却一事无成?

——从脚手架、意识形态到情绪战争的三个维度解剖你有没有发现一个悖论: 同一个人,同一颗大脑,同一套意志力。 在公司里,你能连续加班三个月交付一个复杂项目,能在状态极差的情况下完成高质量的方案,能在截…

2026/10/15 19:24:10 阅读更多 →
非线性控制中的Lipschitz条件:从数学基础到工程实践

非线性控制中的Lipschitz条件:从数学基础到工程实践

1. 项目概述:为什么非线性控制绕不开Lipschitz条件?如果你刚开始接触非线性控制理论,可能会被一堆数学条件搞得头大,其中“Lipschitz条件”绝对是出场率最高的明星之一。它不像“稳定性”、“能控性”那样直观,名字听起…

2026/10/14 21:22:50 阅读更多 →

最新新闻

Spring AI多模态实战:图片理解与视觉模型集成指南

Spring AI多模态实战:图片理解与视觉模型集成指南

1. 从文本到视觉:多模态能力到底解决了什么痛点大模型能写代码、能编故事、能陪你聊天,但很长一段时间里,它其实是个“瞎子”。你丢一张图过去,它只能看到文件名,或者干脆告诉你“我无法处理图片”。这个限制在实际项目…

2026/10/16 8:22:27 阅读更多 →
双线性插值C语言实现:图像缩放平滑处理与代码详解

双线性插值C语言实现:图像缩放平滑处理与代码详解

简介:一款面向C语言学习者和数值计算开发者的双线性插值算法实现资源,系统讲解二维平面上基于四个相邻已知点进行加权插值的数学原理,从权重系数计算到结果输出均给出清晰实现与注释,并打包为可直接编译运行的Visual Studio工程。…

2026/10/16 8:22:27 阅读更多 →
弹簧质点系统仿真:显式与隐式积分器的稳定性对比与代码实现

弹簧质点系统仿真:显式与隐式积分器的稳定性对比与代码实现

1. 从一根弹簧说起:为什么积分器的选择决定了仿真的生死如果你做过物理仿真,大概率经历过这样的场景:明明代码逻辑没问题,参数也调得不算离谱,但仿真跑着跑着就炸了——质点飞出去、弹簧无限拉伸、整个系统数值发散。很…

2026/10/16 8:22:27 阅读更多 →
数值分析上机编程全攻略:题型拆解、算法选型与Python实现

数值分析上机编程全攻略:题型拆解、算法选型与Python实现

数值分析上机编程题,大概是每个工科学生都绕不过去的坎。期末要交作业、考试前突击练习、保研复试上机考核,翻来覆去就是那几个经典题目:插值、数值积分、非线性方程求根、线性方程组迭代解、常微分方程数值解。题目看着简单,但真…

2026/10/16 8:22:27 阅读更多 →
AI Agent编排实战:历史资料汇编六轮复盘与架构设计

AI Agent编排实战:历史资料汇编六轮复盘与架构设计

1. 为什么"资料汇编"这件事值得用 Agent 编排重做一遍做历史专题资料汇编的人大概都有过这种体验:一个专题从立项到成稿,真正花在"找资料"上的时间可能只占三成,剩下七成全耗在"整理、比对、去重、补漏、格式统一&q…

2026/10/16 8:22:27 阅读更多 →
Agent Harness Engineering产品化:定价模型与增长策略实战指南

Agent Harness Engineering产品化:定价模型与增长策略实战指南

做Agent产品的团队这两年是肉眼可见地多起来了,我也算是在这个坑里折腾出来的。身边同行朋友的情况大多相似:内部已经攒了一套挺完整的Agent Harness Engineering底座,模型路由、工具调度、上下文管理、可观测性这些模块一个不少,…

2026/10/16 8:21:25 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →