← 返回教程库

Function Calling:让大模型调用你的硬件函数

最后更新 2026-06-20
L4 · AI 赋能 / AIoT ⏱ 约 17 分钟 🟢 软件/低风险
你将学到
  • 讲透 Function Calling 到底是什么、模型在这一步做的是哪种决策
  • 看懂"模型选函数 → ESP32 执行 → 结果回传"这条控制链路怎么走
  • 看懂一段把硬件动作描述给模型、由模型决定调哪个的示意代码
  • 知道它和"纯 prompt 硬解析"相比好在哪,以及为什么它是 AI Agent 控硬件的基础

上一节你让硬件接上了大模型,它能听懂人话、能对答。但到这里它还只是"会聊天"——你说"开灯",它顶多回你一句"好的,已为你开灯",灯却纹丝不动。它说了,但它没做。

这一节要补上的,正是从"说"到"做"的那一步。你对桌上的板子说"把客厅灯打开",它不是回一句客套话,而是真的让 GPIO 翻高电平、灯亮起来。让大模型不只吐文字、还能伸手去碰你的硬件——这件事的机制,就叫 Function Calling。

读这篇前,你得先跑通过上一节的对话链路:板子能把文字发给模型、能拿回回复。还没搞定的,回去看硬件调用大模型 API,这是这一节的地基。

📌 说明

本节只讲原理与链路,把"模型为什么能调你的函数"这件事讲清楚,配一段示意代码帮你建立直觉。完整可跑的实现——带 HTTPS、JSON 解析、真实的函数注册与调度——见实战里的小智旗舰项目,那是整套工程的事。别指望抄完这一篇就有一个能听话开灯的音箱,那不现实。


什么是 Function Calling:模型多了一个动作

我们先把"大模型只会生成文字"这个印象打破。

你平时见到的大模型,输入一段文字、输出一段文字。Function Calling 给它加了一种新输出:模型可以在该用工具的时候,不直接回答你,而是输出一个"我要调用某个函数、参数是这些"的结构化指令。

打个比方。你问一个人"现在几点了",他有两种反应:一种是凭感觉随口说"大概三点吧"(这是纯生成文字);另一种是抬手看了眼手表再告诉你(这是去调用了一个工具)。Function Calling 就是给模型装上了"抬手看表"的能力——遇到它自己答不准、或者本就该去操作点什么的请求,它会选择调用一个外部函数,而不是硬编一个答案。

关键要理解清楚:模型本身并不执行函数。 它做的只是一个决策——"根据这句话,我判断应该调 turn_on_led 这个函数,参数 room客厅"。它把这个决策以结构化的形式(通常是一段 JSON)吐出来。真正去执行的,是你的代码。

所以 Function Calling 的本质是一次分工

  • 模型负责"理解 + 选择":听懂这句模糊的人话,判断该调哪个函数、参数怎么填。这是它最擅长的。
  • 你的代码负责"执行":拿到模型选好的函数名和参数,老老实实去跑对应的硬件动作。这是它碰不到、也不该碰的。

这个分工想明白了,你就抓住了这一节的全部。


在硬件上,这条链路怎么走

把上面的分工落到 ESP32 上,一次"说话 → 动手"的完整过程是这样四步:

第一步:把你的硬件动作"描述"给模型

模型不会凭空知道你的板子能干嘛。你得先告诉它:我这儿有几个函数可以调,每个叫什么、是干嘛的、要哪些参数。这份"工具清单"通常随每次请求一起发给模型——比如 turn_on_led(开某个灯)、read_temp(读温度)、set_fan(调风扇档位)。

描述得越清楚,模型选得越准。"turn_on_led:打开指定房间的灯,参数 room 是房间名"——这一句话,就是模型判断的全部依据。

第二步:模型听懂人话,选出函数和参数

你说"客厅有点热"。模型读到这句,再对照你给的工具清单,做出判断:这是要调风扇,于是输出一段结构化指令——set_fan,参数 level: 2。注意,"有点热"这种模糊表达对应到"风扇开 2 档",正是模型的价值所在:它替你做了从自然语言到具体指令的翻译

第三步:ESP32 端执行对应动作

你的代码收到模型这段指令,解析出函数名 set_fan 和参数,然后在本地真正执行——驱动对应 GPIO、调 PWM、控制继电器。风扇转起来了。这一步全是你熟悉的硬件控制,和点 LED、控电机没本质区别,只不过"什么时候执行、用什么参数"是模型刚替你定的。

第四步:把结果回传给模型

动作做完,你把结果再发回给模型——"风扇已开到 2 档"或者"读到温度 28.5℃"。模型拿到这个结果,才能给你一句像样的收尾回复:"好的,风扇开到 2 档了",或者基于读数继续推理。

