LLM应用中的Function Calling机制
LLM应用中的Function Calling机制
LLM在当下应用非常广泛,从最常见的聊天问答(例如:Chatgpt,DeekSeek聊天),到基于内容检索的问答系统,处处可见它的身影。
在LLM(大型语言模型)应用开发中,Function Calling(函数调用)是一项强大的机制,它使得大模型不再局限于单纯的文本生成,而是能够与外部世界进行更深层次的交互。通过这种机制,我们可以赋予LLM调用特定工具或API的能力,从而解锁无数复杂且实用的应用场景。
什么是Function Calling
简单来说,Function Calling是一种让LLM模型理解并生成特定结构化数据(JSON格式),以触发外部函数或工具的能力。你不是直接让模型执行某个操作,而是告诉它“当用户想要做某件事时,请返回给我一个带有特定参数的函数名”。之后,你的应用代码会接收这个“函数调用”的指令,并负责真正去执行这个函数。
从定义来说,Function Calling是模型生成一个结构化数据来触发外部函数。
大体的工作流如下:
- 开发者提供函数描述(Schema)给LLM。
- 用户输入触发了某个函数的需求。
- LLM根据函数描述生成一个包含函数名和参数的JSON对象。
- 开发者应用代码解析并执行这个函数。
为什么需要Function Calling
如果没有Function Calling,LLM只能回答它“知道”的信息,比如“今天天气怎么样?”。它可能会生成一段关于天气的描述,但这并不能真正告诉你实时的天气情况。
LLM存在的不足
LLM的Function Calling机制是解决LLM存在的不足。
知识库的静态性
LLM本身是基于大量知识训练而成的,但是它的知识库却是静态的,它不能获取当前时间上海的天气情况。
模型能力有限
仍然以上面的例子说明,获取到的天气情况,如果我们想把它保存,LLM不能帮我们把它存储到数据库,用于以后分析使用。
模型输出内容可靠性有限
LLM一直都存在“幻想”问题,如果不能结合具体场景,它的回复内容可能也是不准确的,例如:让它推测API使用,并没有给出具体格式下,它给出的内容可能不是正确的。
如何实现一个简单的Function Calling应用
为了展示一个实现Function Calling应用实例,这里我选择了OpenRouter作为LLM云端,它适配了大量LLM供应商,模型选择比较方便。
这里需要注意一点,在OpenRouter上有大量的模型可用,还有很多都是免费的,如果要使用Function Calling机制,不管是免费的还是收费的模型,都要先查看一下它是否支持Function Calling机制,可以打开页面左侧的筛选(筛选支持Tools)。

