深夜十一点,你走进一家还没打烊的餐厅。你对着服务员说:“来一杯热拿铁,少冰。”服务员并没有亲自跑去咖啡豆仓库拉货,也没有自己动手磨豆子——他熟练地写下一张小票:“拿铁,热,少冰”,贴在传菜口,后厨收到后照单制作,几分钟后端出咖啡,服务员再把咖啡端到你面前。整个过程里,服务员只做两件事:听懂你的话,写对单子;至于咖啡怎么萃取,那是后厨的事。 大模型(LLM)就是这位服务员。它博学多才,能写诗、能翻译,但它出生时并不自带“实时天气”“你的订单数据库”“某个公司的股票行情”这些后厨能力。Function Calling(函数调用)就是递给它的那张传菜小票:当对话需要真实数据或实际动作时,模型不是自己编一个答案,而是生成一张结构化的小票,交给程序里的“后厨函数”去执行,再把结果端回来。这一来,大模型就从“只会背菜谱的机器人”变成了“会帮你向世界下单的服务员”。

岗位关联

掌握 Function Calling,你就能让大模型真正“动手办事”——查询数据、下单、发通知。这是从聊天机器人进阶到 AI 应用开发者的关键一步,也是当前 AI 工程师岗位最基础的硬技能之一。

目录
  1. 可视化演示
  2. 函数调用概览——服务员为什么不自己炒菜
  3. 函数 Schema——写好菜单上的菜品卡
  4. 提示词设计——培训服务员何时下单
  5. 调用执行流程——从下单到上菜的四步
  6. 返回结果处理——后厨出品的最后质检
  7. 多轮调用编排——一餐饭的多次加菜
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

\u5c55\u793aSeq2Seq\u6a21\u578b\u4e2d\u6ce8\u610f\u529b\u6743\u91cd\u77e9\u9635\u7684\u5206\u5e03\uff0c\u901a\u8fc7\u6e90\u5e8f\u5217\u4e0e\u76ee\u6807\u5e8f\u5217\u7684\u5bf9\u9f50\u5173\u7cfb\u76f4\u89c2\u5448\u73b0\u6ce8\u610f\u529b\u5206\u914d\u673a\u5236\u3002

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

\u6ce8\u610f\u529b\u6743\u91cd\u77e9\u9635 \u7ebf\u6761\u7c97\u7ec6\u8868\u793a\u6ce8\u610f\u529b\u6743\u91cd\u5927\u5c0f \u7ea2\u8272\u4e3a\u6700\u5927\u6743\u91cd\u8fde\u63a5 \u03b1 = softmax(Q\u00b7K^T/\u221ad) \u70b9\u51fb\u76ee\u6807\u8bcd\u67e5\u770b\u6ce8\u610f\u529b\u5206\u5e03

函数调用概览——服务员为什么不自己炒菜

想象一位新来的服务员。顾客问“今天北京多少度?”,他不能凭空编一个温度,因为后厨(外部世界)才有温度计。没有 Function Calling 时,模型只能凭训练时的记忆“猜一个答案”,就像服务员凭印象瞎报菜价——看着像那么回事,其实根本不新鲜。而有了 Function Calling,模型会在发现自己“没有这道菜”时,主动写一张小票交给后厨:“请查询北京今天的天气”。后厨查完,把结果端回来,服务员再转述给顾客。

从技术上说,Function Calling 是让大模型在生成自然语言之外,额外生成一段结构化的函数调用指令。这段指令通常包含两个关键信息:调用哪个函数(函数名),以及传入什么参数。它并不是让模型真的去执行代码——模型只是一个“写字的人”,真正动手的是你程序里写好的函数。这就像服务员只负责写单和传菜,绝不亲自进后厨炒菜。

为什么需要这种设计?因为大模型的核心是“预测下一个词”,它不掌握实时数据,也不具备执行动作的能力。如果让它在对话里直接算出订单金额、直接扣款,那无异于让服务员凭记忆报出全城所有菜品的最新价格——风险极高。Function Calling 把“理解意图”和“执行动作”干净地切开:模型负责理解,代码负责执行,双方通过一张 JSON 小票协作。

