Advertisement

还不懂 Function Calling? 看完这篇就行

阅读量:

函数调用

前言

使用大型语言模型进行函数调用 (Function Calling) 是一个庞大且不断发展的主题。这对AI应用尤为重要:

无论是为了绕过当前AI技术的局限性,而设计的原生AI应用,

还是为了提升性能、用户体验或效率,寻求整合AI技术的现有应用。

先不深入讨论LLM在应用中应扮演的角色及相关的最佳实践。这些观点反映在AI应用框架的设计上:从LangChain到LlamaIndex再到XXXAgent。

相反,我们将讨论如何使用XXX 来支持函数调用,以及如何利用它实现你的目标,从开发应用时的推理用途,到硬核定制的内部运作。在这个指南中,

我们首先将展示如何使用XXX 进行函数调用。

接着,我们将介绍使用XXX 进行函数调用的技术细节,主要涉及模板的使用。

在开始之前,还有一件事我们尚未介绍,那就是…

什么是函数调用?

这一概念也可能被称为“工具使用” (“tool use”)。虽然有人认为“工具”是“函数”的泛化形式,但在当前,它们的区别仅在技术层面上,表现为编程接口的不同输入输出类型。

大型语言模型(LLMs)确实强大。然而,有时候单靠大型语言模型的能力还是不够的。

一方面,大型语言模型存在建模局限性。首先,对于训练数据中没有的信息,包括训练结束后发生的事情,它们并不了解。此外,它们通过概率方式学习,这意味着对于有固定规则集的任务,如数学计算,可能不够精确。

另一方面,将大型语言模型作为即插即用服务与其它系统进行编程式协作,并非易事。大型语言模型的表达多含主观解释成分,因而产生歧义;而其他软件、应用或系统则通过预定义、固定和结构化的代码及编程接口进行沟通。

为此,函数调用确立了一个通用协议,规定了大型语言模型应与其他实体互动的流程。主要流程如下:

应用程序向大型语言模型提供一组函数及其使用说明。

大型语言模型根据用户查询,选择使用或不使用,或被迫使用一个或多个函数。

如果大型语言模型选择使用这些函数,它会根据函数说明如何使用。

应用程序按照选择使用这些函数,并获取结果。如果需要进一步互动,结果将提供给大型语言模型。

大型语言模型理解并遵循此协议有多种方式。关键在于提示工程 (Prompt Engineering) 或模型内化的模板。XXX2预先训练了多种支持函数调用的模板,以便用户可以直接利用这一过程。

使用函数调用进行推理

注意,随着框架和XXX模型的不断演进,推理的使用方式可能会发生变化。

由于函数调用本质上是通过提示工程实现的,您可以手动构建XXX2模型的输入。但是,支持函数调用的框架可以帮助您完成所有繁重的工作。

接下来,我们将介绍(通过专用的函数调用模板)使用

XXX-Agent,

Hugging Face transformers,

Ollama

vLLM

如果您熟悉OpenAI API的使用,您也可以直接使用适用于XXX 的OpenAI兼容API服务。然而,并非所有服务都支持XXX 的函数调用。目前,支持的解决方案包括由Ollama或vLLM提供的自托管服务和阿里云百炼的云服务。

如果您熟悉应用框架,例如LangChain,您也可以通过ReAct Prompting在XXX 中使用函数调用功能。

案例

我们同样通过一个示例来展示推理的使用方法。假设我们使用的编程语言是Python 3.11。

场景:假设我们要询问模型某个地点的温度。通常,模型会回答无法提供实时信息。但我们有两个工具,可以分别获取城市的当前温度和指定日期的温度,我们希望模型能够利用这些工具。

为了这个示例案例,您可以使用以下代码:

工具应使用JSON Schema进行描述,消息应包含尽可能多的有效信息。您可以在下面找到工具和消息的解释:

XXX-Agent

XXX-Agent 实际上是一个用于开发AI应用的Python智能体框架。尽管其设计用例比高效推理更高级,但它确实包含了XXX 函数调用的规范实现。基于OpenAI兼容API,它可以通过模板为XXX 提供了对用户透明的的函数调用能力。

值得注意的是,由于应用框架可以在幕后完成大量工作,目前XXX 官方的函数调用实现非常灵活且超出了简单的模板化,这使得它难以适应那些使用能力较弱的模板引擎的其他框架。

