想象你走进一家餐厅,新来的服务员小张笑容满面,能跟你聊天气、聊菜品的典故,可当你说“来一碗牛肉面,不要辣”时,他只把这句话原封不动复述一遍,然后站在原地。半小时过去了,面没来。问题出在哪?小张只会“说话”,不会“下单”——他没能把你的需求写成一张标准订单送到后厨。对小语言模型(SLM)来说,函数调用就是“下单”这项关键技能。本文用“服务员学下单”这条主线,讲清楚如何让 SLM 学会调用工具:先读懂“菜单”(函数描述格式),再学会“写订单”(调用解析逻辑),经过“师徒带教”(训练数据构造)和“上岗集训”(微调策略),最终能“统筹宴席”(多工具编排),并在小小的“街边厨房”(端侧设备)里游刃有余。
学会函数调用,SLM 才能从“会聊天”升级为“能办事”。这是端侧 Agent 工程师的核心技能,也是小模型在手机、车载、家居等场景真正落地的关键一环。
可视化演示
动态展示2PC协议的Prepare和Commit两阶段执行流程,呈现协调者与参与者之间的投票与提交消息交互机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
2PC - PREPARE阶段 协调者发送PREPARE消息给所有参与者 参与者回复YES/NO投票 C→P:PREPARE 阶段1准备投票
函数调用概述
餐厅里新来的服务员小张,口才极好,客人说“来碗牛肉面,不要辣”,他能一字不差地复述,然后站在原地。客人等了半小时,面没来。问题出在哪?小张只会“说话”,不会“下单”——他没有把需求写成一张标准订单送到后厨。对 SLM 来说,函数调用就是“下单”这项技能。没有这项技能,模型再能聊,也改变不了现实世界的一分一毫。
纯文本聊天就像复述,能理解却无法行动。要让 SLM 成为真正的 Agent(智能体),它必须学会输出一种结构化指令,也就是函数调用:一个函数名加一组参数。例如用户问“北京天气怎么样”,模型输出 get_weather("北京"),系统便调用真实天气 API 拿回数据。这样一来,模型的能力就从“嘴甜”升级为“手勤”,从聊天机器人变成能办事的助手。
那么,函数调用在数学上是什么?本质上是一个条件概率问题:给定用户请求 $x$ 和当前可用的函数列表 $F$,模型选择调用第 $i$ 个函数的概率:
这个概率越大,说明模型越擅长“在合适的时机点合适的菜”。训练函数调用能力,就是要最大化这个概率——让模型在“该点菜”时准确报出菜名和口味,在“不用点菜”时安静地陪聊。
1. 条件概率符号:$P(f_i \mid x, F)$ 表示在“用户请求为 $x$、函数列表为 $F$”这两个条件下,模型认为应该调用第 $i$ 个函数的概率。
2. 用户请求:$x$ 是用户输入的自然语言,比如“帮我查一下北京的天气”,相当于客人说的话。
3. 函数列表:$F$ 是所有可用工具的集合,相当于餐厅的菜单,列出了后厨能做的所有菜。
4. 选择含义:$f_i$ 是第 $i$ 个函数,$P$ 值最高的那个函数就是模型准备“下单”的目标。
就像新服务员上岗必须先认识后厨有哪些灶台、能做什么菜,SLM 也要先“知道”有哪些工具、各自负责什么。认识工具、理解需求、生成标准订单,这就是函数调用能力的三板斧。接下来我们逐个拆解,先从最重要的“菜单”讲起。
函数描述格式
小张上岗第一天,老板丢给他一本厚厚的菜单。如果菜单上只写“菜一”“菜二”,不写口味、不写食材,小张永远不知道“菜一”是红烧肉还是青菜豆腐。菜单必须写明:菜名、做法、用什么料。函数的“菜单”也是同理——模型只能根据你给它的描述来理解每个工具是干什么的。
在工程实践中,我们通常用 JSON Schema 来写这份“菜单”。每个函数包含三要素:函数名 name、用途描述 description、参数定义 parameters。比如 get_weather 的 description 写“查询指定城市的实时天气”,模型看到“北京天气怎么样”,自然就联想到它;而 set_alarm 的描述写“设定某个时间的提醒闹钟”,模型听到“明早 7 点叫我”就会找到它。
形式上,整个函数列表是一个结构化集合,每个函数又是一个包含名字、描述和参数的三元组:
其中 $\mathrm{params}_i$ 内部还要用 JSON Schema 定义每个参数的类型、是否必填、取值范围。写 description 时要用“模型的话”:写清楚触发场景和输入含义,少用“等等”“之类”这种模糊词,否则模型会在两个相似工具之间摇摆,就像客人说“来份荤菜”,服务员不知道是点红烧肉还是点烤鸭。
1. 函数集合:$F$ 是所有可用工具的清单,集合里装着 $n$ 个函数,相当于餐厅里可供点选的 $n$ 道菜。
2. 单个函数:$f_i$ 是第 $i$ 个工具,每个工具对应一段可执行的真实代码,就像一道菜对应一个灶台流程。
3. 函数名:$\mathrm{name}_i$ 是函数名,模型生成的调用里必须用它来指路,相当于点菜单上的菜名编号。
4. 描述与参数:$\mathrm{desc}_i$ 说明工具的使用场景,$\mathrm{params}_i$ 用 JSON Schema 约束参数类型与必填项。
一份好菜单,是服务员准确点单的前提;一份好的函数描述,是模型准确调用的前提。菜单写得好,前台和后厨都省心。写完菜单,下一步就是教模型读懂客人的话、写出标准订单——这就是调用解析逻辑要解决的问题。
调用解析逻辑
小张记性不错,但习惯用自己发明的“密语”记单:“那碗面,那个啥,辣的不要,加个蛋。”后厨看了直翻白眼。于是餐厅规定:所有订单必须写成统一格式——菜名、辣度、数量,逐项填写。对模型输出也是如此:如果模型随意发挥,系统就没法稳定地执行。所以我们要训练模型输出一种“标准订单”,再用解析器去读懂它。
SLM 输出的是一段文本,比如 <tool_call>{"name":"get_weather","arguments":{"city":"北京"}}</tool_call>。解析器要做的,就像收银员核对订单:先定位 <tool_call> 标签包围的区域,再从中抽取函数名和参数对象,最后把参数传给对应的工具执行。这一步看似简单,却是工程里最容易翻车的地方——模型偶尔会多打一个空格、把一个引号写弯、甚至参数值里混进多余字符。
从概率角度看,模型生成调用文本的过程,就是在所有可能的输出序列中挑选概率最高的那个:
在实际解析时,我们要做“格式规范化”:容忍多余空白,尝试修复不完整的 JSON,对参数值做类型强制转换(比如把数字字符串转成整数),校验必填参数是否齐全。如果解析失败,系统应返回“无法解析”并触发重试或询问用户,而不是直接崩溃——就像收银员看不清订单时会再问客人一遍,而不是随便报个菜名进后厨。
1. 候选输出空间:$\mathcal{Y}$ 是所有可能输出文本的集合,范围极大,包含普通回答和函数调用。
2. 模型概率:$P_\theta(y \mid x, F)$ 中的 $P_\theta$ 表示模型在参数 $\theta$ 下生成文本 $y$ 的概率。
3. 条件输入:$x$ 是用户请求,$F$ 是函数列表,两者共同决定输出分布,就像客人和菜单共同决定订单内容。
4. 最优输出:$\hat{y}$ 是概率最高的输出序列,也就是模型最终“写”下的标准订单。
一张写错格式的订单会毁掉一桌好菜;一段格式不规范的输出也会让 Agent 空转甚至报错。因此训练时我们会刻意把模型“调教”成稳定输出标准格式的服务员。而“调教”需要的教材,就是我们下一节要讲的训练数据。
训练数据构造
要让小张学会写标准订单,光背菜谱不够,还得给他看大量“客人说了什么 → 服务员写了什么”的师徒案例。这些案例就是训练数据。案例越真实,小张面对陌生客人时越从容;案例里有各种刁钻场景,他才不会在高峰期手忙脚乱。
每组训练数据包含三部分:用户请求 $x$、可用的函数列表 $F$、标准答案 $y$。注意 $y$ 有两种形态:需要工具时,它是一个标准函数调用;不需要工具时(比如用户只是问好),它就是一句普通回复。数据来源主要有三个:清洗后的真实用户日志、让大模型(如 GPT 系列)蒸馏生成的样例、以及人工编写的高质量模板。三者混合,既保证真实度,又保证多样性。
训练时,我们用监督学习的方式最大化标准答案的概率,也就是最小化交叉熵损失:
这里特别要强调负样本的设计。如果训练集里每个问题都调用工具,模型就会变成“乱下单”的服务员——用户问“你好”,它也调个搜索工具。所以必须混入大量“不需要调用工具”的样本,标准答案就是自然语言回复,让模型学会判断边界:能直接答的就直接答,答不了才下单。
1. 训练集规模:$N$ 是训练样本总数,样本由“用户请求、函数列表、标准答案”三元组构成。
2. 单样本对数似然:$\log P_\theta(y_i \mid x_i, F_i)$ 量化模型对第 $i$ 个样本答对的自信程度。
3. 平均求和:$\frac{1}{N} \sum_{i=1}^{N}$ 把所有样本的损失求平均,让每个样本公平贡献。
4. 负号求最小:$-$ 号把“最大化概率”翻转为“最小化损失”,方便梯度下降优化。
师徒案例越真实、越多样,小张面对陌生客人时越从容。数据质量直接决定模型调用工具有多“靠谱”。教材备齐之后,接下来就是正式的“上岗集训”——微调。
微调训练策略
小张在厨师学校已经学会了听说读写,上岗集训不需要把学校重读一遍,只需要重点练两件事:认菜单、写订单。对 SLM 来说,这就是微调——在预训练的基础上,用小而精的数据“集训”函数调用技能。全量微调要更新所有参数,对端侧小模型来说成本太高,于是我们常用 LoRA。
LoRA 的核心思想是“冻结旧知识,只学新增量”。具体做法是:把原始权重矩阵 $W_0$ 冻住不动,在旁边加一个低秩的增量矩阵 $\Delta W$,训练时只更新这个增量。这就像老服务员已经会说话、会走路,集训时只需要换一本点单手册,不用重新培训整个人。
数学上,LoRA 把增量分解成两个小矩阵的乘积:
参数量从 $d \times d$ 骤降到 $2 \times r \times d$,训练开销大幅降低,普通消费级显卡甚至 CPU 都能跑。推理时把 $BA$ 合并回 $W_0$,不增加任何延迟。除 LoRA 外,解码参数也很关键:函数调用追求确定性,温度 $\tau$ 应设低(0.1~0.3),避免模型同一个订单每次写出来的格式都不一样。
1. 原始权重:$W_0$ 是预训练得到的基础权重矩阵,训练中保持冻结,相当于服务员原有的语言功底。
2. 低秩增量:$\Delta W = BA$ 是两个小矩阵相乘的结果,代表本次集训学到的“新技能”。
3. 维度约束:$A \in \mathbb{R}^{r \times d}$、$B \in \mathbb{R}^{d \times r}$,$r \ll d$ 说明增量矩阵非常“瘦”。
4. 合并推理:$W' = W_0 + BA$,推理时先合并再使用,速度与原始模型完全一致。
集训结束,小张出师:认得菜单、写得规范、不乱点。LoRA 让这场集训又快又省,小本经营的餐厅(端侧设备)也请得起。不过,真正的大场面还在后面——客人办婚宴时,要学会统筹多个档口。
多工具编排
客人要办婚宴,需求一串:先看天气定场地,再订蛋糕,最后设提醒。小张不可能一个灶台跑到底,他要统筹凉菜、热菜、甜品多个档口,还得讲究先后顺序,遇到某道菜卖完了,要立刻换个备选方案。真实世界里的 Agent 也是如此——很少有任务只调一个工具就能完成。
多工具编排的第一步是“路由”:模型根据用户意图,把请求分发给正确的工具。第二步是“多步调用”:把上一步的结果作为下一步的输入,形成一条调用链。比如“导航到最近的充电站,顺便看看那边天气”就需要先调定位工具,再调导航工具和天气工具。模型要学会安排顺序,而不是一股脑全调一遍。
从概率上,一整条调用链可以看成每一步条件概率的乘积:
工程上还要重点处理异常:工具返回错误时先重试一次;重试失败就切换到功能类似的备用工具;实在不行就向用户说明并请求补充信息。同时要给整条链设定步数上限和超时时间,防止 Agent 陷入“死循环”——就像服务员不能因为甜品档口排队,就让整场婚宴的客人都干等着。
1. 调用序列:$(c_1, c_2, \dots, c_k)$ 是一个 Agent 在一轮任务中依次发起的 $k$ 次工具调用。
2. 链式法则:$\prod_{j=1}^{k}$ 表示把每一步的条件概率相乘,得到整条调用链出现的概率。
3. 历史依赖:$P(c_j \mid x, c_{<j}, F)$ 说明第 $j$ 步的选择依赖于之前所有调用的结果。
4. 整体决策:整条链的概率越高,说明编排越合理,模型越有可能成功完成任务。
优秀的服务员不是闷头跑腿,而是眼里有整场宴席;优秀的 Agent 也不只会调一个工具,而是懂得统筹规划。编排能力,决定了它能接多大的“活”。宴席方案定好了,最后还得看厨房装不装得下——这就是端侧落地的问题。
端侧落地优化
婚宴方案再完美,也得看厨房实力。端侧设备就像一个小厨房:灶台少、储物柜小、出菜要快。SLM 要在这里安家,必须精打细算。好在有三个优化方向:量化、延迟优化、离线部署。
量化是最立竿见影的手段。把权重从 FP16(16 位浮点)压缩成 INT8 甚至 INT4(整数),就像把大块食材提前切成小块,更省空间。延迟优化则靠缓存和预加载:重复的请求缓存结果,KV Cache 复用,模型提前预热,相当于后厨提前备好半成品,高峰期才能快速出菜。离线部署让模型和工具全部在本地运行,数据不出设备,既保护隐私又避免网络抖动。
量化带来的内存节省可以用一个简单的公式估算:
以 1B 参数的模型为例:FP16 存储约需 2GB,量化到 INT4 只需约 500MB,内存直降 75%,手机、耳机、车载盒子都能跑。需要注意的是,量化可能带来少量精度损失,通常要在量化后用小规模函数调用数据做校准,把损失补回来——就像后厨换了新灶台,要先试炒几道菜调整火候。
1. 参数量:$P_{\text{params}}$ 是模型的总参数量,决定了内存占用的基本盘。
2. 原始位宽:$b_0$ 是量化前每个权重占用的比特数,通常为 16(FP16)。
3. 比例缩减:$\frac{b_q}{b_0}$ 是量化后权重体积占原始体积的比例,越小越省空间。
4. 节省总量:$M_{\text{saved}}$ 是减少的内存字节数,量化到 INT4 时约为原始内存的 75%。
小厨房也能端出满汉全席——只要菜谱精简、备菜到位、流程顺畅。端侧 SLM 加上函数调用,就是那个在手机、手表、车上随时待命的“贴身服务员”:反应快、不联网、懂规矩、能办事。
AI 应用场景
端侧语音助手
阶段:模型微调与端侧推理部署
解决的问题:传统语音助手依赖云端解析意图,延迟高、断网即失灵,而且用户的日程、位置等隐私数据要上传服务器。通过函数调用微调,SLM 在本地直接解析用户指令并生成结构化调用,由系统调用本地能力(设置闹钟、读取日程、控制智能家居),全程数据不出设备,响应更快、隐私更安全。
具体案例:用户对手机说“明早七点提醒我开会”,端侧 SLM 输出 <tool_call>{"name":"set_alarm","arguments":{"time":"07:00","note":"开会"}}</tool_call>,解析器抽出参数后写入本地闹钟应用。整个过程就像服务员在店内下单,不用把客人需求喊到店外。
智能座舱车载助手
阶段:多工具编排与系统工程
解决的问题:车载环境网络不稳定,隧道、山区经常断网,云端 Agent 不可靠。车载助手需要 SLM 在本地编排多个车控工具,把“打开空调、导航到最近的充电站”这类复合指令拆成多个函数调用依次执行。
具体案例:司机说“太热了,顺便找地方充电”,SLM 先调用 set_ac(26) 调节空调,再调用 search_poi("充电站") 搜索位置,最后调用 navigate(poi) 发起导航。三个工具像三个档口依次出菜,环环相扣。
工业巡检助手
阶段:工具函数设计与离线部署
解决的问题:工厂设备间网络受限,巡检人员需要随身助手实时读取传感器数据并对照安全阈值。SLM 通过函数调用读取本地传感器接口,返回结构化结果,还能在多步巡检中把上一步的读数传给下一步的比对工具。
具体案例:巡检员问“三号机组温度正常吗”,SLM 调用 read_sensor("temp") 拿到数值,再调用 compare_threshold(65) 判断是否超限,最终回复“当前 58 度,正常”。模型和工具全部离线部署在手持终端上,断网也能工作。
Python 演示
import jsonimport re TOOLS = [ { "name": "get_weather", "description": "查询指定城市的实时天气", "parameters": {"city": {"type": "string", "required": True}}, }, { "name": "set_alarm", "description": "设定某个时间的提醒闹钟", "parameters": { "time": {"type": "string", "required": True}, "note": {"type": "string"}, }, }, { "name": "search_web", "description": "搜索互联网上的最新信息", "parameters": {"query": {"type": "string", "required": True}}, },] CALL_RE = re.compile(r"<tool_call>\s*(.*?)\s*</tool_call>", re.S) def parse_tool_call(text): """从模型输出中提取函数名与参数,返回 dict 或 None""" m = CALL_RE.search(text) if not m: return None try: data = json.loads(m.group(1)) return {"name": data["name"], "arguments": data.get("arguments", {})} except (json.JSONDecodeError, KeyError): return None def execute_tool(call): """根据函数名分发到对应的具体实现""" name = call["name"] args = call["arguments"] if name == "get_weather": return {"result": "{} 晴 气温 25 度".format(args["city"])} if name == "set_alarm": return {"result": "已设定 {} 闹钟 {}".format(args["time"], args.get("note", ""))} if name == "search_web": return {"result": "找到关于 {} 的最新资讯 3 条".format(args["query"])} return {"error": "未知工具 {}".format(name)} def mock_slm(user_msg): """模拟微调后 SLM 的输出,真实场景中由模型推理生成""" if "天气" in user_msg: return '<tool_call>{"name": "get_weather", "arguments": {"city": "北京"}}</tool_call>' if "提醒" in user_msg or "闹钟" in user_msg: return '<tool_call>{"name": "set_alarm", "arguments": {"time": "07:00", "note": "起床"}}</tool_call>' return '<tool_call>{"name": "search_web", "arguments": {"query": "端侧大模型"}}</tool_call>' def agent_loop(user_msg): """一次完整的工具调用流程:生成 -> 解析 -> 执行 -> 返回""" raw = mock_slm(user_msg) call = parse_tool_call(raw) if call is None: return "模型未生成合法调用,直接回复:我无法完成这个任务。" result = execute_tool(call) if "error" in result: return "调用失败:{}".format(result["error"]) return "已调用 {},{}".format(call["name"], result["result"]) if __name__ == "__main__": for msg in ["帮我查一下北京的天气", "明早七点提醒我起床"]: print("用户:{}".format(msg)) print("助手:{}".format(agent_loop(msg)))
CALL_RE = re.compile(r"<tool_call>\s*(.*?)\s*</tool_call>", re.S)用正则定位<tool_call>标签包裹的调用区域,相当于收银员先找到订单上“点菜栏”在哪。data = json.loads(m.group(1))把调用文本解析成 Python 字典,这一步必须容错,捕获 JSON 解析异常。data.get("arguments", {})参数缺省时给空字典,避免取不到参数直接崩溃,体现“订单少写一行也别掀桌”的容错思想。def execute_tool(call)工具分发器,按函数名把请求送到对应的“档口”,对应真实系统的 API 路由。raw = mock_slm(user_msg)模拟微调后 SLM 的输出,真实场景中这一行替换为模型推理代码。if call is None:解析失败时的兜底分支,直接回复用户而不是抛异常,就像看不清订单时礼貌地再问一遍。if __name__ == "__main__":程序入口,遍历两条测试指令,打印完整的“用户 → 助手”对话链路。
常见误区
误区我以为函数调用就像搜索引擎,模型在函数列表里找一条最像的现成答案,然后把对应代码拿过来执行。既然函数都定义好了,模型只是“挑一个”而已。
正解实际上,函数调用是生成式的。模型不是“挑选”现成文本,而是逐字逐词地生成一段结构化文本(函数名和参数)。就像服务员不是从柜台上拿起一张写好的订单,而是根据客人的话现场写出订单内容。一个函数可能有无数种参数组合,只有生成式模型才能处理这种开放性。
为何常见:因为人类习惯“查表”思维,总觉得函数是固定的、调用就是匹配。但神经网络天生是概率生成器,理解这一点才能想明白:为什么训练目标是最大化生成概率,而不是优化检索排序。
误区函数能跑就行,name 和 parameters 写清楚就够了,description 多写少写无所谓,反正模型是“智能”的,应该能自己理解每个函数是干嘛的。
正解描述词恰恰是函数调用准确率的“命门”。SLM 参数量小,语义理解能力有限,它判断“该调哪个函数”几乎完全依赖 description。写得含糊,模型就会在两个相似工具之间摇摆;写清触发场景(比如“当用户询问天气时”),模型才能精准点单。好的描述就像菜单上“微辣”“多葱”的标注,直接影响出菜对不对。
为何常见:因为人类看函数名 get_weather 就能猜出功能,下意识觉得模型也能。但小模型没有这种“常识联想”,它只能依靠你写在 description 里的显式信息。
误区我写好了解析代码,要求模型每次输出都严格合法的 JSON。一旦解析失败,就认为是模型太笨,反复加大训练数据量。
正解SLM 生成的文本天然带噪声,完美 JSON 是理想不是常态。工程上必须做容错解析:容忍多余空格、修复引号、类型强制转换、缺字段时给默认值。这就像后厨不会因为客人写字潦草就把单子扔回去,而是会核对、询问、尽量理解。解析器设计得够“皮实”,远比苛求模型完美更实际。
为何常见:开发者习惯了机器之间的严格协议,容易把“模型输出”也当成“机器输出”。但模型是概率采样器,输出天然有随机性,必须用“人读手写单”的心态去设计解析层。
误区微调函数调用能力,数据当然是越多越好。我准备了十万条调用样本,全部灌进去训练,模型应该会更强。
正解数据质量比数量重要得多。十万条相似样本可能不如一万条多样样本:重复的“查天气”模板不但没帮助,还会让模型对“查询”类请求过度敏感。更重要的是负样本——如果所有样本都要求调用工具,模型就学不会“什么时候不调用”。正确做法是控制正负样本比例(通常 1:1 到 3:1),并保证场景、句式、参数组合的多样性。
为何常见:大模型时代“数据越多越好”的说法深入人心,但对微调而言,目标功能范围很窄,盲目堆量只会稀释有效信息,还挤占了负样本的空间。
误区既然大模型用提示词写清函数格式就能调用工具,小模型照葫芦画瓢应该也行?我写了个超长的 system prompt,把函数定义全塞进去,结果 SLM 输出格式还是飘忽不定。
正解提示词对 SLM 的约束力远弱于大模型。SLM 参数量小,指令遵循能力有限,很难仅靠提示词稳定输出 <tool_call> 这种严格格式。微调才是把“输出规范”内化到权重里的可靠手段——就像光靠口头叮嘱“记得写标准订单”,新服务员记不住,必须反复演练形成肌肉记忆。实际工程中,微调后再加提示词做引导,效果最佳。
为何常见:因为大模型(GPT、Claude)的提示词函数调用体验太好,让人误以为所有模型都具备同等的指令遵循能力,忽略了小模型的能力瓶颈。
面试题
Q1. 函数调用的本质是什么?
- A.从预定义答案库中检索最佳文本
- B.用条件语言模型生成结构化调用文本← 正确答案
- C.通过搜索引擎定位工具所在位置
- D.对用户文本做关键词规则匹配
答案:B。函数调用是生成式任务,模型在给定用户请求和函数列表的条件下,逐词生成函数名和参数,而不是检索现成答案或做关键词匹配。服务员是“现写订单”,不是“背答案”。
Q2. 描述函数时最常用的标准格式是?
- A.JSON Schema 结构← 正确答案
- B.XML 标签嵌套结构
- C.YAML 配置文件结构
- D.CSV 表格行列结构
答案:A。函数描述(名称、说明、参数类型与约束)工程上统一用 JSON Schema 表达,解析方便且生态成熟。XML、YAML、CSV 虽然也能存数据,但不是函数描述的事实标准,就像菜单统一用文字标注而不用表格画菜。
Q3. 解析模型输出 <tool_call>{"name":"get_weather","arguments":{"city":"北京"}}</tool_call> 时,第一步应该做什么?
- A.直接调用外部工具执行
- B.把调用文本原样返回给用户
- C.抽取函数名与参数并校验← 正确答案
- D.丢弃输出重新生成一次
答案:C。正确的流程是先用正则或解析器定位调用区域,抽取函数名和参数对象并做校验,全部通过后才交给工具执行。直接执行或原样返回都跳过了关键的“收银核对”环节,遇到格式瑕疵就会出错。
Q4. 训练数据中加入“不需要调用工具”的负样本,其主要作用是?
- A.显著减小训练集的数据规模
- B.让模型学会区分何时不该调用← 正确答案
- C.提高模型参数的稀疏程度
- D.压缩端侧模型的存储体积
答案:B。负样本教会模型判断边界:能直接回答的问题就正常回复,不要动不动“下单”。没有负样本,模型会变成乱调用工具的服务员,问“你好”也去调搜索接口。它不影响参数稀疏性,也不压缩模型体积。
Q5. LoRA 微调的核心思想是?
- A.用梯度更新全部原始参数
- B.在每次推理前修改输入文本
- C.冻结原权重并训练低秩增量← 正确答案
- D.用更大规模语料重新预训练
答案:C。LoRA 冻结预训练权重 $W_0$,只训练低秩矩阵 $A$、$B$,用 $W' = W_0 + BA$ 得到微调结果。它不更新全部参数,不修改输入,也不做预训练,而是像在旧书上贴便签一样做“小修补”。
Q6. 端侧部署 SLM 时,降低模型内存占用最有效的手段是?
- A.增大推理时的批处理大小
- B.提高解码时的温度参数数值
- C.增加训练阶段的数据增强次数
- D.把权重量化到 INT8 或 INT4← 正确答案
答案:D。量化把每个权重从 16 位降到 8 位或 4 位,内存直接缩减一半以上,是端侧部署最立竿见影的手段。批处理、温度、数据增强都不改变模型权重本身,对内存占用几乎没有帮助。
Q7. 函数描述中 description 写得含糊不清时,最可能造成的后果是?
- A.模型参数数量快速下降
- B.模型选错工具或误填参数← 正确答案
- C.端侧推理延迟明显缩短
- D.模型在加载时直接崩溃报错
答案:B。SLM 判断调用哪个工具几乎完全依赖 description。写得不清楚,模型会在相似工具间摇摆,或者把“查天气”误路由到“搜索网页”。描述质量不影响参数量、推理速度和加载过程,只影响“点单准不准”。
Q8. 构造函数调用训练数据时,合理的数据来源包括哪些?
- A.清洗后的真实用户日志← 正确答案
- B.大模型蒸馏生成的样例← 正确答案
- C.人工编写的高质量模板← 正确答案
- D.随机堆砌的无关文本段落
答案:ABC。真实日志保证场景真实性,大模型蒸馏快速扩充覆盖面,人工模板控制格式和多样性,三者结合是业界主流做法。随机无关文本没有“请求-调用”对应关系,对训练毫无帮助,就像给服务员看小说学不会写订单。
Q9. 多工具编排中合理的异常处理手段包括哪些?
- A.失败后重试相同调用← 正确答案
- B.切换到备用等价工具← 正确答案
- C.向用户说明并请求补充← 正确答案
- D.直接静默忽略错误结果
答案:ABC。工具调用失败时,先重试解决偶发错误;仍失败则切换备用工具;都不行就告知用户并请求补充信息。静默忽略错误会让 Agent 假装成功,用户拿到错误结果还以为任务完成了,是工程大忌。
Q10. 以下哪些措施能提高 SLM 函数调用的稳定性?
- A.使用统一的调用输出格式约定← 正确答案
- B.微调时加入格式错误负样本← 正确答案
- C.解码时采用较低的温度参数← 正确答案
- D.删除所有函数描述仅留函数名
答案:ABC。统一输出格式(如 <tool_call> 包裹)让解析器有据可依;格式错误负样本让模型知道“写错订单会被退单”;低温度让生成更确定。删除函数描述等于让服务员对着光秃秃的菜名猜口味,稳定性必然崩盘。