典型场景俯拾皆是:智能客服查订单、订餐助手下单、数据分析师用自然语言查数据库、IoT 助手控制设备。在这些场景里,模型不再“编答案”,而是“找答案”。就像餐厅服务员可以不会做满汉全席,但只要他懂得把每道菜准确地传到后厨,这桌饭就能稳稳当当地上齐。

函数 Schema——写好菜单上的菜品卡

餐厅里每道菜都会有一张菜品卡:菜名、简介、需要什么食材、哪些配料可加可不加。后厨照卡备菜,服务员照卡下单,顾客照卡点菜。在 Function Calling 中,这张“菜品卡”就叫 函数 Schema(结构定义)。你必须在调用模型之前,把你准备的每个函数都写成一张这样的卡,告诉模型:世界上存在一个叫 get_weather 的函数,它的作用是查询天气,它需要一个叫 city 的参数,而且这个参数是必须的。

用术语说,Schema 是一个 JSON 对象,常见的结构如下:type 固定声明这是一个“函数”工具;name 是函数名;description 用自然语言描述这个函数做什么、什么情况下该用它;parameters 是一个 JSON Schema,定义了每个参数的名称、类型和含义;required 列出哪些参数是必填的。这些字段共同构成模型“决定是否调用、调用时填什么参数”的依据。

tool:={name, description, parameters}\displaystyle \mathrm{tool} := \{\mathrm{name},\ \mathrm{description},\ \mathrm{parameters}\}

1. name(函数名):函数的唯一标识。模型输出调用指令时,会把这个名字原样写在 JSON 里,程序才能找到对应函数执行。可以把它理解成菜单上的“宫保鸡丁”四个字,顾客报出菜名,后厨立刻知道做哪道菜。

name\mathrm{name}

2. description(功能描述):用一句人话说明函数是干什么的。这是模型判断“该不该调用”的核心依据。描述写得越清楚,模型越知道什么时候该掏小票,什么时候不该。

description\mathrm{description}

3. parameters(参数定义):声明这个函数需要哪些输入,每个输入是什么类型、什么含义。模型会从这里学习如何从用户的话里抽取参数,就像菜品卡写明“需要鸡胸肉 200 克、花生米 50 克”。

parameters\mathrm{parameters}

Schema 写得有多好,模型就有多靠谱。如果你的 description 写的是“处理一些事情”,模型很可能在需要查询天气时不敢调用,或者乱调用。好的 Schema 应该像一位老练的厨师写菜单:每道菜的名字清晰、用料明确、口味可选。参数描述也别吝啬,例如 city 参数可以写“城市名,如北京、上海”,模型就能从“北京天气怎么样”里准确抽出“北京”。这顿“函数服务”好不好吃,从你写菜单那一刻就决定了。

提示词设计——培训服务员何时下单

有了菜品卡,还得培训服务员:什么情况下该下单?顾客说“你们有炒饭吗”是询问,不需要下单;顾客说“我要一份扬州炒饭”才需要下单。大模型同样需要一套“培训手册”,告诉它调用函数的时机和参数抽取规则。这部分工作就写在 System Prompt 里,我们称之为提示词设计。

首先,要明确调用时机。一个好的提示词会这样写:“当用户询问天气、且能确定城市时,调用 get_weather;如果城市不明确,先向用户询问城市再调用。”这相当于教服务员:顾客说“帮我看看外边冷不冷”时,先确认地点,再决定是否后厨查天气,而不是直接瞎报。

其次,是参数抽取。用户说话往往随意:“北京明天穿什么?”你希望模型抽取出 city="北京"、date="明天" 并映射成函数可用的日期格式。提示词里可以补充说明:“日期需转换为 YYYY-MM-DD 格式”。模型就像服务员听顾客说“老样子”——他能根据培训经验,把“老样子”翻译成具体菜品。参数抽取质量直接决定函数执行的成功率。

