深入解析大语言模型的 Function Call 实现—— 以 Qwen2.5为例
GitHub代码仓库
引言
在现代大语言模型(LLM)中,Function Call(函数调用)能力极大地提升了模型的实用性,使其能够调用外部 API、执行复杂计算或获取实时数据。例如,在 OpenAI API 和 Qwen2.5-7B-Instruct 这样的模型中,用户可以向模型提供工具(Tools),并允许模型在适当的时候调用它们。
本文以 Qwen2.5-7B-Instruct 为例,探讨其 Function Call 机制的底层实现。
1. Function Call 机制概述
通常,大语言模型的输入输出都是字符串,但 Function Call 允许模型在对话过程中识别特定的函数调用需求,并以结构化数据格式返回调用参数。
在 Qwen2.5-7B-Instruct 中,Function Call 由以下几部分组成:
-
messages:对话历史,包括用户、系统、助手的消息。
-
tools:可供调用的函数信息,描述了函数名称、参数格式等。
2. Function Call 的 Prompt 生成
在 Qwen2.5-7B-Instruct 后端,所有 messages 和 tools 会被应用到一个固定的模板上,以便模型正确解析和执行 Function Call。
2.1 获取模板
from transformers import Qwen2ForCausalLM, Qwen2TokenizerFast
model_name_or_path = "Qwen/Qwen2.5-7B-Instruct"
tokenizer = Qwen2TokenizerFast.from_pretrained(model_name_or_path)
print(tokenizer.get_chat_template())
此代码返回一个 Prompt 生成模板,该模板会根据 messages 和 tools 生成最终的输入格式。
2.2 模板内容解析
模板的核心部分如下:
{%- if tools %}
{{- '<|im_start|>system\n' }}
{%- if messages[0]['role'] == 'system' %}
{{- messages[0]['content'] }}
{%- else %}
{{- 'You are Qwen, created by Alibaba Cloud. You are a helpful assistant.' }}
{%- endif %}
{{- "\n\n# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
{%- for tool in tools %}
{{- "\n" }}
{{- tool | tojson }}
{%- endfor %}
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
{%- else %}
{%- if messages[0]['role'] == 'system' %}
{{- '<|im_start|>system\n' + messages[0]['content'] + '<|im_end|>\n' }}
{%- else %}
{{- '<|im_start|>system\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- for message in messages %}
{%- if (message.role == "user") or (message.role == "system" and not loop.first) or (message.role == "assistant" and not message.tool_calls) %}
{{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
{%- elif message.role == "assistant" %}
{{- '<|im_start|>' + message.role }}
{%- if message.content %}
{{- '\n' + message.content }}
{%- endif %}
{%- for tool_call in message.tool_calls %}
{%- if tool_call.function is defined %}
{%- set tool_call = tool_call.function %}
{%- endif %}
{{- '\n<tool_call>\n{"name": "' }}
{{- tool_call.name }}
{{- '", "arguments": ' }}
{{- tool_call.arguments | tojson }}
{{- '}\n</tool_call>' }}
{%- endfor %}
{{- '<|im_end|>\n' }}
{%- elif message.role == "tool" %}
{%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != "tool") %}
{{- '<|im_start|>user' }}
{%- endif %}
{{- '\n<tool_response>\n' }}
{{- message.content }}
{{- '\n</tool_response>' }}
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
{{- '<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- if add_generation_prompt %}
{{- '<|im_start|>assistant\n' }}
{%- endif %}
- 工具(Tools)定义:当提供 tools 时,系统消息(System Message)中会插入一段描述工具的文本,包括工具的 JSON 格式定义。
- 函数调用返回格式:要求助手在返回函数调用时,使用 <tool_call></tool_call> XML 标签包裹 JSON 格式的调用信息。
- 消息格式:不同角色的消息采用 <|im_start|>role\nmessage<|im_end|> 的格式,保证模型能够正确解析对话内容。
3. 应用模板并生成 Prompt
当 messages 和 tools 被应用到该模板后,会生成如下格式的输入文本:
text = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, tokenize=False)
print(text)
3.1 示例输入
{
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [
{"role": "system", "content": "你是Qwen, 由阿里巴巴创建.\n\nCurrent Date: 2025-03-15"},
{"role": "user", "content": "北京的气温是多少?"}
],
"tools": [
{
"name": "get_current_temperature",
"description": "Get current temperature at a location.",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The location to get the temperature for."},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit to return the temperature in."}
},
"required": ["location"]
}
}
]
}
3.2 生成的 Prompt
<|im_start|>system
你是Qwen, 由阿里巴巴创建.
Current Date: 2025-03-15
# Tools
You may call one or more functions to assist with the user query.
You are provided with function signatures within <tools></tools> XML tags:
<tools>
{"type": "function", "function": {"name": "get_current_temperature", "description": "Get current temperature at a location.", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "The location to get the temperature for, in the format \"City, State, Country\"."}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit to return the temperature in. Defaults to \"celsius\"."}}, "required": ["location"]}}}
</tools>
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call><|im_end|>
<|im_start|>user
北京的气温是多少?<|im_end|>
<|im_start|>assistant
4. 运行模型并解析输出
在生成 text 后,我们需要调用 Qwen2.5-7B-Instruct 进行推理,并解析返回的函数调用。
4.1 加载模型并进行推理
model = Qwen2ForCausalLM.from_pretrained(
model_name_or_path,
torch_dtype="auto",
device_map="auto",
)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
model.eval()
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=512)
output_text = tokenizer.batch_decode(outputs)[0][len(text):]
print(output_text)
4.2 生成的输出
<tool_call>
{"name": "get_current_temperature", "arguments": {"location": "北京, 北京市, 中国", "unit": "celsius"}}
</tool_call><|im_end|>
5. 结论
Qwen2.5-7B-Instruct 通过结构化的模板和XML 格式化的函数调用,让 LLM 能够有效调用外部工具。
- 模板结构清晰:采用 <|im_start|> 和 <|im_end|> 明确区分对话内容。
- 工具调用明确:使用 定义可用函数,并在 <tool_call></tool_call> 结构中返回函数调用。
- 易于扩展:可以轻松添加多个 tools,支持复杂应用场景。
这种 Function Call 机制为 LLM 在实际应用中提供了极大的灵活性,使其能在多种任务中高效执行函数调用并获取外部信息。