FunctionCalling原理
# Function Calling是什么
# 引言
在人工智能领域,大语言模型(LLM)的发展日新月异。从最初只能进行简单文本生成的基础模型,到如今具备强大语言理解和生成能力的先进系统,大语言模型已经深刻改变了我们与计算机交互的方式。然而,传统大语言模型存在着一些局限性,如无法获取实时数据、难以执行复杂任务等。为了克服这些问题,Function Calling应运而生,它为大语言模型与外部世界的交互提供了一种全新的解决方案,使得大语言模型能够更加智能、高效地完成各种任务。
# Function Calling诞生的背后故事
# 大模型的知识局限性
我们常见的大模型,如OpenAI的GPT-4、智谱AI的GLM4等,大多是预训练大模型。这些模型的知识基于其训练数据的时间范围,例如2024年2月更新的GPT-4-0125-preview,其训练数据截止在2023年12月,因此对于2024年发生的事情它一无所知,甚至无法获知当前时间。这就导致大模型在面对需要实时信息的问题时显得无能为力。
# 传统解决方案的不足
在2023年6月13日之前,大模型本身并没有调用外部函数的能力。开发AI应用通常依赖特定的框架或平台,如Semantic Kernel或Langchain来实现互联网搜索或调用外部API。在这个过程中,设计提示词是关键,但由于每个人编写的提示词结构不同,无法实现标准化,导致准确率大打折扣。
此外,OpenAI曾引入插件功能,但该功能存在诸多缺陷。例如,同时使用的插件数量受限,同一时间最多只能激活和使用三个插件,用户无法构建具有一定复杂性和强大功能的AI应用;缺乏有效的Agent调度机制,用户只能手动选择有限数量的插件,限制了功能的灵活性和扩展性;无法为特定场景提供完整的端到端解决方案,影响了用户体验和解决问题的效率;需要多次与GPT进行交互,导致整体响应延迟高,使得插件在实际应用中的性能表现不佳。最终,插件商店在2024年4月被废弃。
# Function Calling的诞生
为了解决上述问题,Function Calling机制在2023年6月13日随着OpenAI的GPT大模型的更新发布而为人所知。它允许大模型直接调用外部函数和API,以获取实时数据和执行特定任务,大大简化了开发过程,推动了大模型技术的实际应用。
# Function Calling的原理
# 工作流程概述
Function Calling的工作流程通常包括以下几个关键步骤:
- 定义函数:开发者需要定义外部函数及其参数。例如,定义一个查询天气的函数:
def get_weather(location: str) -> str:
"""获取指定城市的实时温度"""
mock_data = {"北京": 28, "上海": 30, "深圳": 32}
return f"{location}当前气温:{mock_data.get(location, 26)}℃"
2
3
4
- 用户请求:用户向模型提出请求,如“深圳现在多少度?”
- 模型生成函数调用:模型识别需要调用外部函数的部分,并生成函数调用参数。例如,模型可能生成如下调用信息:
{
"index": 0,
"id": "call_667d5e06ea7243c38b9082",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"深圳\"}"
}
}
2
3
4
5
6
7
8
9
10
调用外部函数:开发者根据模型生成的参数调用外部函数。
get_weather("深圳")
1返回结果给模型:将函数调用的结果返回给模型。例如,将获取到的天气信息返回给模型,模型再将其整合为自然语言回复给用户。
整理最终结果:模型根据函数调用的结果整理最终答案。
工作流:
# 核心原理剖析
- 意图识别:模型通过语义分析理解用户请求的目标,例如识别出用户是在查询天气。训练时会引入函数调用的示例数据,使模型学习何时触发调用。例如,标注“深圳现在多少度?”对应调用天气API,并提取参数
{location: "深圳"}
。 - 函数匹配:模型内部有一个函数目录或知识库,存储了各种可调用函数的信息,包括函数的功能描述、参数要求等,也可以使用提示词吧函数喂给大模型。模型会将解析出的语义与这些函数信息进行匹配,找到最适合的函数。
- 参数生成:确定要调用的函数后,模型会根据文本中的相关内容生成正确的参数,将自然语言描述转换为函数能够接受的参数格式。
- 执行回调与结果整合:开发者的代码执行真实业务逻辑,调用外部函数并获取结果。然后将结果反馈给模型,模型将其融入自然语言回复。同时,支持多轮对话,模型可根据函数返回结果决定后续操作,如数据不足时调用其他API。
# Function Calling的架构
# 技术栈与关键特征
Function Calling的技术栈具有一些关键特征:
- 语义理解层:基于few - shot learning的意图识别准确率可达92%以上,能够准确理解用户的意图。
- 参数结构化:支持JSON Schema定义复杂数据结构,确保模型生成的参数格式符合函数要求,便于函数的准确调用。
- 动态上下文:支持多轮对话中的持续状态维护,使得模型在多轮交互中能够保持对上下文的理解,更好地完成任务。
# 实现模式
常见的实现模式有以下几种:
模式 | 适用场景 | 延迟 | 安全性 |
---|---|---|---|
同步直连 | 内部系统调用 | <2s | 高 |
异步队列 | 批处理任务 | 可变 | 中 |
沙箱执行 | 代码解释器 | 3 - 5s | 低 |
插件市场 | 第三方服务集成 | 2 - 4s | 可变 |
# 应用架构设计示例
以智能工单系统为例,其架构设计如下:
- 用户发起请求:用户向大语言模型反馈“产线设备报错代码E105”。
- 模型调用工具:大语言模型调用ToolGateway的get_error_code手册。
- 查询数据:ToolGateway向ERP系统进行SQL查询E105。
- 返回结果:ERP系统返回散热系统故障信息给ToolGateway,ToolGateway再将故障解决方案反馈给大语言模型。
- 生成工单:大语言模型调用ToolGateway创建维修工单,ToolGateway将工单信息写入ERP系统。
- 反馈用户:大语言模型告知用户已安排工程师处理。
# 性能优化策略
为了提高Function Calling的性能,可采用以下策略:
- 缓存机制:对高频查询结果建立向量缓存,可将命中率提升40%,减少重复查询的开销。
- 批量处理:合并同类请求降低API调用次数,提高系统的处理效率。
- 模型蒸馏:构建轻量级路由模型,可将模型体积缩小80%,减少计算资源的占用。
- 流式响应:分段返回降低首字节时间,提升用户体验。
# 安全与治理框架
- 权限控制矩阵:明确不同用户角色对不同工具的使用权限,例如“financial_data”只能由“L3 + 管理者”访问,“production_data”可由“生产部员工”访问,“system_config”仅“IT管理员”可操作。
- 审计追踪设计:采用全链路请求追踪(如X - Ray集成),对敏感操作设置二次确认机制,并建立动态权限回收系统,确保系统的安全性和可追溯性。
# 开发实践
# 原生代码实现
以OpenAI API为例,典型的开发流程如下:
定义函数库:开发者预先声明可用函数及参数格式。例如:
def get_weather(location: str) -> str: """获取指定城市的实时温度(示例数据)""" mock_data = {"北京": 28, "上海": 30, "深圳": 32} return f"{location}当前气温:{mock_data.get(location, 26)}℃"
1
2
3
4用户请求与模型响应:用户输入问题,如“深圳现在多少度?”,模型返回函数调用指令。
- url:
/chat/completions
- 用户的请求体:
{ "model": "qwen-plus", "messages": [ { "role": "user", "content": "深圳现在多少度?" } ], "tools": [ // 必须带上 tools 声明 { "type": "function", // tool 类型 "function": { "name": "get_weather", "description": "获取指定城市的实时温度", // tool 描述 "parameters": { // 参数信息 "type": "object", "properties": { "location": { "type": "string", "description": "城市名称,如:北京" } }, "required": [ "location" ] } } } ], "tool_choice": "auto" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31- url:
模型生成函数调用:模型识别需要调用外部函数的部分,并选择和生成函数调用参数。模型返回的响应:
{ "choices": [ // 选择的 tools { "message": { "content": "", "role": "assistant", // 角色为assistant "tool_calls": [ { "index": 0, "id": "call_667d5e06ea7243c38b9082", // tool的ID "type": "function", "function": { "name": "get_weather", // tool名称 "arguments": "{\"location\": \"深圳\"}" // 参数 } } ] }, "finish_reason": "tool_calls", "index": 0, "logprobs": null } ], "object": "chat.completion", "usage": { "prompt_tokens": 174, "completion_tokens": 17, "total_tokens": 191, "prompt_tokens_details": { "cached_tokens": 0 } }, "created": 1748228293, "system_fingerprint": null, "model": "qwen-plus", "id": "chatcmpl-a8dafc1f-8092-9456-953e-7a55ff7be5c8" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37用户执行函数:用户根据模型选择的
tool
来执行函数。get_weather("深圳")
1返回结果给模型:用户系统调用天气API获取数据后,将结果返回模型,生成最终回答。
- url:
/chat/completions
。 - 请求体:
{ "model": "qwen-plus", "messages": [ { // 带上历史 message "role": "user", "content": "深圳现在多少度?" }, { // 模型选择的 tool "content": "", "role": "assistant", "tool_calls": [ { "index": 0, "id": "call_667d5e06ea7243c38b9082", // tool 的 ID "type": "function", "function": { "name": "get_weather", "arguments": "{\"location\": \"深圳\"}" } } ] }, { // 执行 tool 的结果 "tool_call_id": "call_667d5e06ea7243c38b9082", // tool 的 ID "role": "tool", "name": "get_weather", "content": "深圳当前气温:32℃" } ], "tools": null, "tool_choice": "auto" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32- url:
整理最终结果:模型根据函数调用的结果整理最终答案。
{ "choices": [ { "message": { "content": "深圳当前的气温是 **32℃**。请注意,气温可能会随时间和天气变化而有所波动,建议关注最新的天气预报,尤其是在户外活动时做好防暑降温措施! ☀️", "role": "assistant" }, "finish_reason": "stop", "index": 0, "logprobs": null } ], "object": "chat.completion", "usage": { "prompt_tokens": 31, "completion_tokens": 42, "total_tokens": 73, "prompt_tokens_details": { "cached_tokens": 0 } }, "created": 1748228369, "system_fingerprint": null, "model": "qwen-plus", "id": "chatcmpl-06026535-5586-95ba-aa4b-75ddaa6a9baf" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
完整的代码见:。
# OpenAI SDK实现
# 初始化OpenAI客户端
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url=os.environ.get("DASHSCOPE_API_BASE"),
)
def create_chat_completion(messages: list, use_tools: bool = False):
"""创建带工具调用的聊天完成"""
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的实时温度",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,如:北京"
}
},
"required": ["location"]
}
}
}
]
return client.chat.completions.create(
model=MODEL_NAME,
messages=messages,
tools=tools,
tool_choice="auto" if use_tools else None
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
完整代码见:
# LangChain实现
def main():
llm = ChatOpenAI(
api_key=API_KEY,
base_url=BASE_URL,
model=MODEL_NAME,
)
llm_with_tools = llm.bind_tools([get_weather])
query = "深圳现在多少度?"
messages = [HumanMessage(query)]
ai_msg = llm_with_tools.invoke(messages)
messages.append(ai_msg) # 添加tool的选择
print("调用的tools:", ai_msg.tool_calls)
for tool_call in ai_msg.tool_calls:
selected_tool = {"get_weather": get_weather}[tool_call["name"].lower()]
tool_msg = selected_tool.invoke(tool_call)
messages.append(tool_msg)
print("messages:", messages)
res = llm_with_tools.invoke(messages)
print(res.model_dump_json())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# LLamaIndex实现
def get_weather(location: str) -> str:
"""获取指定城市的实时温度"""
mock_data = {"北京": 28, "上海": 30, "深圳": 32}
return f"{location}当前气温:{mock_data.get(location, 26)}℃"
def main():
tool = FunctionTool.from_defaults(fn=get_weather)
llm = OpenAILike(
model=MODEL_NAME,
api_base=BASE_URL,
api_key=API_KEY,
is_chat_model=True,
is_function_calling_model=True,
)
response = llm.predict_and_call(
[tool],
"深圳现在多少度?",
)
print(str(response))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 总结
# Function Calling的优势总结
Function Calling为大语言模型带来了诸多优势:
- 扩展模型能力:突破了模型固有知识的限制,使模型能够获取实时信息,处理更复杂的任务,如查询数据库、调用API、执行计算等。
- 精准控制:约束输出格式(如JSON/XML),确保下游系统能够更好地兼容模型的输出,提高了数据处理的准确性和效率。
- 动态任务处理:模型可以根据用户请求动态选择需要调用的函数,实现灵活的任务处理,满足不同场景的需求。
# 面临的挑战
尽管Function Calling具有强大的功能,但也面临一些挑战:
- 函数定义的一致性:不同大模型供应商的接口格式略有差异,开发者在支持多个大模型时需要进行适配,增加了开发的复杂性。
- 准确性问题:模型可能错误匹配函数或参数,需要强化训练和参数校验,提高调用的准确性。
- 安全性风险:函数调用涉及与外部系统的交互,需要严格控制权限,防止恶意请求和数据泄露。
- 不兼容:部分大模型不支持Function Calling,例如DeepSeek R1。
# 未来展望
随着技术的不断发展,Function Calling有望在更多领域发挥重要作用:
- Agent生态系统:构建自主智能体协作网络,使多个智能体能够通过Function Calling相互协作,完成更复杂的任务。
- 物理世界接口:实现与IoT设备的直接控制,通过Function Calling让大模型能够控制现实世界中的各种设备,如智能家居、工业设备等。
- 动态工具发现:基于语义的自动工具组合,使模型能够根据任务需求自动发现和组合合适的工具,提高任务处理的效率和灵活性。
Function Calling是大语言模型发展中的一项重要技术,它为大模型与外部世界的交互提供了强大的支持。尽管面临一些挑战,但随着技术的不断进步和完善,Function Calling必将在人工智能领域发挥越来越重要的作用,推动AI应用向更加智能、高效的方向发展。