最后,考虑默认行为与边界情况。如果函数有默认值,比如 get_weather 不传日期就默认今天,就可以在 Schema 的 description 里写“默认当天”;如果用户要求的功能不在任何函数覆盖范围内,提示词要告诉模型“不要虚构,直接说明暂无此服务”。边界情况就像顾客点了一道菜单上没有的菜——服务员应当礼貌告知,而不是随便写个菜名让后厨为难。提示词 + Schema 是一对搭档:Schema 告诉模型“有哪些菜”,提示词告诉模型“什么时候点、怎么点”。

调用执行流程——从下单到上菜的四步

当顾客说完“帮我查一下北京的天气”,真正的 Function Calling 流程就开始了。这顿饭一共分四步上桌。第一步,模型输出:模型带着你的消息和 Schema 一起推理,发现“该查天气了”,于是在回复中输出一个特殊的 tool_calls 字段,里面写着函数名和参数。第二步,解析调用:你的程序收到这个字段,把它从 JSON 里读出来,确认要调用哪个函数、参数是什么。第三步,执行函数:程序在你自己的代码里调用那个真实函数,比如向天气 API 发请求,拿到真实数据。第四步,结果回填:把函数返回结果作为一条特殊消息送回给模型,模型再根据结果组织成一句自然语言回复用户。

用公式看,前两步发生在模型侧,后两步发生在你的程序侧:

output=LLM(messages,tools)⟹result=f(arguments)\displaystyle \mathrm{output} = \mathrm{LLM}(\mathrm{messages}, \mathrm{tools}) \quad\Longrightarrow\quad \mathrm{result} = f(\mathrm{arguments})

1. messages(对话消息):当前对话的全部上下文,包括用户输入和历史消息。服务员要记住顾客前面说过的话,模型同样需要这些信息来判断当下该不该调用工具。

messages\mathrm{messages}

2. LLM 输出(模型推理):模型读完消息和工具声明后,决定生成普通文本回复,还是生成 tool_calls 调用指令。它像一个训练有素的服务员,能判断“这桌该下单了”。

LLM(messages,tools)\mathrm{LLM}(\mathrm{messages}, \mathrm{tools})

3. f(arguments)(函数执行):你的程序真正调用函数,传入从模型输出中解析出的参数。这是热锅热油的真实步骤,不是模型在脑内模拟的“想象做菜”。

f(arguments)f(\mathrm{arguments})

四步流程中有一个关键认知必须刻在脑子里:模型永远不会替你去执行函数。get_weather 的 API 请求是你发出的,数据库查询是你写的 SQL 做的事,扣款操作是你调用的支付接口完成的。模型只是那位把菜名写在小票上的服务员,而后厨的火,始终燃烧在你自己家的灶台上。这张传菜小票虽然只是薄薄一层 JSON,却把“意图”和“执行”牢牢焊接在了一起。

返回结果处理——后厨出品的最后质检

后厨端出一盘菜,服务员不能直接往桌上一扔——他得先看一眼菜是否完整、是否符合备注,再决定怎么跟顾客说。函数返回结果也是如此:函数执行完,返回的往往是一串字符串或 JSON,你(或程序)需要对它做解析、校验、错误处理,最后把干净的结果交回给模型,让模型生成对用户友好的回复。

先看结果解析。假设 get_weather 返回的是一串 JSON:{"city":"北京","weather":"晴 22°C"}。你的程序应该用 json.loads 把它解析成字典,而不是直接把这串原始文本塞回给模型——就像服务员看到后厨递来的原料,要先装盘,不能直接把血淋淋的鸡肉端给顾客。解析后的结构化数据,模型才能高效阅读。