在开始之前,让我们确保已安装了最新的库:

复制代码
    pip install -U XXX-agent
    For this guide, we are at version v0.0.10.
    
    
    bash
准备工作

XXX-Agent可以封装一个不支持函数调用的OpenAI兼容API。您可以使用大多数推理框架来提供此类API,或者从DashScope或Together等云提供商处获取一个。

假设在http://localhost:8000/v1处有一个OpenAI兼容API,XXX-Agent提供了一个快捷函数get_chat_model,用于获取具有函数调用支持的模型推理类:

复制代码
    from XXX_agent.llm import get_chat_model
    
    llm = get_chat_model({
    "model": "XXX/XXX -7B-Instruct",
    "model_server": "http://localhost:8000/v1",
    "api_key": "EMPTY",
    })
    
    
    bash

在上述代码中,model_server是其他OpenAI兼容API客户端常用的api_base。建议您提供api_key(但不要以明文形式出现在代码中),即使API服务器不检查它,在这种情况下,您可以将其设置为任何值。

对于模型输入,应使用系统、用户和助手历史记录的通用消息结构:

复制代码
    messages = MESSAGES[:]
    # [
    #    {"role": "system", "content": "You are XXX, created by Alibaba Cloud. You are a helpful assistant.\n\nCurrent Date: 2024-09-30"},
    #    {"role": "user", "content": "What's the temperature in San Francisco now? How about tomorrow?"}
    # ]
    
    
    bash

我们在系统消息中添加当前日期,以便使用户消息中的”明天”有明确的参照点。如果需要,也可以将其添加到用户消息中。

目前,XXX-Agent使用“函数”而非“工具”。这需要对我们工具描述进行一些小的更改,即提取函数字段:

functions = [tool[“function”] for tool in TOOLS]
工具调用和工具结果
为了与模型交互,应使用chat方法:

复制代码
    for responses in llm.chat(
    messages=messages,
    functions=functions,
    extra_generate_cfg=dict(parallel_function_calls=True),
    ):
    pass
    messages.extend(responses)
    
    
    bash

在上述代码中,chat方法接收messages、functions以及一个extra_generate_cfg参数。你可以在extra_generate_cfg中放入诸如temperature和top_p等采样参数。这里,我们添加了XXX-Agent提供的特殊控制parallel_function_calls。顾名思义,它将启用并行函数调用,这意味着模型可能为单次请求生成多个函数调用,按照其判断进行。

chat方法返回一个列表的生成器,每个列表可能包含多条消息。因为我们启用了parallel_function_calls,我们应该在响应中得到两条消息:

复制代码
    [
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_current_temperature', 'arguments': '{"location": "San Francisco, CA, USA", "unit": "celsius"}'}},
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_temperature_date', 'arguments': '{"location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'}},
    ]
    
    
    bash

我们可以看到,XXX-Agent试图以更易于使用的结构化格式解析模型生成。与函数调用相关的详细信息被放置在消息的function_call字段中:

name:代表要调用的函数的字符串

arguments:表示函数应带有的参数的JSON格式字符串

请注意,XXX -7B-Instruct相当强大:

它遵循函数指令,在位置中添加了州和国家。

它正确地推断出明天的日期,并以函数要求的格式给出。

接下来是关键部分——检查和应用函数调用:

复制代码
    for message in responses:
    if fn_call := message.get("function_call", None):
        fn_name: str = fn_call['name']
        fn_args: dict = json.loads(fn_call["arguments"])
    
        fn_res: str = json.dumps(get_function_by_name(fn_name)(**fn_args))
    
        messages.append({
            "role": "function",
            "name": fn_name,
            "content": fn_res,
        })
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/vmCWyxlFtrVDj5K8OuPURJehcaNE.png)

获取工具结果:

第1行:我们应该按模型生成它们的顺序迭代函数调用。

第2行:通过检查生成消息的function_call字段,我们可以查看是否需要按模型判断进行函数调用。

第3-4行:相关详情,包括函数名称和参数,也可以在那里找到,分别是name和arguments。

第6行:有了这些细节,应该调用函数并获取结果。这里,我们假设有一个名为get_function_by_name的函数来帮助我们根据名称获取相关函数。

