想象你走进一家餐厅,点了一道“宫保鸡丁,不要辣”。店长没有亲自下厨,而是在工单上写下:“炒菜台——宫保鸡丁,免辣”,递给后厨。炒菜台的师傅照单烹饪,出菜后传菜员把菜端回店长面前。店长确认菜品无误,才把菜送到你桌上。你全程只说了一句话,但背后是店长的决策、厨师的执行、传菜员的传递,环环相扣。 大模型的 Function Calling 机制,正是这套“店长派工”流程:大模型是店长,工具函数是后厨的各个工作台。店长不需要自己动手做菜,只需要写一张结构化的“派工单”(输出 JSON 格式的函数调用指令),由应用层(后厨)负责执行,再把结果端回来继续服务。这个能力,让大模型从只能“纸上谈兵”的聊天机器人,升级为真正能查天气、订机票、调数据库、操作系统的 AI 员工。

岗位关联

掌握 Function Calling,你就能让大模型从纸上谈兵升级为真正能查数据、调接口、操作系统的 AI 员工——这是构建 AI Agent 应用不可绕过的地基。

目录
  1. 可视化演示
  2. 概念与价值
  3. 工作原理
  4. 函数定义
  5. 调用流程
  6. 与Agent关系
  7. 实践要点
  8. 局限与挑战
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

\u53ef\u89c6\u5316\u5c55\u793aFunction Calling\u7684\u5b8c\u6574\u8c03\u7528\u6d41\u7a0b\u3001\u53c2\u6570\u7ed3\u6784\u5316\u63d0\u53d6\u4e0eAgent\u5de5\u5177\u94fe\u8c03\u7528\uff0c\u4ea4\u4e92\u5207\u6362\u89c2\u5bdf\u5927\u6a21\u578b\u8fde\u63a5\u5916\u90e8\u5de5\u5177\u4e0eAPI\u7684\u6838\u5fc3\u673a\u5236\u3002

本节配有交互动效(进入视口后自动加载,约 3KB)。

Function Calling\u5b8c\u6574\u6d41\u7a0b \u7528\u6237\u610f\u56fe\u2192\u6a21\u578b\u9009\u62e9\u51fd\u6570\u2192\u53c2\u6570\u63d0\u53d6\u2192\u6267\u884c\u2192\u7ed3\u679c\u8fd4\u56de \u6a21\u578b\u8f93\u51faJSON\u683c\u5f0f\u51fd\u6570\u540d\u4e0e\u53c2\u6570\u503c User \u2192 LLM \u2192 f(args) \u2192 Result \u70b9\u51fb\u5207\u6362\u67e5\u770b\u53c2\u6570\u63d0\u53d6

概念与价值

店长有一本厚菜单,但他自己不做菜。顾客点“宫保鸡丁不要辣”,店长不犹豫——他知道炒菜台能完成,写下工单递过去。传统的聊天机器人就像一位只会口头答应的“假店长”,你说“帮我订张去上海的机票”,它只能回复“好的,请您去航司官网操作”。它没有手,接触不到任何真实系统。Function Calling 的出现,给这位店长装上了与后厨对话的传声筒:让模型输出一份“派工单”,由应用层的代码去真正动手。

在技术层面,Function Calling 是“将自然语言指令映射为结构化函数调用参数”的机制。模型输出的不是自由文本,而是包含函数名和参数值的 JSON 对象。这个 JSON 对象会被应用层解析、校验并执行。执行后的结果会被拼接到对话上下文中,模型再基于结果生成面向用户的最终答复。整个链路中,模型负责“思考”,应用层负责“行动”。

LLM(x)→文本⟹LLM(x,F)→JSON(f,θ)→执行→r→LLM(x,r)\text{LLM}(x) \rightarrow \text{文本} \quad \Longrightarrow \quad \text{LLM}(x, \mathcal{F}) \rightarrow \text{JSON}(f, \theta) \rightarrow \text{执行} \rightarrow r \rightarrow \text{LLM}(x, r)