再看错误处理。如果城市名不存在,函数可能返回 {"error":"city not found"}。这时你传给模型的结果应该明确带着错误信息,并引导模型向用户道歉或追问:“抱歉,我没有查到这座城市,您说的是‘北京’吗?”如果不做错误处理,模型可能基于残缺结果瞎编一个天气,酿成“服务员把菜烧糊了还给顾客上桌”的事故。永远把错误信息当作合法结果的一部分来传递,是 Function Calling 应用成熟的标志。

格式化回复也非常重要。模型接回函数结果后,不再是“朗读 JSON”,而是把结果转成自然的回答:“北京今天晴,气温 22 摄氏度。”这一步通常在第二轮回调中完成:你以 tool 角色把函数结果加入对话,然后让模型再次生成最终回复。最后别忘了校验防御:在使用模型吐出的 arguments 之前,要在你的代码里检查参数类型、范围,防止模型生成不符合预期的参数。就像服务员下单前要确认顾客没有少给钱——信任,但要核对。

多轮调用编排——一餐饭的多次加菜

真实的对话很少一次就结束。顾客先问北京天气,又问“那上海呢?”,接着说“帮我订两张明天去上海的机票”。每一次追问都可能触发新的函数调用。这就是多轮调用编排:让模型在连续几轮对话中,依据对话历史反复调用不同函数,完成一个复杂的任务。

关键前提是保留完整对话历史。比如第一轮模型调用了 get_weather,返回结果已经作为 tool 消息写进了消息列表;第二轮用户问“上海呢?”,如果只把新问题发给模型,模型根本不知道“上海”指的是“查上海天气”还是“订上海的机票”。正确做法是把之前所有轮次的 user、assistant、tool 消息都保留下来,追加式地发给模型,让模型像服务员一样完整记得整桌客人的所有需求。

history′=history∪{assistant_tool_call, tool_result}\displaystyle \mathrm{history}' = \mathrm{history} \cup \{\mathrm{assistant\_tool\_call},\ \mathrm{tool\_result}\}

1. history(历史状态):已有的对话消息数组,保存了此前所有轮次的交流记录。它是模型理解当前语境的记忆载体,相当于服务员脑中“这桌客人之前说过什么”的印象。

history\mathrm{history}

2. assistant_tool_call(模型的调用记录):模型生成的那条 tool_calls 消息,必须原样追加进历史。它记录了“我当时决定调用哪个函数、传了什么参数”,是后续对话的逻辑锚点。

assistant_tool_call\mathrm{assistant\_tool\_call}

3. tool_result(函数结果):把函数执行结果也追加进去。模型在下一轮生成时能看到“函数实际返回了什么”,才能接着用户的话往下说,比如“上海是小雨,建议带伞”。

tool_result\mathrm{tool\_result}

当一次请求需要调用多个函数时,还可能出现连续调用:模型先调用 query_weather 得知“明天下雨”,再调用 recommend_destination 推荐室内景点。某些 API 还支持并行调用:模型一次输出多个 tool_calls,你的程序可以并行执行它们,再一起回填结果,就像服务员一次性把三张单子夹在一起送到后厨,提高效率。但别忘了设置终止条件:当模型不再输出 tool_calls 时,说明任务完成了,此时把最终回复交给用户;如果给模型设一个最大循环次数(比如 5 次),还能防止它陷入“反复调工具却永不结账”的死循环。一餐饭总有吃完的时候,聪明的编排者会记得替顾客买单。

AI 应用场景

智能客服订餐助手

阶段:Schema 定义 + 多轮调用编排

解决的问题:传统客服机器人只能根据话术模板回答固定问题,无法对接真实库存、菜单和订单系统。用户说“来一杯拿铁和一份提拉米苏”,机器人根本不知道这两个商品是否还有货、能不能加购物车。用 Function Calling 后,机器人成为“传菜服务员”,把每个需求转成真实的系统调用。