第8-12行:获得结果后,将函数结果作为content添加到消息中,并将role设置为"function"。

现在消息是

复制代码
    [
    {'role': 'system', 'content': 'You are XXX, created by Alibaba Cloud. You are a helpful assistant.\n\nCurrent Date: 2024-09-30'},
    {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"},
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_current_temperature', 'arguments': '{"location": "San Francisco, CA, USA", "unit": "celsius"}'}},
    {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_temperature_date', 'arguments': '{"location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'}},
    {'role': 'function', 'name': 'get_current_temperature', 'content': '{"temperature": 26.1, "location": "San Francisco, CA, USA", "unit": "celsius"}'},
    {'role': 'function', 'name': 'get_temperature_date', 'content': '{"temperature": 25.9, "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'},
    ]
    
    
    bash
最终响应

最后,再次模型以获取最终的模型结果:

复制代码
    for responses in llm.chat(messages=messages, functions=functions):
    pass
    messages.extend(responses)
    
    
    bash

最终响应应如下所示

复制代码
    {'role': 'assistant', 'content': 'Currently, the temperature in San Francisco is approximately 26.1°C. Tomorrow, on 2024-10-01, the temperature is forecasted to be around 25.9°C.'}
    
    
    bash

Hugging Face transformers

由于函数调用基于提示工程和模板,transformers通过其tokenizer工具支持这一功能,特别是tokenizer.apply_chat_template方法,它利用Jinja模板引擎隐藏了构建模型输入的复杂性。然而,这意味着用户需要自行处理模型输出部分,包括解析生成的函数调用消息。

博客文章Tool Use, Unified对于理解其设计非常有帮助。务必阅读一下。

自v4.42.0版本起,transformers中提供了工具使用API。在开始之前,让我们确认这一点:

复制代码
    pip install "transformers>4.42.0"
    
    
    bash

对于本指南,我们处于v4.44.2版本。

准备工作
对于 XXX ,tokenizer_config.json 中的聊天模板已经包含了对 Hermes 风格工具调用的支持。我们只需加载模型和分词器:

复制代码
    from transformers import AutoModelForCausalLM, AutoTokenizer
    
    model_name_or_path = "XXX/XXX -7B-Instruct"
    
    tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)
    model = AutoModelForCausalLM.from_pretrained(
    model_name_or_path,
    torch_dtype="auto",
    device_map="auto",
    )
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/nBV5jNLJyfRKA3GbiOdosmkgcC7a.png)

输入与准备代码中的相同:

复制代码
    tools = TOOLS
    messages = MESSAGES[:]
    
    
    bash

在transformers中,您也可以直接将Python函数作为工具使用,但需遵循特定约束[1]:

复制代码
    tools = [get_current_temperature, get_temperature_date]
    
    
    bash

工具调用和工具结果
为了构造输入序列,我们应该使用apply_chat_template方法,然后让模型继续生成文本:

复制代码
    text = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, tokenize=False)
    inputs = tokenizer(text, return_tensors="pt").to(model.device)
    outputs = model.generate(**inputs, max_new_tokens=512)
    output_text = tokenizer.batch_decode(outputs)[0][len(text):]
    
    
    bash

输出文本应如下所示:

复制代码
    <tool_call>
    {"name": "get_current_temperature", "arguments": {"location": "San Francisco, CA, USA"}}
    </tool_call>
    <tool_call>
    {"name": "get_temperature_date", "arguments": {"location": "San Francisco, CA, USA", "date": "2024-10-01"}}
    </tool_call><|im_end|>
    
    
    bash

现在我们需要做两件事:

解析生成的工具调用为一条消息,并将其添加到消息列表中,以便模型了解所使用的工具。

获取工具的结果并将其添加到消息列表中,以便模型了解工具调用的结果。

In transformers, the tool calls should be a field of assistant messages. Let’s use a simple function called try_parse_tool_calls to parse the tool calls:

