大模型的函数调用是你想象的这样吗

66 阅读10分钟

混沌随想 混沌随想

首页

关于作者

⚙️

  • 大模型的函数调用是你想象的这样吗

2025年9月17日

大模型的函数调用是你想象的这样吗

函数调用有原生和非原生两种,在 2024 年,大多开源模型不支持原生的函数调用,2025 年新出的模型普遍都支持 openai 类的 Function call。

最近尝试用 Qwen3 235B A22B 的原生函数调用,调试了很多场景,发现在原生的 Function call 下,指令遵守能力很弱,甚至不如之前 qwen 2.5 的提示词函数调用。然后尝试转之前的非原生方案,发现效果反而更好。在 2024 年应用 qwen 2.5 的时候,当时研究过一些 paper,原生函数调用会限制模型的自由,从而降低性能。看起来这个问题在开源模型中至今(2025 年下旬)还是有。为此,继续深入研究了这个问题。

函数调用的底层

原生函数调用

函数调用是由大量意图识别、工具调用训练 + 约束解码两个步骤组成。前者通常是大量的训练保证模型准确的理解意图,并调用工具。但由于概率的存在,模型不可能保证 100% 的完整 json 。 原生函数调用能 100 % 输出 json 靠约束解码算法的功劳 ,这个算法会在 LLM每次 token 产生候选词的时候,对那些不符合 json格式的候选者设置负无限大的概率,从而强制模型跳过那些不合法的 json token。

基于提示词的函数调用

与原生调用不同,提示工程使用并不依赖于模型任何内置的功能。它是通过精心设计的提示词来引导任何具备强大指令遵循能力的LLM完成工具调用任务。

通过提示词构造类似的 prompt,核心思路是告诉模型它的角色、可用的工具(函数),以及必须遵循的输出格式。

一个典型的提示词结构如下:

你是一个智能助手,可以调用外部工具来完成用户的请求。
根据用户的需求,从以下可用工具列表中选择一个或多个最合适的工具。
你必须严格按照指定的JSON格式返回你的响应,不要添加任何额外的解释或对话。

# 可用工具列表
[
  {
    "name": "get_current_weather",
    "description": "获取指定城市的实时天气信息。",
    "parameters": {
      "type": "object",
      "properties": {
        "location": {
          "type": "string",
          "description": "城市名称,例如:北京"
        },
        "unit": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "温度单位"
        }
      },
      "required": ["location"]
    }
  },
  {
    "name": "send_email",
    "description": "向指定收件人发送邮件。",
    "parameters": {
      "type": "object",
      "properties": {
        "recipient": {
          "type": "string",
          "description": "收件人的电子邮件地址"
        },
        "subject": {
          "type": "string",
          "description": "邮件主题"
        },
        "body": {
          "type": "string",
          "description": "邮件正文内容"
        }
      },
      "required": ["recipient", "subject", "body"]
    }
  }
]

# 输出格式
请将你的思考过程和最终要调用的工具封装在一个JSON对象中,并用 ```json ... ``` 包裹。
格式如下:
{
  "thought": "这里是你对用户需求的分析过程,说明你为什么选择这个工具以及如何填充参数。",
  "tool_calls": [
    {
      "function": "工具名称",
      "arguments": {
        "参数1": "值1",
        "参数2": "值2"
      }
    }
  ]
}

应用程序的后端接收到这个包含JSON的字符串后,需要进行解析和处理。这个过程通常分为两步:提取和解析。

  1. 提取JSON字符串

程序需要从模型的完整响应中准确地抽取出被 json … 包裹的内容。这可以通过正则表达式或简单的字符串分割来实现。

2. 解析JSON并执行调用

拿到纯净的JSON字符串后,程序会尝试将其转换成可操作的对象(如Python中的字典)。

由于模型概率的存在,基于提示词的函数调用无法保证100%,但通常能通过防御性解析,避免大多数错误,在极端情况解析识别,再由 LLM 重新生成。

性能对比

原生的函数调用一定是最佳的吗?

一项早期研究 arxiv.org/abs/2408.02… 表明,更宽松的提示通常会产生更好的结果, JSON 约束解码模式在大多数情况下表现较差,其次是 FRI(通过提示词来指导模型)、NL to Format (先输出自然语言,再转成格式化)。

但这项研究是针对去年和前年的模型,得出格式的限制,尤其是约束解码(JSON 模式),会阻碍推理能力。这和从笔者的长期对开源模型的体验中的感受一致,基于提示词的效果比原生函数调用更好。

由于 2025 年,新的开源模型普遍针对原生函数调用做了优化,于是继续深入调查了一些研究。

其中找到 ProlLM函数调用最新排行榜(2025/08),它测试了完全相同的模型在分别使用原生函数调用和提示工程方法时的性能表现。

函数准确率(Function Accuracy):衡量模型是否选择了正确的函数来响应用户请求。

参数正确性(Argument Correctness):衡量在选择了正确的函数之后,模型为其提供的参数是否准确无误。

Deepseek V3 和 sonnet 4.0在使用提示工程(Prompting)方法时,其函数准确率均高于其使用原生函数调用(Function Calling)。