具体案例:用户说“我要一杯热拿铁,少冰,再来一块提拉米苏”。模型第一轮调用 check_stock 确认提拉米苏有货,第二轮调用 add_to_cart 将两件商品加入购物车,第三轮调用 create_order 生成订单并返回订单号,最后模型用自然语言告知用户“您的订单 1024 已确认,预计 20 分钟后送达”。全程用户感觉是在和一个真人服务员对话,实际每一单都触发了真实系统操作。

企业数据问答系统

阶段:函数 Schema 定义 + 返回结果处理

解决的问题:老板问“上季度华东区销售额是多少?”,传统做法是让数据分析师手写 SQL 查询,周期长且门槛高。Function Calling 让模型自动把自然语言问题转成结构化查询参数,交给后端查询函数执行,再把查询结果格式化为图表和文字摘要。

具体案例:用户在对话框中输入“对比华东和华南上季度各月销售额”。模型调用 query_sales,参数为 region: ["华东","华南"]、time_range: "上一季度",后端执行 SQL 返回一个 JSON 数组。函数返回后,模型把原始数据整理成“华东 3 月销售额环比上升 12%,华南持平”的报告。即便查询失败,模型也能根据错误信息提示用户调整问题。

Python 演示

python61 行
# -*- coding: utf-8 -*-"""Function Calling 模拟演示:大模型连接外部天气服务"""import json def get_weather(city, date="今天"):    """模拟真实后端的天气查询函数"""    weather_map = {"北京": "晴 22°C", "上海": "小雨 18°C", "广州": "多云 26°C"}    return json.dumps({"city": city, "date": date, "weather": weather_map.get(city, "未知")}, ensure_ascii=False) FUNCTIONS = {"get_weather": get_weather} def mock_llm(messages, tools):    """模拟支持 Function Calling 的 LLM,真实环境替换为 client.chat.completions.create"""    last = messages[-1]["content"] if messages else ""    if "天气" in last and not any(m["role"] == "tool" for m in messages):        args = {"city": last.split("查")[-1].strip("的天气 ") or "北京", "date": "今天"}        return {            "tool_calls": [{                "id": "call_001",                "type": "function",                "function": {"name": "get_weather", "arguments": json.dumps(args, ensure_ascii=False)}            }]        }    return {"content": "已为你查到最新天气信息,请注意查看。"} def run_function_call(user_input):    """执行完整的多轮函数调用流程"""    tools = [{        "type": "function",        "function": {            "name": "get_weather",            "description": "查询指定城市当天的天气",            "parameters": {                "type": "object",                "properties": {                    "city": {"type": "string", "description": "城市名,如北京"},                    "date": {"type": "string", "description": "查询日期,默认今天"}                },                "required": ["city"]            }        }    }]    history = [{"role": "user", "content": user_input}]    first = mock_llm(history, tools)    if "tool_calls" not in first:        print("模型回复:", first.get("content"))        return    calls = first["tool_calls"]    history.append({"role": "assistant", "content": None, "tool_calls": calls})    for call in calls:        fn_name = call["function"]["name"]        fn_args = json.loads(call["function"]["arguments"])        fn_result = FUNCTIONS[fn_name](**fn_args)        history.append({"role": "tool", "tool_call_id": call["id"], "content": fn_result})    second = mock_llm(history, tools)    print("用户:", user_input)    print("函数结果:", fn_result)    print("模型最终回复:", second.get("content")) if __name__ == "__main__":    run_function_call("帮我查一下北京的天气")
目的:演示了 Function Calling 的完整闭环——定义工具 Schema、让模型输出 tool_calls、程序解析参数并执行真实函数、再把结果回填给模型生成最终回复。可直接运行,无需 API Key。
  1. tools = [{...}]定义函数 Schema,相当于给服务员看的菜品卡。参数必须用 JSON Schema 格式声明类型、描述和必填项。
  2. first = mock_llm(history, tools)模拟真实环境中的 client.chat.completions.create(...) 调用,传入消息和工具声明,模型返回 tool_calls。
  3. history.append({"role": "assistant", "content": None, "tool_calls": calls})把模型的调用指令追加进历史,这是多轮对话不丢状态的命脉。
  4. fn_result = FUNCTIONS[fn_name](**fn_args)程序真正执行函数。注意:执行发生在你的代码里,模型只负责生成参数。
  5. history.append({"role": "tool", "tool_call_id": call["id"], "content": fn_result})将函数返回结果以 tool 角色回填,模型下一轮才能据此生成自然语言回复。