1. 传统模式:$\text{LLM}(x) \rightarrow \text{文本}$ — 模型仅根据用户输入 $x$ 生成一段文本回复,无法触碰真实世界。

LLM(x)→文本\text{LLM}(x) \rightarrow \text{文本}

2. 注入函数清单:$\mathcal{F}$ — 开发者把可用工具函数以结构化定义的方式注入给模型,相当于给店长看后厨岗位表。

LLM(x,F)\text{LLM}(x, \mathcal{F})

3. 结构化输出:$\text{JSON}(f, \theta)$ — 模型输出选中的函数名 $f$ 与参数 $\theta$,相当于店长写下的派工单。

JSON(f,θ)\text{JSON}(f, \theta)

4. 执行与回传:$r \rightarrow \text{LLM}(x, r)$ — 应用层执行函数得到结果 $r$,拼回上下文让模型继续推理。

执行→r→LLM(x,r)\text{执行} \rightarrow r \rightarrow \text{LLM}(x, r)

店长还是那个店长,但有了传声筒之后,他不再是“只说不做”的空谈者,而是能调动整个后厨资源的真正决策者。这就是 Function Calling 的核心价值:把大模型的“大脑”与外界的“手脚”连接起来,让 AI 从聊天进入行动时代。

工作原理

店长拿到顾客的订单后,心里会快速盘算:这道菜该找炒菜台还是烧烤台?要不要备注免辣?这个“盘算”的过程,就是模型在做意图识别和调度决策。Function Calling 的工作原理并不神秘——它本质上仍然是“预测下一个 token”。当用户说“北京今天天气怎样”,模型在概率分布中“感觉”到:最像的下一个输出应该是一个 JSON 调用指令,指向 get_weather 函数。

具体来说,模型会在开发者提供的函数列表中做“隐式的选择题”。它先理解用户意图,再判断哪个函数与意图最匹配,然后按照函数定义中的 JSON Schema 约束,填充出合法的参数值。整个过程发生在一次前向推理中,模型没有真的“运行”任何代码,它只是生成了看起来最合理的结构化文本。真正执行代码的是应用层。

f∗=arg⁡max⁡f∈FPθ(f,args∣x,F)f^* = \mathop{\arg\max}_{f \in \mathcal{F}} P_{\theta}(f, \text{args} \mid x, \mathcal{F})

1. 符号 $f^*$:被模型最终选中的那个函数,例如 get_weather。

f∗f^*

2. 候选集合 $\mathcal{F}$:开发者注册进上下文的全部工具函数,相当于后厨的岗位表。

f∈Ff \in \mathcal{F}

3. 条件概率:$P_{\theta}(f, \text{args} \mid x, \mathcal{F})$ — 给定用户输入 $x$ 和函数清单 $\mathcal{F}$,模型评估“调用 $f$ 并传参 args”的概率。

Pθ(f,args∣x,F)P_{\theta}(f, \text{args} \mid x, \mathcal{F})

4. argmax 决策:$\mathop{\arg\max}$ — 取出概率最大的那个组合作为最终的函数调用输出。

arg⁡max⁡f∈F\mathop{\arg\max}_{f \in \mathcal{F}}

店长的“盘算”看似是思考,其实是经验直觉的快速匹配。大模型也一样——它不懂天气,也不懂数据库,但它见过海量“问题—函数调用”的配对文本,知道什么时候该把派工单递给哪个工作台。理解这一点,你就不会对模型“偶尔选错函数”感到意外了。

函数定义

后厨的岗位表写得越清楚,店长派工就越准。如果岗位表上只写“张三——做菜”,店长怎么知道张三擅长川菜还是粤菜?同样,函数定义就是写给模型看的“岗位说明”。一份高质量的函数定义包含函数名、描述信息和参数 Schema 三部分。函数名要见名知意,比如 get_weather 就比 f1 直观得多;描述信息要说明函数做什么、什么时候用;参数 Schema 则规定每个参数的类型、是否必填、取值范围。

