Function Calling 最大的骗局:AI 根本不会调用你的函数
这篇文章我们就把这个误会彻底拆开揉碎看看 Function Calling 这台“魔术”背后到底是谁在拉线。01、先破除一个迷信MYTH · 模型到底做没做上一篇文章发出去后我们还有一个问题没有说明“模型是怎么连上12306网站的它是不是自己写了段代码偷偷跑了一下”在这里我特别说明因为“Function Calling”函数调用这个名字本身就带有极强的误导性——听起来好像模型真的伸出一只虚拟的手“啪”地点了一下某个函数把它执行了。但残忍的真相是大模型自始至终一行代码都没有执行过它甚至连你写的那个函数长什么样都不知道它只是很会“猜”猜你接下来大概率想调用哪个工具、传什么参数然后老老实实地把这个“猜测”用一种约定好的格式写出来仅此而已。真正把工具“跑起来”的从头到尾都是你写的那部分 Python 代码。模型只是一个非常擅长猜测和总结的“内容生成器”它连自己刚才“调用”了什么都不记得——它只是生成了一段符合格式的文字。这篇文章我们就把这个误会彻底拆开揉碎看看 Function Calling 这台“魔术”背后到底是谁在拉线。02、总机接线员的比喻ANALOGY · 判断与填单我最喜欢用这个比喻给人解释 Function Calling大模型更像一个坐在总机前的接线员而不是亲自跑腿的快递员。你打电话进公司总机说“我要投诉产品质量问题”接线员不会自己去处理你的投诉他只会判断——“哦这个应该转到售后部门”然后把电话转过去。真正处理你投诉的是售后部门的人接线员只是判断该转给谁这一件事做得特别准。大模型在 Function Calling 里扮演的正是这个“接线员”角色。它的工作只有两件事1.判断用户这句话是不是需要借助外部工具才能回答如果需要该用哪个工具2.填单把需要传给这个工具的参数按照约定好的格式整理出来。至于工具本身怎么运行、会不会报错、跑出来的结果对不对——这些统统不归模型管模型两眼一抹黑它只负责“转接”和“填单”这两步。那“约定好的格式”具体长什么样这就是我们接下来要拆解的核心。03、模型是怎么“猜”出该调用哪个工具的MECHANISM · 联系单与转接单还记得上一篇里我们传给模型的那个 tools 参数吗它长这样Pythontools [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气情况, parameters: { type: object, properties: { city: { type: string, description: 城市名称例如上海 } }, required: [city], }, }, } ]这一坨 JSON本质上就是递给接线员的一张“部门联系单”。上面写着“有一个叫 get_weather 的部门它是干查天气这活儿的接电话时得问对方要一个叫 city 的信息。”模型看到用户问“上海明天下雨吗”之后会拿这句话去跟“联系单”上罗列的每个部门比对——“查天气”这个描述和用户的意图很匹配啊那就该转到 get_weather 这个部门。于是模型不会直接生成一句大白话回复而是生成一段结构化的“转接单”大概长这样不同模型的具体返回结构略有差异但思路一致Json{ tool_calls: [ { id: call_8f3a2b, function: { name: get_weather, arguments: {\city\: \上海\} } } ] }看到没有模型输出的依然只是一段文字只不过这段文字被严格规范成了 JSON 格式方便你的代码去解析。模型没有“执行”任何东西它只是很擅长按照给定的格式把“我猜你想调用 get_weather参数是上海”这件事表达出来。这就是 Function Calling 的全部秘密写清楚“菜单”给模型看模型帮你点一道菜剩下炒菜的活全是你自己的代码在干04、手写一个真正的“工具调度中心”HANDS-ON · Tool Registry 实战理解了原理我们这次不满足于只写一个工具了来点更接近真实项目的东西——搭一个能同时管理多个工具的“调度中心”。这次我们用 deepseek-v4-flash 来做示例DeepSeek 的接口是 OpenAI 兼容格式换个 base_url 和模型名就能直接跑。先设计一个工具注册表Tool Registry用装饰器的方式登记工具顺便自动把函数签名转换成模型能读懂的 JSON Schema免得每次手写一遍Pythonimport inspect import json from openai import OpenAI client OpenAI( api_keysk-xxxxxxx, base_urlhttps://api.deepseek.com, ) # ----------------------------- # 第一步搭一个工具注册表用装饰器自动登记工具 # ----------------------------- class ToolRegistry: def __init__(self): self._funcs {} # 存真正的函数本体 self._schemas [] # 存喂给模型看的联系单 def register(self, description: str): 装饰器登记一个工具并自动从函数签名推导参数 schema def decorator(func): sig inspect.signature(func) properties {} required [] for name, param in sig.parameters.items(): # 简化处理默认都当字符串参数 # 实际项目可以根据类型注解自动推导 properties[name] { type: string, description: f{name}参数, } required.append(name) self._funcs[func.__name__] func self._schemas.append( { type: function, function: { name: func.__name__, description: description, parameters: { type: object, properties: properties, required: required, }, }, } ) return func return decorator def get_schemas(self): return self._schemas def call(self, name: str, args: dict): 真正执行某个工具函数 if name not in self._funcs: return f错误不存在名为{name}的工具 try: return self._funcs[name](**args) except Exception as e: # 执行失败也要告诉模型而不是程序崩掉 return f工具执行出错{str(e)} registry ToolRegistry() # ----------------------------- # 第二步登记工具 # ----------------------------- registry.register(查询指定城市的实时天气) def get_weather(city: str) - str: fake_db { 上海: 小雨18-24℃, 北京: 晴20-28℃, } return fake_db.get(city, 暂无该城市天气数据) registry.register(查询指定股票代码的当前价格) def get_stock_price(code: str) - str: fake_db { SPCX: 139.140美元, AAPL: 317.310美元, } return fake_db.get(code, 暂无该股票数据) # ----------------------------- # 第三步Agent 主循环 # ----------------------------- def run_agent(user_input: str): messages [ { role: user, content: user_input, } ] response client.chat.completions.create( modeldeepseek-v4-flash, messagesmessages, toolsregistry.get_schemas(), ) reply response.choices[0].message # 如果模型决定调用工具 if reply.tool_calls: messages.append(reply) for call in reply.tool_calls: # 模型返回的是 JSON 字符串需要解析 try: args json.loads(call.function.arguments) except json.JSONDecodeError: # JSON 不合法时兜底 args {} result registry.call( call.function.name, args, ) messages.append( { role: tool, tool_call_id: call.id, content: str(result), } ) # 第二轮请求让模型结合工具结果回答 second_response client.chat.completions.create( modeldeepseek-v4-flash, messagesmessages, ) return second_response.choices[0].message.content # 不需要调用工具 return reply.content # ----------------------------- # 测试 # ----------------------------- if __name__ __main__: print(run_agent(帮我查下苹果(AAPL)现在多少钱一股))跑起来后模型会先在心里“转接”到 get_stock_price 这个工具参数猜出 code“AAPL”我们的调度中心真正执行完拿到 “317.310美元”再喂回给模型模型才组织出一句“苹果目前股价为317.310美元”这样的自然语言回复给你。对比第一篇的代码这次多了一个“工具注册表”——这是所有正经智能体项目里都会有的东西工具一多你不可能每次都手写一遍 JSON Schema用装饰器自动生成是业界很常见的做法LangChain 这类框架内部本质上也是干这个事。05、三个新手最容易踩的坑PITFALLS · 踩坑实录写到这里必须得跟你唠叨几个我自己踩过的坑不然你上手写第一个真实项目时大概率会懵。坑一JSON参数不一定合法 模型本质是在“预测下一个字”它不是一个严格的 JSON 生成器。偶尔它会漏掉一个引号或者多加一个逗号导致 json.loads() 直接报错崩溃。上面代码里我专门加了 try/except 兜底这不是我小心谨慎而是这个坑我是真摔过——线上跑了两周好好的某一天模型突然抽风输出了个残缺JSON整个服务直接500排查了半天才发现是这里没兜底。永远不要相信模型输出的格式100%规范这是血的教训。坑二参数类型模型不检查 比如你的函数要求传入一个整数 days: int但模型有时候会传个字符串 “three” 过来尤其是用户说“帮我查未来三天”这种口语化表达时。模型只管“猜个参数出来”至于这个参数符不符合你函数的类型要求它不负责。所以工具函数内部或者调度中心的 call 方法里最好做一层参数校验和类型转换别指望模型帮你把关。坑三工具失败别让程序崩溃 新手很容易犯的错是工具内部报错了就直接抛异常让整个程序挂掉。正确的做法是像上面代码里那样把错误信息也当成一条正常的工具返回结果塞回给模型让模型知道“哦这个工具刚才失败了”模型甚至可能会据此调整策略比如换一种方式重试或者老实告诉用户“抱歉查不到”。这种“让模型知道自己失败了”的设计其实是后面第7篇要讲的“自我纠正”能力的雏形。这一篇我们捅破了什么SUMMARY · 写在最后回顾一下今天的核心Function Calling 从来不是模型在“执行代码”而是一场设计精良的“结构化猜词游戏”——你把工具清单递给模型模型猜一个最匹配的工具和参数用 JSON 格式写出来然后你的代码负责真刀真枪地把它跑起来再把真实结果喂回去。理解了这一层你会发现智能体系统所有“智能”调用底层都不过是猜测解析执行反馈的循环但你可能也发现了一个问题如果任务复杂到需要“先查一下天气根据天气结果再决定要不要接着查机票”模型能不能在生成“转接单”之前先把自己的思考过程说出来让整个决策过程更靠谱、更可控这正是下一篇的主角——ReActReasoning Acting范式。我们会让智能体在每次“转接”之前先大大方方地把“我为什么要这么做”说出来你会发现这一个小小的改动能让整个智能体的可靠性和可调试性上一个台阶。下一篇见。我是写代码的中年人长期专注于 AI 算法、智能体、以及 AI 工程化落地。

相关新闻

从手动到自动化:swift-create-xcframework如何改变Swift包的分发方式

从手动到自动化:swift-create-xcframework如何改变Swift包的分发方式

从手动到自动化:swift-create-xcframework如何改变Swift包的分发方式 【免费下载链接】swift-create-xcframework A simple Command Line Tool to create XCFrameworks by wrapping xcodebuild. 项目地址: https://gitcode.com/gh_mirrors/sw/swift-create-xcfram…

2026/7/16 21:27:11 阅读更多 →
巡检机器人远程监控物联网系统方案

巡检机器人远程监控物联网系统方案

在工业4.0与智能制造深度融合的背景下,巡检机器人作为替代人工进行高危、高频、高强度巡检任务的智能载体,已广泛应用于变电站、数据中心、石化管廊、配电房、工厂车间等关键场景。然而,随着机器人部署规模的扩大与作业环境的复杂化&#xff…

2026/7/16 21:27:11 阅读更多 →
Spyc与Composer集成:现代PHP项目中的YAML配置管理

Spyc与Composer集成:现代PHP项目中的YAML配置管理

Spyc与Composer集成:现代PHP项目中的YAML配置管理 【免费下载链接】spyc A simple YAML loader/dumper class for PHP 项目地址: https://gitcode.com/gh_mirrors/sp/spyc 在现代PHP开发中,高效管理配置文件是提升开发效率的关键环节。Spyc作为一…

2026/7/16 21:25:11 阅读更多 →

最新新闻

Grok 4.20长文本与实时数据协同处理技术解析

Grok 4.20长文本与实时数据协同处理技术解析

1. Grok 4.20 不是“又一个大模型升级”,而是长文本与实时数据协同处理的临界点突破你有没有试过把一份200页的PDF技术白皮书、一整套带注释的API文档、或者连续72小时的IoT设备日志,直接丢进当前主流的大模型对话框里?大概率会看到三类反馈&…

2026/7/16 22:19:32 阅读更多 →
YOLOv11n蚊子种类检测数据集13166张(存在增强处理),yolo,voc,coco三种标注方式17类蚊虫数据集病媒生物检测,虫媒防控,疾控监测,昆虫分类,图像识别,PyQt5界面,实时检测,

YOLOv11n蚊子种类检测数据集13166张(存在增强处理),yolo,voc,coco三种标注方式17类蚊虫数据集病媒生物检测,虫媒防控,疾控监测,昆虫分类,图像识别,PyQt5界面,实时检测,

蚊子种类检测数据集13166张(存在增强处理),yolo,voc,coco三种标注方式 图像尺寸:512*512 类别数量:17类 训练集图像数量:11520; 验证集图像数量:1097; 测试集图像数量:549 类别名称: 每一类图像数 &#xf…

2026/7/16 22:19:32 阅读更多 →
2026年AI编程工具横评:Trae vs Cursor vs Copilot,谁才是开发者的最佳搭档?

2026年AI编程工具横评:Trae vs Cursor vs Copilot,谁才是开发者的最佳搭档?

前言 2026年,AI辅助编程已经从「尝鲜」变成了「刚需」。 无论是刚入门的实习生,还是摸爬滚打十几年的老架构师,都在用AI工具提升开发效率。但问题是——市面上的AI编程工具琳琅满目,到底哪一款真正适合你? 今天我们不吹…

2026/7/16 22:17:30 阅读更多 →
Packet Tracer实战指南:从零搭建企业级网络拓扑

Packet Tracer实战指南:从零搭建企业级网络拓扑

1. 企业级网络拓扑设计入门 第一次打开Packet Tracer时,面对空白的拓扑工作区可能会有些不知所措。别担心,我们先从最基础的企业网络架构开始。典型的企业网络通常包含总部与分支机构的互联、多个VLAN划分以及DHCP服务等核心功能。这就像建造一栋大楼&am…

2026/7/16 22:11:29 阅读更多 →
LabVIEW实现USB相机图像采集与处理全解析

LabVIEW实现USB相机图像采集与处理全解析

1. 为什么选择LabVIEW进行USB相机图像采集在工业检测、医疗影像和科研实验中,图像采集系统往往需要与数据处理、逻辑控制等功能紧密结合。LabVIEW作为图形化编程语言的代表,其独特的优势使其成为图像采集领域的理想选择。首先,LabVIEW的并行数…

2026/7/16 22:11:29 阅读更多 →
为什么92%的AI Agent PoC失败?揭秘头部科技公司筛选高价值场景的4维评估模型

为什么92%的AI Agent PoC失败?揭秘头部科技公司筛选高价值场景的4维评估模型

更多请点击: https://intelliparadigm.com 第一章:为什么92%的AI Agent PoC失败?揭秘头部科技公司筛选高价值场景的4维评估模型 92%的AI Agent概念验证(PoC)未能进入规模化落地阶段,根本原因并非技术不成…

2026/7/16 22:11:29 阅读更多 →

日新闻

HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储

HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储

本篇深入剖析 ImportantDays 项目的数据持久化方案——基于 HarmonyOS ArkData 模块的 Preferences 轻量级存储,以及 PreferenceUtil 工具类的单例封装。一、HarmonyOS 数据存储方案对比 HarmonyOS 提供了多种数据存储方案:方案适用场景特点Preferences轻…

2026/7/16 0:08:27 阅读更多 →
Python实现跨境电商商品图批量翻译教程

Python实现跨境电商商品图批量翻译教程

一、问题引入做跨境电商的卖家朋友,你是否遇到过这样的困扰?每次上架新品到亚马逊、Shopee或Lazada等平台,都需要处理大量商品图片的多语言版本。比如上架200款衣服,每款需要翻译成英语、日语、韩语等5种语言,这意味着…

2026/7/16 0:08:27 阅读更多 →
鸿蒙 7 新特性实战①:从 0 到 1 掌握 @kit 标准导入规范

鸿蒙 7 新特性实战①:从 0 到 1 掌握 @kit 标准导入规范

从鸿蒙 7(HarmonyOS NEXT)开始,官方全面完成了从 ohos.* 零散模块到 kit.* 领域套件的体系重构。对开发者来说,第一道门槛不是 API 用法变化,而是统一的导入规范——旧体系默认导入、解构导入混用的混乱局面被彻底终结…

2026/7/16 0:10:29 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/15 21:09:01 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/15 19:42:20 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/15 17:52:08 阅读更多 →

月新闻