常见误区

误区很多初学者以为 Function Calling 就是让大模型“自己算出来”——比如用户问“3.14 的平方是多少”,就直接让模型在对话里计算出结果,而不去调用计算函数。他们觉得模型既然这么聪明,何必还要写一个 `calculate` 函数。

正解模型只负责生成调用指令(写小票),真正执行 calculate 函数的是你程序里的 Python 代码。就像服务员只写单,绝不亲自炒菜。哪怕模型能“凭感觉”说出一个近似结果,它也无法保证准确性和实时性,严格来说那只是它在玩文字接龙。

为何常见:因为大模型生成的文本太自然了,让人误以为它真的“动手做了”。实际上它连加法都可能算错,更别提访问外部系统。

误区有人以为只要在 System Prompt 里写“你可以调用 get_weather 函数”,模型就会真的去调用。他们不定义 tools 参数,只靠文字描述让模型‘假装’调用。

正解Function Calling 的前提是把函数以结构化的 tools 形式传给模型。只有看到 Schema(函数名、参数定义、必填项),模型才会产出规整的 tool_calls JSON。提示词里口头的“调用”,模型只会当成聊天内容,输出一段文字,你的程序无从解析执行。

为何常见:传统 Prompt 工程中,大家习惯了“用话术引导模型”,容易忽视 Function Calling 是 API 层面的结构化能力,而非纯文本技巧。

误区初学者写 Schema 时只写 `name` 和 `parameters`,觉得 `description` 可有可无。结果模型要么不调用函数,要么把用户说的“北京”抽成 `city` 参数时抽错了。

正解description 是模型理解函数功能和学习参数抽取规则的主要来源。写清楚“city 是城市名,如北京、上海”,模型才能从“北京明天天气如何”里准确抽出 city="北京"。菜单上只有菜名没有简介,服务员也容易上错菜。

为何常见:因为 Schema 看起来像“格式定义”,人们容易只关注字段结构,而忽略自然语言描述对模型行为的关键引导作用。

误区有些开发者在每轮对话中只把最新的用户消息发给模型,导致第一次调用天气的结果在第二轮就“失忆”。用户追问“那上海呢?”模型完全不知道这是在问天气。

正解正确的做法是把整个消息列表(含 user、assistant 的 tool_calls、tool 的执行结果)都保留下来并发给模型。历史是模型的记性,丢掉历史等于让服务员忘了整桌客人说过什么。推荐在循环中控制最大调用次数,既保留状态又防止死循环。

为何常见:因为普通聊天接口只需要传最近消息,大家习惯性延续了这种“无状态”思维,没意识到工具调用本身会改变对话状态。

面试题

Q1. Function Calling 的核心作用是什么?

单选题 · easy

  • A.让模型自动写代码
  • B.输出结构化函数调用← 正确答案
  • C.替代全部后端接口
  • D.在本地直接执行程序

答案:B。Function Calling 的核心是让模型在对话中输出结构化的 tool_calls 指令,包含函数名和参数,而不是让模型自己执行代码。它像服务员写传菜小票,真正炒菜的是后厨(你的程序)。

Q2. 函数 Schema 中哪个字段描述“函数是做什么的”?

单选题 · easy

  • A.函数名称字段
  • B.参数类型字段
  • C.函数描述字段← 正确答案
  • D.必填数组字段

答案:C。description 字段用于说明函数的功能和适用场景,是模型决定是否调用以及如何抽取参数的重要依据。name 是函数名,parameters 定义参数,required 声明必填项,它们各有分工。

Q3. 下面哪项是典型的函数调用场景?