复制代码
    import re
    
    def try_parse_tool_calls(content: str):
    """Try parse the tool calls."""
    tool_calls = []
    offset = 0
    for i, m in enumerate(re.finditer(r"<tool_call>\n(.+)?\n</tool_call>", content)):
        if i == 0:
            offset = m.start()
        try:
            func = json.loads(m.group(1))
            tool_calls.append({"type": "function", "function": func})
            if isinstance(func["arguments"], str):
                func["arguments"] = json.loads(func["arguments"])
        except json.JSONDecodeError as e:
            print(f"Failed to parse tool calls: the content is {m.group(1)} and {e}")
            pass
    if tool_calls:
        if offset > 0 and content[:offset].strip():
            c = content[:offset]
        else: 
            c = ""
        return {"role": "assistant", "content": c, "tool_calls": tool_calls}
    return {"role": "assistant", "content": re.sub(r"<\|im_end\|>$", "", content)}
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/7sTQ3P5jkWGav6X0DeM4oOq8EHdr.png)

This function does not cover all possible scenarios and thus is prone to errors. But it should suffice for the purpose of this guide.

备注

tokenizer_config.json 中的模板假设生成的内容和工具调用是在同一消息中,而不是分开的助手消息,例如:

{ “role”: “assistant”, “content”: “To obtain the current
temperature, I should call the functions get_current_temperate.”,
“tool_calls”: [
{“type”: “function”, “function”: {“name”: “get_current_temperature”, “arguments”: {“location”: “San Francisco,
CA, USA”, “unit”: “celsius”}}} ] } 而非

[ {
“role”: “assistant”,
“content”: “To obtain the current temperature, I should call the functions get_current_temperate.”, }, {
“role”: “assistant”,
“content”: “”,
“tool_calls”: [
{“type”: “function”, “function”: {“name”: “get_current_temperature”, “arguments”: {“location”: “San Francisco,
CA, USA”, “unit”: “celsius”}}}
] } ] try_parse_tool_calls 中大致实现了这一约定,但如果你正在编写自己的工具调用解析器,请留意这一点。

复制代码
    messages.append(try_parse_tool_calls(output_text))
    
    if tool_calls := messages[-1].get("tool_calls", None):
    for tool_call in tool_calls:
        if fn_call := tool_call.get("function"):
            fn_name: str = fn_call["name"]
            fn_args: dict = fn_call["arguments"]
    
            fn_res: str = json.dumps(get_function_by_name(fn_name)(**fn_args))
    
            messages.append({
                "role": "tool",
                "name": fn_name,
                "content": fn_res,
            })
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/U2K4M3EosN7w9QOc156maz8yLlIp.png)

现在消息应如下所示:

复制代码
    [
    {'role': 'system', 'content': 'You are XXX, created by Alibaba Cloud. You are a helpful assistant.\n\nCurrent Date: 2024-09-30'},
    {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"},
    {'role': 'assistant', 'content': '', 'tool_calls': [
        {'type': 'function', 'function': {'name': 'get_current_temperature', 'arguments': {'location': 'San Francisco, CA, USA'}}}, 
        {'type': 'function', 'function': {'name': 'get_temperature_date', 'arguments': {'location': 'San Francisco, CA, USA', 'date': '2024-10-01'}}},
    ]},
    {'role': 'tool', 'name': 'get_current_temperature', 'content': '{"temperature": 26.1, "location": "San Francisco, CA, USA", "unit": "celsius"}'},
    {'role': 'tool', 'name': 'get_temperature_date', 'content': '{"temperature": 25.9, "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'},
    ]
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/EyTWYFLZuHO1Ud5P4vmeVbl8cJ6j.png)

这些消息类似于XXX-Agent的消息,但存在一些主要差异:

工具而非函数

默认情况下为并行调用

    • 多个工具调用以列表形式在一个助手消息中,而不是多个消息
    • 如果函数参数是有效的JSON格式字符串,则将其解析为字典。
最终响应

现在是时候根据工具结果,让模型为我们生成实际响应了。再次查询模型:

复制代码
    text = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, tokenize=False)
    inputs = tokenizer(text, return_tensors="pt").to(model.device)
    outputs = model.generate(**inputs, max_new_tokens=512)
    output_text = tokenizer.batch_decode(outputs)[0][len(text):]
    
    
    bash

输出文本应如下所示:

复制代码
    The current temperature in San Francisco is approximately 26.1°C. Tomorrow, on October 1, 2024, the temperature is expected to be around 25.9°C.<|im_end|>
    
    
    bash

将结果文本作为助手消息添加,最终消息应准备好进行进一步交互:

复制代码
    messages.append(try_parse_tool_calls(output_text))
    
    
    bash

