推荐语
深入了解大模型调用工具的核心原理,探索Function calling技术如何提升智能体性能。
核心内容:
1. Function calling技术的概念与重要性
2. 大模型调用外部工具的执行流程和底层机制
3. 提升Agent性能的四大优化方案与实战案例
杨芳贤
53A创始人/腾讯云(TVP)最具价值专家
在Agent智能体技术大爆发的今天,我们会经常听到这样的一个专业名词:Function calling。但凡谈到智能体开发,就免不了需要让大模型通过Function calling功能来调用外部工具,我们评价模型的Agent能力,往往就是看模型的Function calling准确率如何,大家讨论MCP技术,都会觉得其本质就是Function calling另一种实现方法。那这无处不在的Function calling到底是什么?它的执行流程和底层运行机制是怎样的,大模型是怎么判断调用哪个工具的呢,为什么有的模型有Function calling能力而其他模型却没有,Function calling和API有啥区别?本期视频,就带你零基础彻底搞懂Function calling技术,打通Agent开发的任督二脉!观前提醒,本期视频非常硬核,不仅会讨论浅层的大模型调用外部工具基本流程,我们还会深入到模型训练流程中,探讨Function calling的底层运行机制,并详细介绍提升模型Agent能力的优化策略。大家可以先点赞收藏一波。此外,更多关于DeepSeek模型的Function calling和智能体开发的代码实操教程,已经在赋泛大模型技术社区中上线了,大家扫码即可领取。Function calling技术也被称作外部函数调用技术,也被称为tool call或tool use,其核心用途是让大模型能够通过调用一些外部工具来完成工作,该技术最早由OpenAI于2023年6月13号正式提出。OpenAI Function calling功能更新公告:https://openai.com/index/function-calling-and-other-api-updates/我们都知道让大模型能调用外部工具,是其由聊天机器人进化为智能体的最重要的一个环节。而外部工具,往往是以API形式存在的,因此要了解Function calling,首先要了解什么是外部工具API。以查询天气为例,最常用的天气查询工具肯定是OpenWeather这款免费工具,这款工具提供了代码使用的接口,也就是所谓的外部工具API,通过这个OpenWeather API,任何应用都能通过运行某段代码来获取OpenWeather的服务,例如我们可以在Python中编写这样一段代码来调用OpenWeather API查询北京地区天气:除了查询天气的API外,还有数以万计的各种不同功能的API,这也是整个应用开发世界的基础。那接下来的问题是,我应该如何把这个外部工具API的查询天气的能力,赋予大模型呢?我们希望实现如图所示的执行流程,当用户问到天气信息的时候,大模型就能自动调用外部工具API查询天气并回复用户。这个看似简单的流程,实际上已经触及当代以大模型为核心的人工智能技术天花板了。首先第一个困难就是,这个调用外部工具的过程是根据用户意图触发的,而不是根据规则触发的。比如以下三个问题,看着类似,并且都出现了北京这个地名,但实际上用户的意图完全不同,并不能按照是否出现地名这一规则去查询天气。?- 今天北京好冷,我想待在家里,帮我推荐几部好看的电影?
这里第一个问题是“北京好冷,我想待家里,帮我推荐电影”,回答这个问题并不需要查询天气,但第二个问题“今天北京好冷,我想出门,要穿多点么?”则需要查询北京天气,而第三个问题,“今天北京好冷,我想去杭州去旅游,会不会更暖和些?”则同时需要查询北京和杭州两个地方的天气,而这还仅仅只有一个外部工具,当代智能体开发少则十几个外部工具,多则几十个外部工具,哪怕我们公开课讲解的DeepSeek手搓Mini Manus,都有6个外部工具,此外,哪怕大模型挑选了合适的工具,作为语言模型,大模型最多只能输出字符串,因此最多只能输出天气查询的需求,并不能亲自下场编写并运行代码,也就是说,大模型本身并不具备直接调用外部工具API的能力。为了解决这两方面问题,OpenAI首先采用一套非常特殊的模型训练流程,给大模型赋予了识别外部工具的能力。同时开创性的提出Function calling技术,通过创建一个外部函数作为中介,来传递大模型的调用请求,并完成API调用。这里我们先介绍Function calling的实现流程,然后再深入模型训练底层技术,来介绍这个能让大模型识别外部工具的训练流程。接下来咱们介绍的这个Function calling流程可是经典中的经典,现在无论是Agent开发框架还是MCP技术,本质上都是对Function calling流程的效率方面的优化,可以说搞懂Function calling流程就能一通百通。首先,Function calling的核心是需要创建一个外部函数来连接大模型和外部工具API,这个外部函数至少需要由两个部分构成,其一是函数说明,用于告诉大模型这个函数的输入和输出,其二就是具体调用外部工具API的代码,例如一个查询天气的外部函数标准格式如图所示,这个函数要求输入地名,即可调用Open Weather API进行天气查询,并返回查询到的天气结果:接下来我们借助这个外部函数,仅需三步就能让大模型顺利获取到天气信息:第一步,将用户的输入和外部函数说明同时带入对话,此时用户输入是“帮我查询北京天气”,而外部函数说明则是一种Json Schema格式的对象,这种对象是一种高度结构化的文本对象类型,用于告诉大模型这个外部函数输出和输出分别是什么,就相当于是外部函数的名片。如果是使用一些Agent开发框架或者MCP工具,这个“名片”会根据原始函数说明文档自动创建,此时写好函数原始说明文档就非常重要。第二步,模型会进行意图识别,若是无关问题,则会直接回复而如果问题和外部外部函数相关,例如询问北京今日天气,大模型则会向外部函数发送一条function call message,函数调用消息,其中包含了需要调用的函数名称和运行函数所需参数,第三步,当外部函数接收到函数调用消息消息时,就会自动带入参数并运行,例如将Beijing这个参数带入get_weather函数种进行运行,并获得北京天气,同时将其封装为一个function response message,加入原始消息列表,并让模型进行最终回复,这个过程如图所示:至此我们就完成了一次完整的Function calling运行流程。需要注意的是,上面介绍的这个执行流程,完全依赖模型原生能力完成,而如果我们采用Agent开发框架,还能大幅减少实际开发所需代码量,例如使用OpenAI前段时间开源的Multi-Agent开发框架Agents-SDK,中间过程可以100%全自动完成,一行代码就能完成Function calling全部流程。而大家如果觉得编写外部函数比较麻烦,近一段时间大火的MCP技术,则可以让开发者无缝接入海量外部工具开发生态中,琳琅满目的已经开发好的外部工具,搭配Agents-SDK,仅需设置一个参数,就能调用调用各种各样的外部工具。自此Agent开发流程就好比堆积木,借助MCP选择合适的外部工具,借助Agent开发框架组装运行。更多关于Function calling和Agent开发的代码实操教程,大家扫码即可领取。不过呢,我们无论使用何种Agent开发框架、无论是否使用MCP,大模型原生的Function calling能力都是让智能体顺利调用外部工具的关键,但是大家有没有好奇过,大模型原生的Function calling能力到底是从何而来的呢?我们又有什么方法能优化大模型的Function calling能力呢?这里我们从一个简单的例子入手进行分析,上面谈到在Function calling流程中,有一个这样的响应,就是当问题关联到外部工具时,大模型会创建一个function call message,乍看觉得没啥问题,但仔细观察我们会发现,这条消息并不是普通的文本响应结果,而是一条高度结构化的json格式的消息。也就是说这条消息以类似字典的形式,保存了需要调用的函数名称、以及需要给外部函数输入什么参数等高度结构化信息。这明显和普通的问答是完全不同的响应模式,那模型是如何在不同的响应模式之间进行切换的呢?答案非常简单,那就是现在的大模型,其实都是带入了一些特殊的标记字符一起进行训练,这些字符没有明确的文本含义,但通过将文本结构化分割,来让大模型学会不同任务模式下的响应方法。这是什么意思呢?举个例子,以DeepSeek-V3-0324模型为例,我们以为大模型响应过程是这样的:其中,这些<|begin▁of▁sentence|>、<|User|>等隐藏着的特殊标记,就是用于引导模型行为的文本结构标记。这些标记是和文本一起带入模型训练过程的,因此模型非常清楚这些标记的含义,例如<|User|>和<|Assistant|>中间代表着用户的提问,而<|begin▁of▁sentence|>、<|end▁of▁sentence|>则代表这一轮对话的全部内容。其实啊,对于DeepSeek R1模型来说<think>字符也是一种特殊标记,只不过允许被打印出来,好让用户区分思考和回复两种文本。好了,明白了这点,接下来模型的Function calling功能就不难理解了,而实际上,大模型的Function calling就是用特殊标记符来规范的一种特殊响应模式。例如当我们给大模型配置了一个查询天气的外部函数的时候,大模型接收到的完整文本如下所示,也就是说,无论我们使用什么Agent开发框架,只要关联了外部工具,大模型就会自动添加为一段系统提示词文本,用于描述外部函数的核心信息,包括函数名称、函数的输入和输出、以及一段外部函数使用简易说明,从而引导模型让其适时的去调用这些工具。当然,除此之外,输入的文本中肯定还包含一段通过特殊字符标记的用户输入的问题。而当模型接收到了这样的文本输入,就可能出现两种响应模式,其一是Function calling响应,也就是去创建那条调用外部函数的消息,此时表面上我们看到的消息是一个结构化文本,而真实的模型响应文本如下所示:此时模型会通过大量的特殊标记符,来规范模型的输出,也就是此时模型只能输出调用函数的名称,如get_weather,以及对应的参数,如Beijing,而这就构成了咱们前面所说的Function call message。而如果模型发现用户输入的问题和外部函数无关,比如用户输入“你好,好久不见”,那大模型就会按照如下方式进行响应。此时就没有那么多和tool相关的关键词了,很明显,这就是一个简单的一个文本的回应。好了,当我们理解了大模型其实是通过大量的特殊标记符来规范格式化输出之后,接下来的问题就是,大模型是如何同时拥有这两种截然不同的响应模式的呢?答案是,借助模型训练过程的指令微调方法,让模型能够有多种不同的响应方法。一般来说,大模型训练过程会分模型预训练和模型指令微调两个阶段,预训练阶段我们会带入海量的文本,训练模型基础语言能力,而指令微调阶段,则会带入大量的带有标签的文本数据,让模型学会各种问题应该如何回答。例如一个典型的指令微调的训练数据如图所示,其中会包含"instruction"字段,也就是系统提示词,以及“input”和“output”字段,也就是一组输入和输出的示例。而图上所展示的,是在一般系统提示词下的一个普通问答示例,"instruction": "你是一名助人为乐的助手。",
"input": "<|begin▁of▁sentence|><|User|>你是谁?<|Assistant|>",
"output": "我是一个智能助手,可以回答各种问题,请问有什么需要帮忙的?<|end▁of▁sentence|>"
除此之外,某些模型的训练过程中,还会在系统提示词中加入一些虚拟的外部工具信息,并在output字段中加入function call message的创建信息,这类训练数据如图所示:"instruction": "You have access to the following functions:\n\n### Function: get_weather\nDescription: 获取某地的天气信息\n\nParameters:\n- location (string): 城市名称,例如:北京、上海\n\n",
"input": "<|begin▁of▁sentence|><|User|>帮我查查上海今天的天气<|Assistant|>",
"output": "<|tool▁calls▁begin|><|tool▁call▁begin|>function<|tool▁sep|>get_weather\njson\n{ \"location\": \"上海\" }\n<|tool▁call▁end|><|tool▁calls▁end|><|end▁of▁sentence|>"
同时加入一些存在外部函数但只进行普通文本响应的训练数据,如图所示:"instruction": "You have access to the following functions:\n\n### Function: get_weather\nDescription: 获取某地的天气信息\n\nParameters:\n- location (string): 城市名称,例如:北京、上海\n\n",
"input": "<|begin▁of▁sentence|><|User|>你会做饭么?<|Assistant|>",
"output": "虽然我不会真正下厨,但我可以提供很多菜谱和烹饪建议哦!<|end▁of▁sentence|>"
通过这些数据的长期训练,就能让模型同时掌握对话能力和调用外部工具能力,同时,由于大模型本身具备举一反三的能力,因此在实际使用过程中遇到新的外部工具,大模型也能对其进行调用。至此,我们就明白了,为什么有的模型有Function calling而有的模型没有,其实都源于训练方法的不同。而需要注意的是,真正大模型训练流程其实非常复杂,要让模型诞生Function calling能力并不简单,例如DeepSeek R1模型,为了优先保障其推理能力,就放弃了对Function calling能力的训练。当然啦,如果模型本身没有Function calling能力,也可以通过一些提示词模版强制让模型输出类似Function call message这种结构的消息,例如这段时间大火的MCP,在clien或者Open-WebUI中调用时,就可以通过自带的强提示词模版让模型实现外部工具调用,但这种情况下调用准确率往往不够,远达不到工业级应用水准。类似开篇所谈到的不同情况下的查询天气的意图识别,是很难做到的。❤️- 今天北京好冷,我想待在家里,帮我推荐几部好看的电影?
而放眼全球,截至目前,原生Function calling能力能达到工业级水平的,也就只有GPT、Gemini、Claude和DeepSeek-V3-0324四款模型,这四款模型不仅拥有非常高的Function calling准确率,而且还训练得到了多工具并联和串联调用的能力,甚至当外部工具调用错误时还能自动debug,这些模型是当代Agent开发的不二之选。并且,作为最强开源对话模型,DeepSeek-V3-0324甚至还在huggingface上开源了网络搜索工具和文档检索工具训练时所采用的系统提示词模版,如果你希望V3模型去调用这类外部工具,使用训练时的提示词模版效果更好哦。file_template = \
"""[file name]: {file_name}
[file content begin]
{file_content}
[file content end]
{question}"""
search_answer_zh_template = \
'''# 以下内容是基于用户发送的消息的搜索结果:
{search_results}
在我给你的搜索结果中,每个结果都是[webpage X begin]...[webpage X end]格式的,X代表每篇文章的数字索引。请在适当的情况下在句子末尾引用上下文。请按照引用编号[citation:X]的格式在答案中对应部分引用上下文。如果一句话源自多个上下文,请列出所有相关的引用编号,例如[citation:3][citation:5],切记不要将引用集中在最后返回引用编号,而是在答案对应部分列出。
在回答时,请注意以下几点:
- 今天是{cur_date}。
- 并非搜索结果的所有内容都与用户的问题密切相关,你需要结合问题,对搜索结果进行甄别、筛选。
- 对于列举类的问题(如列举所有航班信息),尽量将答案控制在10个要点以内,并告诉用户可以查看搜索来源、获得完整信息。优先提供信息完整、最相关的列举项;如非必要,不要主动告诉用户搜索结果未提供的内容。
- 对于创作类的问题(如写论文),请务必在正文的段落中引用对应的参考编号,例如[citation:3][citation:5],不能只在文章末尾引用。你需要解读并概括用户的题目要求,选择合适的格式,充分利用搜索结果并抽取重要信息,生成符合用户要求、极具思想深度、富有创造力与专业性的答案。你的创作篇幅需要尽可能延长,对于每一个要点的论述要推测用户的意图,给出尽可能多角度的回答要点,且务必信息量大、论述详尽。
- 如果回答很长,请尽量结构化、分段落总结。如果需要分点作答,尽量控制在5个点以内,并合并相关的内容。
- 对于客观类的问答,如果问题的答案非常简短,可以适当补充一到两句相关信息,以丰富内容。
- 你需要根据用户要求和回答内容选择合适、美观的回答格式,确保可读性强。
- 你的回答应该综合多个相关网页来回答,不能重复引用一个网页。
- 除非用户要求,否则你回答的语言需要和用户提问的语言保持一致。
# 用户消息为:
{question}'''
DeepSeek-V3-0324模型开源地址:https://huggingface.co/deepseek-ai/DeepSeek-V3-0324并且,如果你的使用场景比较特殊,希望使用一些小尺寸模型,并提升某些特定的外部工具调用准确率,也可以通过微调方法,带入特定领域的Function calling数据集进行微调,也能大幅提升某些指定工具的调用准确率。目前llama-facroty或Qwen-Agent等微调工具,都支持Function calling能力微调。