单选题 · easy

  • A.图像风格迁移
  • B.语音识别转换
  • C.实时天气查询← 正确答案
  • D.文本情感分类

答案:C。Function Calling 适合模型需要获取实时数据或执行动作的场景,如天气查询。图像风格迁移、语音识别通常是模型多模态能力或独立模型完成的任务,不依赖工具调用来弥补信息缺失。

Q4. 模型返回的 tool_calls 中,调用参数存放在哪个属性?

单选题 · mid

  • A.role 属性
  • B.type 属性
  • C.name 属性
  • D.arguments 属性← 正确答案

答案:D。tool_calls 里的每个调用包含 id、type 和 function,而 function 对象内有 name(函数名)和 arguments(参数 JSON 字符串)。解析时先取 function.arguments 再 json.loads,就像服务员从单子上读出“热拿铁、少冰”。

Q5. 函数执行完成后,如何把结果交还给模型继续对话?

单选题 · mid

  • A.以 user 角色追加消息
  • B.以 tool 角色追加消息← 正确答案
  • C.以 assistant 角色追加消息
  • D.替换掉整段历史对话

答案:B。OpenAI 协议要求函数结果以 role="tool" 且带 tool_call_id 的消息追加进历史,与对应的 assistant tool_calls 消息配对。这样模型才能“看到”后厨端出的真实菜品,而不是顾客自说自话。

Q6. 编写函数 Schema 时,下列哪些做法正确?

多选题 · mid

  • A.参数写清含义← 正确答案
  • B.尽量省略参数
  • C.明确必填字段← 正确答案
  • D.写清函数功能← 正确答案

答案:ACD。Schema 的 description 要写清函数功能,每个参数的描述要说明含义和示例,required 要列出必填字段,这些都能提升模型抽参的准确性。刻意省略参数会让模型缺少输入依据,就像菜品卡不写食材,后厨无法备菜。

Q7. 多轮调用时,最容易导致模型“失忆”的做法是?

单选题 · mid

  • A.保留全部历史消息
  • B.追加 tool 结果消息
  • C.只传最新用户消息← 正确答案
  • D.设置最大循环次数

答案:C。只把最新用户消息发给模型,模型就丢失了此前“调用过什么函数、返回了什么结果”的上下文。比如用户先问北京天气再问“那上海呢”,模型不知道“那”指的是天气查询。历史消息是模型的服务员记忆。

Q8. 当一次回复需要调用多个独立函数时,通常采用什么模式?

单选题 · hard

  • A.并行返回多次调用← 正确答案
  • B.强制只调用一个
  • C.把多次调用写成文本
  • D.让用户人工拼接结果

答案:A。OpenAI 等平台支持模型一次返回多个 tool_calls,程序可并行执行这些独立函数,再统一回填结果。这就像服务员把多张单子同时夹到传菜口,后厨可以同时开火,比串行逐个调用效率更高。

Q9. 解析模型返回的 arguments 时,正确做法是?

单选题 · hard

  • A.直接 eval 执行
  • B.用 JSON 解析并校验← 正确答案
  • C.按字符串位置截取
  • D.忽略参数直接调用

答案:B。arguments 是 JSON 字符串,需要用 json.loads 解析成对象,并在执行前校验字段类型和范围。直接 eval 有代码注入风险,字符串截取脆弱且容易出错,忽略参数则函数无法正常运行。

Q10. 关于多轮调用编排,以下说法正确的有?

多选题 · hard

  • A.保留完整调用历史← 正确答案
  • B.丢弃函数结果消息
  • C.追加 tool 结果回填← 正确答案
  • D.设置终止条件防死循环← 正确答案

答案:ACD。多轮调用要求保留并追加历史,尤其是 assistant 的 tool_calls 与 tool 的返回结果必须成对出现,保证模型知道函数实际执行了什么。同时应设置最大循环次数,防止模型反复输出 tool_calls 却不结束对话,导致无限“加菜”。