首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >大模型怎么知道该调哪个工具?function calling 决策机制拆解

大模型怎么知道该调哪个工具?function calling 决策机制拆解

原创
作者头像
用户11136834
发布于 2026-10-04 02:14:29
发布于 2026-10-04 02:14:29
270
举报

前言

本系列前两篇讲了 MCP 的接入(SDK 十几行跑起 Server)和协议层(60 行手写 JSON-RPC)。但留了一个最关键的问题没拆:客户端把 20 个工具清单递给大模型之后,模型是怎么决定"这次该调 echo、还是直接回答、还是先调两个工具再回答"的?

这篇文章就拆这一环——function calling 的决策机制。搞懂它,你才能真正解释那些日常玄学:为什么工具描述改一个词,模型就不调了;为什么工具挂多了,回答反而变笨;为什么模型的参数偶尔是坏的 JSON。

一、先纠正一个普遍误解:模型从不"执行"工具

很多人以为"模型调用了工具",好像模型伸手去执行了什么。真相是:模型只做一件事——生成文本,只不过是一段特殊格式的文本。

完整的决策循环分五步,执行方是应用,不是模型:

  1. 应用把工具定义(名称、描述、参数 schema)和用户问题一起放进请求;
  2. 模型生成一段结构化输出:我要调用 get_forecast,参数是 {"city": "上海"};
  3. 应用解析这段输出,真正去执行(本地函数 / HTTP 接口 / MCP 的 tools/call);
  4. 应用把执行结果作为一条新消息塞回对话;
  5. 模型看到结果,生成最终的自然语言回答。

所以工具调用本质是一场应用与模型之间的结构化对白。模型是大脑,应用是手脚。理解了这一点,后面所有机制都顺理成章。

二、工具定义是怎么被模型"看见"的:不是查询,是全量塞入

第二个常见误解:以为工具定义放在某个"工具库"里,模型按需查询。实际是——每一次请求,所有工具定义都会序列化成文本,塞进模型的上下文窗口,和用户消息一起算输入 token。

这意味着三件事:

  1. 工具定义按 token 计费。一个中等复杂度的工具定义约一两百 token,20 个工具就是每轮对话多出几千 token 的固定开销,而且每一轮都要付;
  2. 工具越多,选择越难。模型要在一段更长的工具清单里做匹配,注意力被稀释,选错、不选、瞎选的概率都上升;
  3. 上下文窗口是硬约束。工具定义 + 对话历史 + 系统提示加在一起,超窗就报错或被截断。

这也是官方生态在推"渐进式工具发现"(按需加载工具清单)的原因——工具一多,全量注入的模式顶不住。

三、决策机制本体:模型怎么"选"

3.1 生成层面:调不调工具,也是一个 token 决定

模型逐 token 生成。走到"该输出内容"的位置时,它面对的候选里既有普通文本的 token,也有"开始一段工具调用"的特殊 token。选哪个,是由上下文概率决定的——用户问题与哪个工具描述更匹配,那个方向的概率就越高。

所以工具描述不是文档,是决策特征。描述里写着"获取天气预报,输入城市名",用户问天气时这段文字被激活的概率就高;描述写得含糊,概率就流向"直接回答"。

3.2 开关层:tool_choice 参数

主流 API 都提供工具选择开关,常用四档:

  • auto:模型自己决定调不调、调哪个(默认);
  • none:禁止调用,只准聊天;
  • required:必须调用至少一个工具;
  • 指定工具:强制调用某个具名工具。

工程上这很有用:确定性流程用强制指定,开放对话用 auto。

3.3 并行调用与多轮循环

复杂问题模型可以一次生成多个工具调用(并行调用),也可以在一轮里"调工具 → 看结果 → 再调另一个工具"地走多步。整个循环由应用驱动:只要有工具调用输出,执行完把结果塞回去再请求,直到模型输出纯文本为止。

四、一个几乎人人都踩的坑:arguments 是字符串

看一段真实的消息流(OpenAI 风格),注意第二段的 tool_calls:

代码语言:javascript
复制
[
  { "role": "user", "content": "上海明天热吗" },
  {
    "role": "assistant",
    "tool_calls": [
      {
        "id": "call_001",
        "type": "function",
        "function": {
          "name": "get_forecast",
          "arguments": "{\"city\": \"上海\"}"
        }
      }
    ]
  },
  { "role": "tool", "tool_call_id": "call_001", "content": "{\"temp\": 31, \"cond\": \"多云\"}" },
  { "role": "assistant", "content": "上海明天 31 度,多云,稍微有点热。" }
]

注意看 arguments 那行:它是一个JSON 字符串,不是 JSON 对象。因为模型的输出本质是文本流,参数是逐 token 拼出来的字符串,API 原样交付。所以应用侧必须自己 JSON.parse,而且必须 try-catch——模型偶尔会产出带尾逗号、单引号甚至截断的坏 JSON。这是 function calling 工程化的第一道防线。

五、工程调优清单:让模型选得更准

把机制翻译成可操作的清单:

  1. 命名即语义:get_weather 比 query_data 好;同族工具用统一前缀(todo_add / todo_list / todo_complete);
  2. 描述写给模型看:说清"做什么、什么时候用、什么时候不用",别写给人看的营销话;
  3. 参数用枚举收窄:type 用 enum 限定取值,比让模型自由发挥字符串稳定得多;
  4. required 只放真正必需的:可选参数越多,模型填错的面越大;
  5. 错误消息要"可读":工具失败时返回"城市名无法识别,请传行政区划全名"这种描述性错误,模型下一轮能自我修正;返回一个裸 Error 就浪费了一次重试机会;
  6. 工具数量控制:十几个以内体验最好;更多就做分组路由(先调一个"目录工具"问有哪些能力,再加载对应子集);
  7. 不要用工具包装常识:问"1+1"也触发计算器工具,是描述写得过宽。

六、串回 MCP:完整链路

现在把三篇文章串成一条线:

  1. MCP 客户端启动时调 tools/list,拿到工具清单(第一篇的 registerTool 注册的那些);
  2. Host 把清单转换成模型 API 的 tools 参数,随每次请求注入(本文第二节);
  3. 模型生成 tool_calls,Host 解析后通过 MCP 的 tools/call 发给你的 Server(第二篇手写的那个方法);
  4. 结果回填,循环直到模型给出最终回答。

MCP 负责运输,function calling 负责决策,应用负责执行——三层各司其职,这就是"AI 应用的手和眼"的全部解剖图。

小结

function calling 没有魔法:工具定义是塞进上下文的文本,"调用"是模型生成的一段特殊文本,选哪个工具由描述与问题的匹配概率决定。记住三个工程锚点——描述即决策��征、arguments 必须防坏 JSON、工具越多越钝——你写出来的工具生态,模型用起来会准得多。

参考

  1. OpenAI Function Calling 文档(tool_choice / tool_calls 消息格式)
  2. Anthropic Tool Use 文档
  3. 本系列:《给大模型装上「USB-C」口:MCP 协议入门与实战》《不用 SDK,60 行 Node.js 看穿协议本质》

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 前言
  • 一、先纠正一个普遍误解:模型从不"执行"工具
  • 二、工具定义是怎么被模型"看见"的:不是查询,是全量塞入
  • 三、决策机制本体:模型怎么"选"
    • 3.1 生成层面:调不调工具,也是一个 token 决定
    • 3.2 开关层:tool_choice 参数
    • 3.3 并行调用与多轮循环
  • 四、一个几乎人人都踩的坑:arguments 是字符串
  • 五、工程调优清单:让模型选得更准
  • 六、串回 MCP:完整链路
  • 小结
  • 参考
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档