这一步常被新手漏掉,但它很重要:没有结果回传,模型就不知道动作成没成、读到的值是多少,对话就断了。调用 → 执行 → 回传,这个回路闭上,模型才真正"知道"它的手伸出去之后发生了什么。

四步连起来:描述工具 → 模型选 → 你执行 → 回传结果。和上一节纯对话的链路相比,多出来的就是"模型不再只生成文字,而是生成一个动作指令;你的硬件接住这个指令去动手"。


一段示意代码:把动作交给模型选

下面这段帮你建立直觉。它定义了两个硬件动作,把它们描述给模型,由模型决定调哪个。这是示意,不是成品——真实实现要带 HTTPS、要解析模型返回的 JSON 结构、要对齐你的接口字段,完整可跑的版本见小智旗舰项目。这里我们只看"思路怎么搭"。

// === 示意代码:让模型在两个硬件动作里选一个去执行 ===
// 真实实现需带 HTTPS / 真实 JSON 解析 / 接口字段对齐,详见小智旗舰项目

// 1) 先定义你的硬件动作(这些是真实的本地函数)
String turn_on_led(int room) {
  digitalWrite(LED_PINS[room], HIGH);     // 真去开灯
  return "已打开 " + ROOM_NAMES[room] + " 的灯";
}
String read_temp() {
  float t = readTemperature();            // 真去读传感器
  return "当前温度 " + String(t) + " 摄氏度";
}

// 2) 把这份"工具清单"描述给模型(随请求发过去)。
//    字段名、格式要对齐你的接口文档,这里只示意"描述什么"
String toolsSpec =
  "[{name:turn_on_led, desc:打开指定房间的灯, args:{room:房间编号}},"
  " {name:read_temp,   desc:读取当前温度,    args:{}}]";

// 3) 把用户的话 + 工具清单发给模型,模型返回"要调哪个 + 参数"
//    注意:模型返回的是一段 JSON 指令,不是直接的答案
//    parseChoice() 在真实代码里要做严谨的 JSON 解析,这里只示意
ToolCall call = parseChoice( ask(userText, toolsSpec) );

// 4) 你的代码按模型的选择,分发到对应函数执行
String result;
if (call.name == "turn_on_led")  result = turn_on_led(call.args["room"]);
else if (call.name == "read_temp") result = read_temp();
else result = "模型选了一个我没注册的函数:" + call.name;  // 防幻觉,见下文

// 5) 把执行结果回传给模型,让它生成给用户的最终回复
String finalReply = ask("函数执行结果:" + result, "");

读这段时,盯住一件事:"该调哪个函数"这个判断,全程不是你写 if 判断用户说了什么,而是模型替你做的。 你只负责把动作描述清楚、把模型选好的结果接住去执行。这就是 Function Calling 和老办法最根本的不同。

💡 提示

调试时先别接真硬件。把第 1 步那两个函数改成只 Serial.println("假装开灯了"),先验证"模型能不能在该开灯时选 turn_on_led、该读温度时选 read_temp"。把"模型选对函数"这一环单独跑通,再换成真正驱动 GPIO。一次只调一个环节,出了问题才知道是模型选错了还是你执行错了。


和"纯 prompt 硬解析"比,好在哪

你可能会想:不用这套机制,我自己解析模型的回复不行吗?让模型回一句话,我在代码里 if (reply.indexOf("开灯") >= 0) 不就完了?

能跑,但很脆。这种"纯 prompt 硬解析"的做法,问题在你预设不完用户的说法:

  • 用户说"把灯打开"你能匹配,可他说"屋里有点暗"呢?说"亮一点"呢?说"帮我开下客厅那个"呢?你得一种种 indexOf 去猜,永远漏。
  • 模型回复的措辞稍变,你的字符串匹配就崩——它今天回"已开灯",明天回"灯已经为你点亮",你的 indexOf("开灯") 就抓不到了。
  • 参数更麻烦。"开 2 档风扇"里那个"2",你得自己从一句自然语言里抠数字,稍微换个说法就抠不准。

Function Calling 把这些全交给了模型:理解用户多变的说法、判断该调哪个动作、抽出结构化的参数——这正是大模型最强的地方,而恰恰是字符串匹配最弱的地方。你拿到的不再是一句需要你猜的自然语言,而是一个干净的 {name, args} 结构,照着分发执行就行。

一句话:别让你的 if 去理解人话,让模型去理解,你的代码只管执行。 这是工程上更稳、也更可扩展的分工——加一个新动作,只需多描述一个工具,不用再去补一堆字符串匹配。


为什么它是"AI Agent 控硬件"的基础