以查询天气为例,函数定义大致是:函数名 get_weather,描述“查询指定城市的当前天气和温度”,参数含 location(字符串,必填)和 unit(枚举:celsius/fahrenheit,可选)。模型看到这个定义后,遇到“北京热不热”就能输出 {"name": "get_weather", "arguments": {"location": "北京"}}。如果描述写得含糊,模型就可能把天气查询误判成搜索函数。

FunctionDef=(name,description,parameters)\text{FunctionDef} = ( \text{name}, \text{description}, \text{parameters} )

1. name 函数名:模型的输出指令中用它来指定目标函数,命名要见名知意。

name\text{name}

2. description 描述:一段自然语言说明,告诉模型“这个函数是干什么的、适合什么场景”。

description\text{description}

3. parameters 参数约束:JSON Schema 对象,声明每个参数的类型(string/number/boolean)、必填性(required)和可选枚举(enum)。

parameters:JSON Schema\text{parameters}: \text{JSON Schema}

一份好的“岗位说明书”能让店长少犯糊涂,一份好的函数定义能让模型少犯错误。多花十分钟打磨 description 和参数 Schema,比事后追着修正错误调用要划算得多——这是所有 Function Calling 实战老手的第一条心得。

调用流程

一张完整的派工单,要经历“写单—递单—做菜—端回”四个环节。Function Calling 的调用流程同样是一套标准四步舞:工具注册、模型输出、应用层执行、结果回传。第一步,开发者把函数定义清单注入系统提示词,相当于把岗位表贴在店长眼前;第二步,模型根据用户输入输出 JSON 格式的函数调用;第三步,应用层解析 JSON、校验参数、执行真实函数;第四步,把函数返回结果拼接到对话上下文,让模型基于结果生成最终答复。

工具注册 ⇒ LLM 输出 (f,θ) ⇒ 执行 f(θ)=r ⇒ LLM(x,r)→回复\text{工具注册} \ \Rightarrow\ \text{LLM 输出 } (f, \theta) \ \Rightarrow\ \text{执行 } f(\theta) = r \ \Rightarrow\ \text{LLM}(x, r) \to \text{回复}

1. 工具注册:把函数定义清单注入模型输入,让模型“知道”有哪些可用函数。

工具注册\text{工具注册}

2. 模型输出:$\text{LLM 输出 } (f, \theta)$ — 模型生成的 JSON 中包含函数名 $f$ 和参数对象 $\theta$。

LLM 输出 (f,θ)\text{LLM 输出 } (f, \theta)

3. 应用层执行:$\text{执行 } f(\theta) = r$ — 不是模型运行函数,而是应用层代码执行 $f(\theta)$ 并得到结果 $r$。

执行 f(θ)=r\text{执行 } f(\theta) = r

4. 二次推理:$\text{LLM}(x, r)$ — 原始输入 $x$ 与工具结果 $r$ 拼接,模型继续生成最终回复。

LLM(x,r)→回复\text{LLM}(x, r) \to \text{回复}

注意第三步有一个容易混淆的点:在 OpenAI 等平台的 API 设计中,模型返回的 function_call 不会自动执行——平台把“做菜”这一步留给开发者自己。这也意味着,执行逻辑的健壮性(参数校验、超时处理、异常兜底)完全掌握在应用层手中。店长可以写错单子,但后厨必须有自己的把关。

与Agent关系

店长不是只派一张单就结束。如果顾客点了满汉全席,店长会连续写多张工单:先让甜品台上甜点,再让炒菜台上主菜,最后让传菜员确认上齐。这就是 Agent(智能体)的工作方式。Function Calling 是 Agent 的“手”——Agent 在循环中反复调用 LLM,让模型决定“下一步做什么”,如果模型决定调用工具,Agent 就执行工具并把结果拼回上下文,然后再次询问模型,如此循环,直到模型认为任务完成。