Ollama

Ollama是一套用于本地部署LLMs的工具集。它还依赖于其模板实现来支持函数调用。不同于使用Python编写的transformers,采用了受Django和Python语法启发的Jinja模板,主要用Go编写的Ollama则使用了Go的text/template包。此外,Ollama内部实现了辅助函数,如果格式被支持的话,它可以自动解析文本中生成的工具调用为结构化的消息。

您可以先查阅Tool support的博客文章。

自v0.3.0版本以来,Ollama已经提供了工具支持。您可以以下命令来检查Ollama的版本:

复制代码
    ollama -v
    
    
    bash

如果版本低于预期,请遵循官方说明安装最新版本。

在本指南中,我们将使用ollama-python,在开始之前,请确保您的环境中已安装此库:

复制代码
    pip install ollama
    
    
    bash

对于本指南,ollama二进制文件的版本为v0.3.9,ollama Python库的版本为v0.3.2。

准备工作

Ollama 中使用的消息结构与 transformers 中的相同,并且 XXX Ollama 模型 的模板已经支持工具调用。

输入与准备代码中的相同:

复制代码
    tools = TOOLS
    messages = MESSAGES[:]
    
    model_name = "XXX :7b"
    
    
    bash

请注意,您不能直接将Python函数作为工具传递,tool的类型必须是dict。

工具调用和工具结果
我们可以使用ollama.chat方法直接查询底层API:

复制代码
    import ollama
    
    response = ollama.chat(
    model=model_name,
    messages=messages,
    tools=tools,
    )
    
    
    bash

响应中的主要字段可能是:

复制代码
    {
    'model': 'XXX :7b',
    'message': {
        'role': 'assistant',
        'content': '',
        'tool_calls': [
            {'function': {'name': 'get_current_temperature', 'arguments': {'location': 'San Francisco, CA, USA'}}},
            {'function': {'name': 'get_temperature_date', 'arguments': {'date': '2024-10-01', 'location': 'San Francisco, CA, USA'}}},
        ],
    },
    }
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/lw1XoucmCIQd06s3xnqNrSLgFtpb.png)

Ollama’s tool call parser has succeeded in parsing the tool results. If not, you may refine the try_parse_tool_calls function above. Then, we can obtain the tool results and add them to the messages. The following is basically the same with transformers:

复制代码
    messages.append(response["message"])
    
    if tool_calls := messages[-1].get("tool_calls", None):
    for tool_call in tool_calls:
        if fn_call := tool_call.get("function"):
            fn_name: str = fn_call["name"]
            fn_args: dict = fn_call["arguments"]
    
            fn_res: str = json.dumps(get_function_by_name(fn_name)(**fn_args))
    
            messages.append({
                "role": "tool",
                "name": fn_name,
                "content": fn_res,
            })
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/vr3GHFmw46VDApQXnfT70CUqayPl.png)

现在消息如下:

复制代码
    [
    {'role': 'system', 'content': 'You are XXX, created by Alibaba Cloud. You are a helpful assistant.\n\nCurrent Date: 2024-09-30'},
    {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"},
    {'role': 'assistant', 'content': '', 'tool_calls': [
        {'function': {'name': 'get_current_temperature', 'arguments': {'location': 'San Francisco, CA, USA'}}},
        {'function': {'name': 'get_temperature_date', 'arguments': {'date': '2024-10-01', 'location': 'San Francisco, CA, USA'}}},
    ]},
    {'role': 'tool', 'name': 'get_current_temperature', 'content': '{"temperature": 26.1, "location": "San Francisco, CA, USA", "unit": "celsius"}'},
    {'role': 'tool', 'name': 'get_temperature_date', 'content': '{"temperature": 25.9, "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'},
    ]
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/wougyIKRQa4j693vLSPfh02dtUVc.png)

最终响应
剩下的部分很简单:

复制代码
    response = ollama.chat(
    model=model_name,
    messages=messages,
    tools=tools,
    )
    messages.append(response["message"])
    
    
    bash

最终的消息应该如下所示:

复制代码
    {'role': 'assistant', 'content': 'The current temperature in San Francisco is approximately 26.1°C. For tomorrow, October 1st, 2024, the forecasted temperature will be around 25.9°C.'}
    
    
    bash

vLLM