我这里使用了DeepSeek V3.1模型进行演示,然后示例一个统计站点拨款文章数量的外部Function Calling,这里我们分布进行详细调用流程:
步骤一:定义函数(Tool Definition)
定义函数的目的是告诉LLM,本LLM应用支持哪些外部的Function Calling,实际就是创建外部函数的JSON Schema,并放入LLM的请求参数中。
如下示例:
{
"model": "deepseek/deepseek-chat-v3-0324",
"tools": [
{
"type": "function",
"function": {
"name": "count_of_articles",
"description": "Return of total count of blog articles in the website",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
}
],
"messages": [
// ...
]
}
这个示例是定义了一个统计博客站点一共有多少篇文章的外部函数。LLM获取到tools中定义的外部函数时,就会解析它其中提供的函数,对于函数的用法,什么含义,是通过参数description,parameters来指定。
步骤二:调用LLM API
在JSON中定义好函数后,我们就需要把它作为请求发送给LLM,此外我们需要把我们的文本请求发在messages中,例如:
{
// ...
"messages": [
{
"role": "system",
"content": "你是AI助手,负责回答回答用户一些问题,便于用户快速获取博客文章的信息。告诉用户使用次数较多时,将会引发限制。"
},
{
"role": "user",
"content": "站点有多少篇文章?"
}
],
}
添加完上述请求后,就可以把整个请求发送给LLM了。
步骤三:解析并执行函数
LLM处理上述请求时,发现关于文章数量统计的有一个外部Function Calling,于是它返回的结果是如下:
{
"id": "gen-1750571355-aHglwd9IFp4rXZs92i",
"provider": "Novita",
"model": "deepseek/deepseek-chat-v3-0324",
"object": "chat.completion",
"created": 1750571355,
"choices": [
{
"logprobs": null,
"finish_reason": "tool_calls",
"native_finish_reason": "tool_calls",
"index": 0,
"message": {
"role": "assistant",
"content": "",
"refusal": null,
"reasoning": null,
"tool_calls": [
{
"index": 0,
"id": "call_7gp5viqwa4lku1jy1xep1tfw",
"type": "function",
"function": {
"name": "count_of_articles",
"arguments": "{}"
}
}
]
}
}
],
// ...
}
它会返回assistant角色的tool_calls请求,请求包含请求的外部函数名(function.name)和参数(function.arguments),同时需要注意这里的tool_calls.id,它在后面需要用到。
步骤四:将函数执行结果反馈给LLM
当我们获取到了LLM返回外部函数Function Calling时,我们通过返回的函数请求去执行外部函数并把结果添加到消息体中。
外部函数
这里的外部函数可以是本地执行的函数,查找或执行一项本地事务,但是也可以是远程的一个接口函数哦,通过远程API Key形式来获取远程事务的结果。
这里假如说我们的站点博客外部函数最终统计出了232篇文章,那么我们需要再把统计结果放入消息体中,如下:
{
// ...
"messages": [
{
"role": "system",
"content": "你是AI助手,负责回答回答用户一些问题,便于用户快速获取博客文章的信息。告诉用户使用次数较多时,将会引发限制。"
},
{
"role": "user",
"content": "站点有多少篇文章?"
},
{
"role": "tool",
"tool_call_id": "call_7gp5viqwa4lku1jy1xep1tfw",
"name": "count_of_articles",
"content": "232"
}
],
}
添加的消息角色是tool,同时指定tool_id(tool_call_id),和函数名与结果。然后再把这个请求发送给LLM,LLM最终会给出自然语言级别的回复,如下:
{
// ...
"choices": [
{
"logprobs": null,
"finish_reason": "stop",
"native_finish_reason": "stop",
"index": 0,
"message": {
"role": "assistant",
"content": "目前站点共有232篇文章。如果查询次数较多,可能会触发限制,请注意合理使用。",
"refusal": null,
"reasoning": null
}
}
],
}
到这里我们完成了一个整体的Function Calling调用机制,在实际应用中,还有更多的玩法。
高级应用与最佳实践
关于Function Calling的使用,上述至少演示了一个简单的单个外部示例,根据实际应用场景还有更复杂的玩法。
多工具调用(Multi-tool Calling)
注意到上述只定义了一个外部函数(统计博客数量),实际应用可能不止这一个函数,可能是多个功能各异的外部函数,提供给LLM使用。
和简单示例相比,多工具下只是定义函数上不同,其他基本没有太大的改变,如下是定义多个外部工具函数:
{
// ...
"tools": [
{
"type": "function",
"function": {
"name": "count_of_articles",
"description": "Return of total count of blog articles in the website",
"parameters": {
// ...
}
}
},
{
"type": "function",
"function": {
"name": "search_article",
"description": "Search a specific article by the content",
"parameters": {
// ...
}
}
}
],
// ...
}
看起来是不是很简单,对于定义了多个外部工具函数的情况,LLM是会自动选择正确函数的(主要通过函数描述来判断的,所以函数描述不要随便乱写)。
链式调用(Chaining)
链式调用是一个比较热门的技术风格,它把和LLM对话的每次会话抽象为一个会话事务,这样每个会话事务是为了解决一个问题的其中一个步骤(这样的方式下,就可以缓解LLM的Token数量限制问题,同时也符合问题分解的思维方式),把这些事务步骤连成一条链就是解决一个复杂问题的解决方案。
LangChain
LangChain是一个专门为实现和管理“链式调用”而设计的框架,它是开源的,它实现了一整套的调用工具箱,非常适合上手。
关于链式调用,我可以举例说明一个,比方说做一个小说内容总结系统,对于传统意义上,只在一个LLM会话上,一篇小说当然无法塞进去的,所以需要使用问题分解的思路,比方说定义一些外部函数:
- 统计有哪些章节
- 统计章节内容大小,并分段总结段内容
- 汇总章节内容
- 汇总整篇小说内容
图示流程如下:

LangChain的改进
对于LangChain的问题,那就是它是顺序化,对于复杂问题难免导致链过长的问题,于是又有了LangGraph的技术方案,LangGraph的主要特点就是把能够可以并行执行的事物并行执行,这样可以更快地获取的问题的答案,但是它也带来了系统的复杂性。
总结与展望
LLM的Function Calling提供了允许LLM调用外部函数的能力,使它能够和外部世界更好地互动。它是与外部世界连接的桥梁,在AI智能体,和自动化领域有非常广阔的情景,未来这方面的技术将会持续迭代与演进。