把视角抬高一点。当模型能"决定调哪个函数",它就不再是一个被动应答的聊天框,而是一个能自己规划、自己行动的执行体——这就是 Agent 的雏形。

想象你说"屋里太闷了,处理一下"。一个 Agent 会:先调 read_temp 看看多热 → 判断要不要开风扇 → 调 set_fan 开到合适档位 → 再回传告诉你它干了啥。多步、自主、按结果决定下一步——这一连串动作的每一环,底层都是一次 Function Calling。没有"模型能调函数"这个能力,Agent 控硬件就无从谈起。

但这里有个工程现实:你不可能把家里每个设备的函数都塞进一个 ESP32、再把这一大坨描述每次都发给模型。设备一多,就需要一套标准的方式,让模型能统一地"发现有哪些工具、怎么调它们"——这就是 MCP(Model Context Protocol)要解决的事。Function Calling 是"模型能调一个函数"的底层机制,MCP 是把这个机制标准化、规模化到"模型能调一整片设备能力"的协议。 你先把这一节的单个函数调用想透,下一节的 MCP 才好懂。


故障 / 避坑:模型不是永远可靠的

让模型来"做决策",就得接受它偶尔会决策错。这几类翻车你得提前防:

现象 最可能的原因 怎么办
模型该调函数时却只回了句文字 工具描述太模糊,或没在请求里带上工具清单 把每个函数的 desc 和参数写清楚;确认每次请求都附了工具清单
模型选了个你没注册的函数 幻觉——它"编"了个不存在的函数名 执行前先校验函数名在白名单里,不在就拒绝(见示意代码第 4 步)
参数对不上 / 类型错 模型填的参数格式和你预期的不一致 执行前校验参数:room 是不是合法编号、level 是不是在 0~3 范围
同一句话有时调对有时调错 模型本身有随机性 关键动作把 temperature 调低;高风险动作加一道确认再执行
模型反复纠结、不下决定 工具职责重叠、描述含糊 让每个函数职责单一、互不重叠,模型才好选

最核心的一条原则:永远不要无条件信任模型选的东西。 模型的输出对你的代码来说是"外部不可信输入",跟用户从网页表单提交的数据没区别。函数名要过白名单、参数要校验范围,再去执行。

🚧 避坑

最危险的不是模型选错无害的动作(顶多灯没开),而是它幻觉调用一个高风险动作——比如你注册了 unlock_door(开门锁),模型在某句模糊指令下误调了它。凡是涉及安全、不可逆、或会动真实世界物理状态的动作(开锁、加热、放水、解锁电机),都必须在执行前加一道独立的人工确认或硬性条件,绝不能让模型一个决策就直接放行。把模型当成一个聪明但偶尔糊涂的助手,关键操作永远留一道你自己的闸。


动手挑战

别只看,动手把"模型选函数"这一环跑通:

  1. 定义两个动作让模型选:写两个本地函数——一个 turn_on_led(room)、一个 read_temp(),先都只用 Serial.println 打印"假装执行了"。把这两个动作的描述发给模型,然后分别对它说"把卧室灯打开"和"现在多少度",看串口里它是不是分别选中了 turn_on_ledread_temp、参数填得对不对。
  2. 加一道白名单校验:故意把工具描述写得诱导一点,看模型会不会幻觉出第三个函数名;在执行前加上"函数名不在白名单就拒绝"的判断,确认你的代码挡住了它。

跑通这两步,你就亲手验证了"模型替你做决策、你的代码安全地执行"这套分工。卡住了?把你的工具描述、模型返回的原始 JSON、还有串口输出一起发给 AI,让它帮你看模型为什么选成这样。


小结 · 你现在掌握了什么

  • 你能说清 Function Calling 是什么:模型多了一种输出——不直接答你,而是决定调哪个函数、传什么参数,执行交给你的代码。
  • 你看懂了硬件上的控制链路——描述工具 → 模型选 → ESP32 执行 → 回传结果——以及每一步谁负责什么。
  • 你知道了它比"纯 prompt 硬解析"稳在哪:让模型去理解多变的人话和抽参数,你的代码只管照结构执行。
  • 你明白了它是 AI Agent 控硬件的底层机制,也知道了模型会幻觉、会选错,关键动作必须加白名单和人工确认这道闸。

从"会聊天"到"会动手",这就是 AI 硬件真正的分水岭。一旦你的硬件能被模型调用,它就从一个应答的玩具,变成了一个能行动的执行体。

下一步:单个函数调用想透了,去看怎么把它标准化、规模化——MCP:让模型统一发现并调用一整片设备能力。再配合 MQTT 消息总线 把多个设备串成网,你就有了一套"模型指挥、设备协同"的雏形。想看工业级的完整落地,去 L4 这一级实战项目里逐行拆小智旗舰。

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明