vLLM 是一个快速且易于使用的库,用于大型语言模型的推理和部署。它使用 transformers 中的分词器来格式化输入,因此我们在准备输入时应该不会遇到任何问题。此外,vLLM 还实现了辅助函数,以便在支持的情况下自动解析生成的工具调用。

工具支持自 v0.6.0 版本起已在 vllm 中可用。请确保安装了一个支持工具调用的版本。更多信息,请查阅 vLLM 文档

在本指南中,我们使用的是 v0.6.1.post2 版本。我们将使用 vllm 提供的 OpenAI 兼容 API,并通过 openai Python 库的 API 客户端来进行操作。

准备工作

对于 XXX ,tokenizer_config.json 中的聊天模板已经包含了对 Hermes 风格工具调用的支持。我们只需要启动一个由 vLLM 提供的 OpenAI 兼容 API 即可:

复制代码
    vllm serve XXX/XXX -7B-Instruct --enable-auto-tool-choice --tool-call-parser hermes
    
    
    bash

输入与准备代码中的相同:

复制代码
    tools = TOOLS
    messages = MESSAGES[:]
    
    
    bash

我们先初始化API客户端:

复制代码
    from openai import OpenAI
    
    openai_api_key = "EMPTY"
    openai_api_base = "http://localhost:8000/v1"
    
    client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
    )
    
    model_name = "XXX/XXX -7B-Instruct"
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Drs5Qc4OGMmiYgw1vT6XW7LJZxNI.png)

工具调用和工具结果
我们可以使用create chat completions endpoint直接查询底层API:

复制代码
    response = client.chat.completions.create(
    model=model_name,
    messages=messages,
    tools=tools,
    temperature=0.7,
    top_p=0.8,
    max_tokens=512,
    extra_body={
        "repetition_penalty": 1.05,
    },
    )
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/nNLKEzmWTfv9MD7kalopu0risPB8.png)

vLLM应当可以为我们解析工具调用,回复的主要字段(response.choices[0])应如下所示:

复制代码
    Choice(
    finish_reason='tool_calls', 
    index=0, 
    logprobs=None, 
    message=ChatCompletionMessage(
        content=None, 
        role='assistant', 
        function_call=None, 
        tool_calls=[
            ChatCompletionMessageToolCall(
                id='chatcmpl-tool-924d705adb044ff88e0ef3afdd155f15', 
                function=Function(arguments='{"location": "San Francisco, CA, USA"}', name='get_current_temperature'), 
                type='function',
            ), 
            ChatCompletionMessageToolCall(
                id='chatcmpl-tool-7e30313081944b11b6e5ebfd02e8e501', 
                function=Function(arguments='{"location": "San Francisco, CA, USA", "date": "2024-10-01"}', name='get_temperature_date'), 
                type='function',
            ),
        ],
    ), 
    stop_reason=None,
    )
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/IifqYZsOacS8QAwL9jVoeX7z3mEN.png)

请注意这里函数的参数是JSON格式字符串,XXX-Agent与其一致,但transformers和Ollama与之相异。

如前所述,有可能存在边界情况,模型生成了工具调用但格式不良也无法被解析。对于生产代码,我们需要尝试自行解析。

随后,我们可以调用工具并获得结果,然后将它们加入消息中:

复制代码
    messages.append(response.choices[0].message.model_dump())
    
    if tool_calls := messages[-1].get("tool_calls", None):
    for tool_call in tool_calls:
        call_id: str = tool_call["id"]
        if fn_call := tool_call.get("function"):
            fn_name: str = fn_call["name"]
            fn_args: dict = json.loads(fn_call["arguments"])
        
            fn_res: str = json.dumps(get_function_by_name(fn_name)(**fn_args))
    
            messages.append({
                "role": "tool",
                "content": fn_res,
                "tool_call_id": call_id,
            })
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/HlakSjBDT93Q2YfNvKdceOnmrZsu.png)

这里需要注意OpenAI API使用tool_call_id字段来识别工具结果和工具调用间的联系。

现在消息如下:

复制代码
    [
    {'role': 'system', 'content': 'You are XXX, created by Alibaba Cloud. You are a helpful assistant.\n\nCurrent Date: 2024-09-30'},
    {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"},
    {'content': None, 'role': 'assistant', 'function_call': None, 'tool_calls': [
        {'id': 'chatcmpl-tool-924d705adb044ff88e0ef3afdd155f15', 'function': {'arguments': '{"location": "San Francisco, CA, USA"}', 'name': 'get_current_temperature'}, 'type': 'function'},
        {'id': 'chatcmpl-tool-7e30313081944b11b6e5ebfd02e8e501', 'function': {'arguments': '{"location": "San Francisco, CA, USA", "date": "2024-10-01"}', 'name': 'get_temperature_date'}, 'type': 'function'},
    ]},
    {'role': 'tool', 'content': '{"temperature": 26.1, "location": "San Francisco, CA, USA", "unit": "celsius"}', 'tool_call_id': 'chatcmpl-tool-924d705adb044ff88e0ef3afdd155f15'},
    {'role': 'tool', 'content': '{"temperature": 25.9, "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}', 'tool_call_id': 'chatcmpl-tool-7e30313081944b11b6e5ebfd02e8e501'},
    ]
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/ys9Sizl3r5FVAYfCHuZpPjhv7XJD.png)

最终响应
让我们再次查询接口,以给模型提供工具结果并获得回复:

复制代码
    response = client.chat.completions.create(
    model=model_name,
    messages=messages,
    tools=tools,
    temperature=0.7,
    top_p=0.8,
    max_tokens=512,
    extra_body={
        "repetition_penalty": 1.05,
    },
    )
    
    messages.append(response.choices[0].message.model_dump())
    
    
    bash
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/RiDchNWvIkYl6X1VZyPELrSqnUuz.png)

The final response (response.choices[0].message.content) should be like

The current temperature in San Francisco is approximately 26.1°C. For tomorrow, the forecasted temperature is around 25.9°C.

小结

现在,我们已经介绍了如何使用XXX2在三种不同的框架中通过函数调用进行推理!让我们做一个简要的比较。

OpenAI API附带了Python、Node.js、Go和.NET SDK。它还遵循OpenAPI标准。

Ollama附带了Python和Node.js SDK。它在不同的base URL上具有与OpenAI兼容的API,可以使用OpenAI API SDK访问。

作为应用程序框架,XXX-Agent可以自动为您调用工具,这在XXX-Agent指南中有所介绍。

此外,在函数调用的模型方面还有更多内容,这意味着您可能需要在生产代码中考虑更多的事情:

函数调用准确性:在评估函数调用的准确性时,有两个方面:(a) 是否选择了正确的函数(包括没有函数)以及(b) 是否生成了正确的函数参数。XXX 并不总是准确的。函数调用可能涉及深入且领域特定的知识。有时,它不能完全理解函数并错误地选择了错误的函数。有时,它可能会陷入循环,需要反复调用相同的函数。有时,它会伪造所需的函数参数而不是向用户请求输入。为了提高函数调用的准确性,建议首先尝试提示工程:更详细的函数描述是否有所帮助?我们是否可以在系统消息中为模型提供指导和示例?如果没有,使用自己的数据进行微调也可以提高性能。

协议一致性:即使具备恰当的函数调用模板,协议也可能被破坏。模型可能会在工具调用中生成额外文本,例如解释说明。生成的工具调用可能是无效的JSON格式字符串,但是是Python dict的字符串表示;生成的工具调用可能是有效的JSON,但不符合提供的JSON Schema。对于这类问题,虽然有些可以通过提示工程解决,但有些是由大型语言模型的本质引起的,很难由大模型本身以通用方式解决。尽管我们在这一方面努力改进XXX ,但极端情况不太可能被完全消除。

函数调用模板

函数调用的模板设计通常包括以下方面:

如何向模型描述这些函数,以便模型理解它们是什么以及如何使用它们。

如何提示模型,以便它知道可以使用函数,并以何种格式生成函数调用。

如何从生成的文本中区分函数调用与其他内容,以便我们能够从生成的文本中提取调用并实际执行调用。

如何将函数结果融入文本中,以便模型能够将其与自己的生成区分开来,并在调用和结果之间建立联系。

对于经验丰富的提示工程师而言,应该有可能利用上下文学习技术和代表性示例,使任何大模型支持函数调用,尽管准确性和稳定性会根据手头任务的“零样本”程度而有所不同。

全部评论 (0)

还没有任何评论哟~