但在 Berkeley Function-Calling Leaderboard 平台,原生函数调用分数明显比基于提示词的高。

说明基于提示词的函数调用版本依赖提示词的设计水平。

提示实现的优化方案

最早期的做法是通过提示词构造 json 格式,常见的有

你是Agent ,任务是 xx

请按格式返回如下json 用``` json ``` 包裹
``` json
{
  "function": "edit_file",
  "arguments": {
    "file": "app.py",
    "changes": "add error handling"
  }
}
```

但 json 有一个很大的问题,就是转义符号的处理非常困难,比如双引号的value 内部出现大段代码的时候,很容易出现内部双引号,这个时候模型需要额外添加转义符 / 。在推理任务复杂的情况,很容易导致 json 解析失败,从而浪费时间。

多项研究表明,像 JSON 这样的受限生成格式会降低模型在复杂推理任务上的性能。

XML 工具调用消除了这些限制,同时保持了结构和可解析性:

<edit_file>
<file>app.py</file>
<changes>add comprehensive error handling with logging</changes>
</edit_file>

在 Morph Apply 进行的测试中,XML 工具调用始终优于 JSON:

畸形工具调用减少 30%, 代码质量得分提高 25%,生成速度提高 40% (约束开销更小), 错误恢复率提高 60%。

在 claude 官方推荐的自定义格式也是 xml , sonent 模型对 xml 有着天然的大量训练。Cursor 的部分版本系统提示显示大量使用 XML 进行工具调用:

<summarization>
If you see a section called "<most_important_user_query>", you should treat that query as the one to answer, and ignore previous user queries. If you are asked to summarize the conversation, you MUST NOT use any tools, even if they are available. You MUST answer the "<most_important_user_query>" query.
</summarization>

笔者最近测试了 XML 替代传统的 json以及原生函数调用,在 qwen3 的表现明显更佳。这里的理解是: xml 属于 html 一类标签语言,天然被大量训练。且 xml 解决转义问题,即使出现标签闭合等格式问题,也能通过程序自动发现和修复,比 json的转义处理更简单,在越复杂的任务越复杂,XML 的优势就越大。

原生函数调用其他问题

在是否选择推理(比如 r1、 qwen3-Thinking等)模型选择的时候,我们经常会为性能和时间两者之间纠结,在纯性能上,推理模型的工具调用能力高很多。但目前开源模型的推理时间不能像 o3 这样控制思考的预算,导致一些简单的任务,模型思考过久,从而浪费时间。

而非推理模型,比如 v3、 Qwen3-Instruct 等,则容易陷入因缺乏思考导致工具调用不准的问题。

通常原生的函数调用能支持让模型先思考,再返回工具。

{
  "role": "assistant",
  "content": "我来为您查询北京的天气信息。",
  "tool_calls": [
    {
      "id": "call_abc123",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"location\": \"北京\", \"unit\": \"celsius\"}"
      }
    }
  ]
}

如上,”我来为您查询北京的天气信息。”本身是模型在工具调用前思考的内容,我们可以通过提示词控制这个思考的长度。但目前测试 Qwen3 235B A22B 不是很稳定,其他开源模型也类似(qwen3-coder 表现更好)。

但我们始终无法把 content 当作思考的过程,因为它本身是需要渲染给用户的内容。

通过 XML ,我们能精确控制推理时间、显示内容以及工具调用,并且强制思考过程,降低了原生函数调用意图识别的不稳定。

一步一步分析,不控制在 30-50字之间 最终显示给用户的内容 工具调用

任何特定格式的有效性都与模型的训练数据高度相关。开发者社区的经验表明,不同模型对结构化数据的处理能力差异显著 ,例如,某些模型(如Claude系列)被观察到能出色地处理XML,而其他模型可能因为其训练语料中包含了大量的Web API和代码库,而对JSON更为熟悉 (reddit社区反馈小模型对 json 更好)。

这意味着不存在一个普遍“最优”的格式。对于工程负责人而言,在选择基础模型时,其对不同结构化数据格式的熟练度应成为一个关键的评估标准,且特定的业务数据可能和格式相关(由于 LLM 的 xml 训练大量是代码)。所以,仅仅依赖通用基准测试是不够的,必须进行针对特定格式和用例的内部评估。

——

相关参考

arxiv.org/abs/2408.02…)

www.prollm.ai/leaderboard…)

gorilla.cs.berkeley.edu/leaderboard…

docs.morphllm.com/guides/xml-…

www.reddit.com/r/LocalLLaM…

来源公众号:混沌随想
个人站点 :imwangfu.com

📝 本文自动同步自知乎,格式排版可能异常,其包含图片、视频内容可能无法正常显示和播放。

原文链接:点击查看原文

——————
文档信息

标题:大模型的函数调用是你想象的这样吗

发表时间:2025年9月17日

笔名:混沌随想

原链接:imwangfu.com/2025/09/_7u…

版权声明:如需转载,请邮件知会 imwangfu@gmail.com,并保留此文档信息申明

更多深度随想可以关注公众号:混沌随想

知乎:混沌随想

——————

更新时间:

2025年9月22日, 08:45:43

基于 AI 自动同步自 imwangfu.com