这个循环是所有 Agent 框架的底层骨架。无论是 LangChain 的 AgentExecutor、OpenAI 的 Assistants API,还是 AutoGPT 式的自主 agent,核心都是“模型决策 → 工具执行 → 结果回传 → 再决策”的循环。Function Calling 为这个循环提供了标准化的“决策输出格式”——统一的 JSON 结构让框架可以轻松解析并调度工具。

st+1={st⊕exec(ft,θt)若 ft≠∅终止并输出最终答案否则s_{t+1} = \left\{ \begin{array}{cl} s_t \oplus \text{exec}(f_t, \theta_t) & \text{若 } f_t \neq \varnothing \\ \text{终止并输出最终答案} & \text{否则} \end{array} \right.

1. 状态 $s_t$:第 $t$ 轮的完整对话上下文,包含历史消息、系统提示词和之前的工具结果。

sts_t

2. 执行工具:$\text{exec}(f_t, \theta_t)$ — 调用第 $t$ 轮模型选中的函数 $f_t$ 并传入参数 $\theta_t$。

exec(ft,θt)\text{exec}(f_t, \theta_t)

3. 拼接符号:$\oplus$ — 将工具结果作为新的消息追加到上下文中,形成下一轮的状态。

st⊕exec(ft,θt)s_t \oplus \text{exec}(f_t, \theta_t)

4. 终止条件:$f_t = \varnothing$ — 当模型不再输出函数调用(即输出普通文本)时,循环结束。

若 ft≠∅⋯否则终止\text{若 } f_t \neq \varnothing \cdots \text{否则终止}

店长“派单—确认—再派单”的循环,背后是后厨—店长之间的信任协作。Agent 之所以强大,正是因为 Function Calling 给了它一双可持续行动的手。理解“循环 + 工具 + 结果回传”这个三角结构,你就理解了 ReAct、Plan-and-Execute 等几乎所有 Agent 模式的本质。

实践要点

一家餐厅想要稳定出品,光靠店长记性好不够,后厨必须有流程规范。Function Calling 工程化落地的核心,是围绕“选得对、传得准、执行稳”三个目标建立防御体系。选得对,靠的是函数描述质量——避免模糊词汇,对易混淆的函数做差异化描述;传得准,靠的是参数 Schema 约束——声明类型、必填项、枚举值,模型输出后还要做一次 JSON 解析和类型校验;执行稳,靠的是错误处理——给函数调用加 try-catch、超时限制、重试机制,甚至把执行错误作为工具结果回传给模型,让它“自我纠错”。

成功率=P(选对函数)×P(参数合法)×P(执行成功)\text{成功率} = P(\text{选对函数}) \times P(\text{参数合法}) \times P(\text{执行成功})

1. 选对函数:$P(\text{选对函数})$ — 取决于 description 是否清晰、函数之间的区分度是否足够。

P(选对函数)P(\text{选对函数})

2. 参数合法:$P(\text{参数合法})$ — 取决于 JSON Schema 约束与应用层校验,防止模型输出“字符串但 Schema 要求数字”这类问题。

P(参数合法)P(\text{参数合法})

3. 执行成功:$P(\text{执行成功})$ — 取决于代码健壮性:异常捕获、超时、重试、降级方案。

P(执行成功)P(\text{执行成功})

安全边界同样不可忽视。对于敏感操作(删除数据、发消息、支付),应用层应当做二次确认或白名单过滤。后厨的刀具需要上锁,Agent 的工具也需要权限控制。记住一条铁律:模型输出的只是“建议指令”,最终能不能执行、怎么执行,决定权永远在应用层。

局限与挑战

店长偶尔也会犯迷糊——看错菜名、写漏配料。Function Calling 同样存在固有局限。第一个挑战是幻觉误调用:模型可能“编造”一个不存在的函数名,或者把用户意图错误映射到一个不相关函数上。这类问题难以彻底根除,只能通过高质量函数描述和输出校验来降低概率。第二个挑战是上下文消耗:工具定义和每次调用的返回结果都要拼进上下文,Agent 多轮循环会导致 token 成本快速膨胀。

Token 成本≈∣工具描述∣×N+∑i=1N∣ri∣\text{Token 成本} \approx |\text{工具描述}| \times N + \sum_{i=1}^{N} |r_i|

1. 描述成本:$|\text{工具描述}| \times N$ — 函数定义清单每轮都会随请求发送,$N$ 是调用轮数,函数越多、轮数越长,开销越大。

∣工具描述∣×N|\text{工具描述}| \times N

2. 结果成本:$\sum_{i=1}^{N} |r_i|$ — 每轮工具返回的结果 $r_i$ 也会留在上下文中,累积起来不可忽视。

∑i=1N∣ri∣\sum_{i=1}^{N} |r_i|

3. 约等号:$\approx$ — 实际成本还包含系统提示词、用户消息等,这里只给出了核心部分。

Token 成本≈\text{Token 成本} \approx

第三个挑战是并发与限流:当大量用户同时触发 Agent 时,外部 API 的频率限制和成本压力会非常明显,需要设计缓存、队列和熔断机制。第四个挑战是评估测试:工具调用链路比纯文本对话复杂得多,需要构建专门的评测集来判断“选对了哪个函数、参数是否精确、最终回答是否满意”。这些挑战不是劝退理由,而是工程化路上的必经关卡——知道店长会犯哪些错,才能设计出防错的后厨流程。

AI 应用场景

智能客服工单系统

阶段:意图识别与工具调度

解决的问题:传统客服需要人工查询订单系统、退换货规则等多个内部系统,响应慢且人力成本高。接入 Function Calling 后,大模型先识别用户意图,再调用订单查询、物流跟踪、售后政策等函数,自动完成信息聚合与答复生成。

具体案例:用户向电商客服发送“我的 iPhone 充电器什么时候发货?”模型识别到“查物流”意图,调用 get_order_status(order_id) 和 track_delivery(tracking_no) 两个函数,返回物流信息后生成自然语言答复。人工客服只需处理模型无法解决的异常工单。

数据分析智能体

阶段:多轮工具调用与 Agent 循环

解决的问题:业务人员想查“华东区 Q3 销量 Top5 商品”,传统 BI 工具需要手写 SQL 或拖拽报表。Function Calling 让模型动态生成 SQL 查询函数调用,并在循环中不断修正查询条件,直到得到满意结果。

具体案例:在 DataAgent 系统中,模型调用 query_sales_database(sql) 后得到结果,发现数据不够细,再调用 group_by_region(sales_table, "华东") 进一步下钻。多轮函数调用完成后,模型用图表描述总结结论,业务人员全程用自然语言操作。

智能家居语音助手

阶段:函数定义与安全边界的工程实践

解决的问题:用户说“把客厅灯调暗到 30%,空调开到 26 度”,传统语音助手依赖预定义的槽位填充(slot filling),泛化能力差。Function Calling 让模型灵活组合多个设备控制函数。

具体案例:模型输出两个函数调用:control_light(room="客厅", brightness=30) 和 control_ac(room="客厅", temperature=26)。应用层对敏感操作(如开锁)做权限校验,对设备控制加超时重试,执行成功后再统一回复“好的,都为您设置好了”。

Python 演示

python74 行
import json def get_weather(location, unit="celsius"):    """模拟查询天气的外部工具函数"""    table = {"北京": ("晴", 25), "上海": ("多云", 28), "广州": ("雷阵雨", 31)}    weather, temp = table.get(location, ("未知", 0))    if unit == "fahrenheit":        temp = round(temp * 9 / 5 + 32, 1)    return json.dumps({        "location": location,        "weather": weather,        "temperature": temp,        "unit": unit    }, ensure_ascii=False) def get_time(city="北京"):    """模拟查询时间的工具函数"""    table = {"北京": "14:30", "上海": "14:30", "广州": "14:30"}    return json.dumps({"city": city, "time": table.get(city, "未知")},                      ensure_ascii=False) FUNCTIONS = {    "get_weather": "查询指定城市的天气温度,参数 location 为城市名",    "get_time": "查询指定城市的当前时间,参数 city 为城市名"} def model_parse_function_call(user_input):    """模拟大模型:把自然语言映射为结构化函数调用"""    if "天气" in user_input or "温度" in user_input:        location = "北京"        for city in ("北京", "上海", "广州"):            if city in user_input:                location = city                break        unit = "fahrenheit" if "华氏" in user_input else "celsius"        return {"name": "get_weather",                "arguments": {"location": location, "unit": unit}}    if "时间" in user_input or "几点" in user_input:        city = "北京"        for c in ("北京", "上海", "广州"):            if c in user_input:                city = c                break        return {"name": "get_time", "arguments": {"city": city}}    return None def execute_function(call):    """应用层执行真实函数调用"""    if call is None:        return "未找到合适的工具"    name = call["name"]    args = call["arguments"]    if name == "get_weather":        return get_weather(**args)    if name == "get_time":        return get_time(**args)    return json.dumps({"error": "未知函数"}, ensure_ascii=False) def run_agent(user_input):    """模拟 Function Calling 完整链路:意图 -> JSON -> 执行 -> 回传"""    print("用户输入:", user_input)    call = model_parse_function_call(user_input)    print("模型输出的函数调用:", json.dumps(call, ensure_ascii=False)          if call else "无")    result = execute_function(call)    print("工具执行结果:", result)    final_answer = "根据查询结果:" + result    print("模型最终回复:", final_answer)    return final_answer if __name__ == "__main__":    run_agent("北京今天天气怎么样?")    print()    run_agent("上海现在几点了?")
目的:用规则关键词模拟大模型的意图识别与 JSON 输出,完整演示“自然语言 → 函数调用 → 执行 → 回传”的 Function Calling 链路。
  1. def model_parse_function_call(user_input):模拟大模型将自然语言映射为结构化函数调用的核心逻辑。
  2. return {"name": "get_weather", "arguments": {...}}模型输出的 JSON 结构:函数名 + 参数对象。
  3. def execute_function(call):应用层执行器:解析模型输出,调度真实函数执行。
  4. return get_weather(**args)将参数字典解包后传给真实工具函数,完成实际调用。
  5. def run_agent(user_input):编排函数:按“意图→JSON→执行→回传”的顺序模拟完整链路。
  6. if __name__ == "__main__":程序入口,展示两个典型场景的调用效果。

常见误区

误区“大模型调用工具,不就是模型自己跑代码、查数据库吗?我只需要把函数描述传给它就行。”很多初学者以为模型收到指令后会主动执行代码,就像人一样“动手操作”。

正解大模型本质上是一个“文本生成器”。它只是根据训练学到的模式,生成一段符合语法的 JSON 调用指令,就像店长写一张派工单。真正执行函数的是应用层代码——解析 JSON、校验参数、调用 API、处理异常,每一步都是开发者写的程序在干活。如果应用层不写执行逻辑,模型输出再多 JSON 也无人响应。

为何常见:因为“调用”这个词太有迷惑性,让人以为模型在做执行动作;加上 Agent 演示中工具自动运行的效果,更强化了“模型自己在动手”的印象。

误区“函数名用 f1、f2、ffa 这种短名,节省 token,大模型一样能理解。”于是把 description 写成“一个函数”甚至留空。

正解函数描述是模型判断“何时使用该函数”的最重要信号。描述越清晰、越能区分相近函数,模型选对的概率越高。比如 get_weather 的描述写“查询指定城市当天天气与温度,适用于天气相关提问”,模型就能把它与 get_time 区分开。函数名和描述是给模型看的“说明书”,省了这几行字,后面可能要付出错误的调用结果来偿还。

为何常见:开发者习惯了给人类同事写简洁的函数名,因为人类能结合上下文补全。但模型没有“项目背景知识”,它只能看到你给它的那几行描述。

误区“把天气、新闻、股票、日历全写进一个 process() 函数,用一个 type 参数区分,这样模型只需要选一个函数,多省事。”

正解单一职责是函数设计的黄金法则。函数职责越单一,模型越容易通过 description 理解它的用途,参数 Schema 也越简单、越不容易出错。把所有逻辑塞进一个大函数会让 description 变得冗长含糊,参数结构变成一个大杂烩,模型反而更容易选错或填错参数。多个功能就拆分成多个函数,哪怕只是多个薄包装。

为何常见:开发者想减少函数数量来降低模型的选择难度,但适得其反——选择难度的关键不在数量,而在每个函数是否清晰可辨。

误区“大模型很聪明,它输出的 JSON 肯定是对的,直接拿去调用外部服务就行,不用校验。”

正解模型是概率生成器,偶尔会输出非法 JSON、参数名拼写错误、类型与 Schema 不匹配,甚至编造不存在的函数名。应用层必须做防御性校验:JSON 解析失败时要求模型重试,参数缺失时用默认值补齐,非法调用则阻止执行。更要把执行异常作为结果回传给模型,让它有机会自我纠正。把大模型当成一个“聪明但偶尔犯晕”的店长,后厨一定要有自己的把关。

为何常见:模型的输出太像正确代码了,而且 demo 中通常恰好成功,让人产生“每次都正确”的错觉。

误区“上一轮已经调用过 get_weather 了,这轮再问天气时,模型应该记得上次传了什么参数,我不用再传一遍工具定义。”

正解大模型没有跨请求的记忆。每轮请求都只包含当前上下文中的内容——工具描述、历史消息、之前的结果都会被拼进 input。如果应用层不把历史工具结果追加到消息列表,模型就“失忆”了。Agent 框架之所以能“记住”多轮调用,正是因为每一轮的完整记录都被拼回了上下文,同时受上下文窗口长度限制。

为何常见:人类有持续记忆,容易把模型拟人化,以为它记得一切对话历史。实际上模型每轮都是从零开始“重新阅读”整个上下文。

面试题

Q1. Function Calling 中模型输出的核心指令格式是什么?

单选题 · easy

  • A.一段自然语言描述文本
  • B.结构化 JSON 函数调用← 正确答案
  • C.二进制编码的工具指令
  • D.高维向量嵌入表示

答案:B。Function Calling 的核心是让模型输出结构化 JSON,包含函数名和参数对象。自然语言描述无法被程序可靠解析,二进制和向量表示也不是 API 层的标准输出。这就像店长写的派工单必须是“岗位名+菜名”的结构化格式,后厨才能按单执行。

Q2. 在 Function Calling 完整流程中,实际执行函数代码的是谁?

单选题 · easy

  • A.大模型自身推理引擎
  • B.应用层的业务代码← 正确答案
  • C.操作系统的进程调度
  • D.用户的浏览器插件

答案:B。模型只负责输出 JSON 指令,真正执行函数的是应用层代码。这对应餐厅里“店长写单、厨师做菜”的分工——模型是决策者,应用层是执行者。操作系统和浏览器插件都不是标准的函数执行主体。

Q3. 函数定义中的 description 字段最关键的作用是什么?

单选题 · mid

  • A.设定函数的调用权限级别
  • B.帮助模型选择合适的函数← 正确答案
  • C.规定函数的超时时间上限
  • D.决定函数返回值的类型

答案:B。description 是模型判断“何时该用这个函数”的核心依据,相当于岗位说明书。权限由应用层控制,超时是执行层的配置,返回类型由参数 Schema 之外的实际代码决定,都与 description 无关。

Q4. 模型输出的 JSON 中参数类型为字符串,而 Schema 声明为数字,应用层应当如何处理?

单选题 · mid

  • A.直接将该参数丢弃并报错
  • B.做一次类型转换和合法性校验← 正确答案
  • C.返回错误后强制结束对话
  • D.让模型重新预训练模型参数

答案:B。模型是概率生成器,偶尔会输出与 Schema 不一致的参数。成熟的应用层应该做类型转换尝试,失败则用默认值或要求模型重新输出,而不是直接崩溃。这就像后厨收到字迹模糊的工单,应该先跟店长确认,而不是扔掉工单。

Q5. Agent 循环中,工具调用结果被回传给模型后放在哪里?

单选题 · mid

  • A.模型的训练数据集中
  • B.对话上下文消息队列中← 正确答案
  • C.系统的持久化数据库仓库
  • D.独立于进程的内存空间中

答案:B。工具结果会作为一条新消息追加到对话消息列表,随下一轮请求一起发给模型。模型每轮只“看”上下文里的内容,不会把工具结果写入训练集或记忆库。这相当于传菜员把菜端回店长面前,店长看到后再决定下一步。

Q6. 下列哪种做法最有利于模型“选对”函数?

单选题 · hard

  • A.将函数名全部改成简短缩写
  • B.写清晰描述并提供调用示例← 正确答案
  • C.把所有函数合并成一个大函数
  • D.随机打乱函数清单的排列顺序

答案:B。清晰的 description 和示例能显著提升模型的选择准确率。缩写函数名、合并大函数、乱序排列都会增加模型的理解难度或引入不确定性。函数名应见名知意,描述要具体,职责要单一,这些才是“选对”的关键。

Q7. 以下哪些属于 Function Calling 的典型应用场景?

多选题 · mid

  • A.查询实时天气信息← 正确答案
  • B.检索数据库用户订单记录← 正确答案
  • C.直接修改模型内部网络权值
  • D.调用第三方接口发送通知消息← 正确答案

答案:ABD。Function Calling 适合连接外部工具和 API——查天气、查数据库、发消息都是典型应用。修改模型内部权值属于训练范畴,不可能也不应该通过工具调用来实现。工具调用是在模型外部发生的动作,模型本身不会因为调用而改变参数。

Q8. 关于函数参数 Schema 的约束能力,下列说法正确的有哪些?

多选题 · mid

  • A.可以声明参数的具体类型约束← 正确答案
  • B.可以标记参数是否为必填项← 正确答案
  • C.可以限定参数的枚举取值集合← 正确答案
  • D.可以直接执行任意系统级命令

答案:ABC。JSON Schema 支持类型(string/number)、必填(required)和枚举(enum)等约束,这些都能指导模型生成更规范的参数。但 Schema 只是“描述”约束,不具备执行能力——执行任意系统命令是应用层代码的功能,与 Schema 无关。

Q9. 下列哪些措施能有效提升 Function Calling 的系统可靠性?

多选题 · hard

  • A.对模型输出先做 JSON 解析校验← 正确答案
  • B.为函数执行添加超时与重试机制← 正确答案
  • C.通过白名单过滤可用函数范围← 正确答案
  • D.完全信任模型输出不设防御逻辑

答案:ABC。JSON 校验防止非法输出,超时重试应对工具不稳定,白名单过滤防止模型访问敏感函数——这三者构成可靠性防线。完全信任模型输出是危险做法,因为模型可能幻觉出不存在的函数名或错误参数,必须由应用层做最后把关。

Q10. 关于 Function Calling 的局限与挑战,下列说法错误的是哪一项?

单选题 · hard

  • A.模型可能幻觉出不存在的函数名
  • B.工具描述会持续消耗上下文 Token
  • C.高并发场景需要设计合理的限流策略
  • D.函数调用完全不受上下文长度限制← 正确答案

答案:D。幻觉误调用确实是已知风险,工具描述注入确实增加 Token 开销,高并发下也确有限流需求。但“不受上下文长度限制”是错的——工具描述和轮次结果都存放在上下文中,一旦超出窗口长度,早期的工具结果会被截断丢弃,Agent 的“记忆”也随之消失。