惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
博客园 - 【当耐特】
IT之家
IT之家
M
MIT News - Artificial intelligence
酷 壳 – CoolShell
酷 壳 – CoolShell
Martin Fowler
Martin Fowler
V
Visual Studio Blog
F
Fortinet All Blogs
The Cloudflare Blog
Last Week in AI
Last Week in AI
博客园 - 司徒正美
G
Google Developers Blog
Vercel News
Vercel News
爱范儿
爱范儿
小众软件
小众软件
WordPress大学
WordPress大学
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
MongoDB | Blog
MongoDB | Blog
A
About on SuperTechFans
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Check Point Blog
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

博客园 - 人艰不拆_zmc

AG-UI 是什么?一篇文章讲清楚 AI Agent 与前端如何交互 AI 里的“本体”到底是什么?一篇写给技术小白的通俗解释 15000mAh 到底是什么概念?一篇看懂电池容量 go2_ros2_sdk 到底是干什么的?从 Go2、官方 SDK、ROS2 一路讲到 SLAM 和 Nav2 买了宇树 Go2 以后怎么二次开发?写给第一次做机器狗项目的人 买了一块 NVIDIA Jetson,怎么在上面安装 ROS2?从 JetPack 到 ROS2 的完整入门指南 NVIDIA Jetson 到底是什么?写给第一次接触机器人和边缘 AI 的人 ROS / ROS2 到底是什么?写给第一次接触机器人开发的人 大模型到底能同时多少人用?一篇看懂并发、排队与容量估算 大模型为什么有快有慢?一篇看懂响应速度背后的关键因素 技术小白也能看懂:大模型里的量化、蒸馏到底是什么意思? Codex 使用技巧:从“会聊天”到“真正能干活” 我终于搞懂了:Codex 对话中插件和 Skill 到底怎么用 我终于搞懂了 Agent Spec:它其实就是 Agent 的“标准设计图” 我终于搞懂了 Tool:原来不只是 Function Calling 里的函数 Skill 里的 Python 脚本,到底是不是 Tool? 我终于搞懂了 Codex 的 Plugin 和 Skill:顺便把 App、MCP 一次理清 我终于搞懂了 Codex 的“应用”:App 到底是什么,怎么添加和维护? 我终于搞懂了 MCP:从 HTTP API 到 ERP MCP Server 的完整入门 我终于搞懂了 Codex 的“记忆”是怎么回事 Codex 用久了越来越慢?我的上下文管理小技巧 我终于搞懂了 Codex 里的 Thread、Turn 和 Session 从 Qwen3.8-27B 到 FP8、NVFP4、MoE:一次搞懂几个常见大模型概念 FPS 是什么意思?简单理解 60 FPS、25 FPS 和视频帧率 DeepSeek Harness 明明像 AI Coding 工具,为什么又能用来构建各种 Agent? 使用 Codex 开发项目,怎么才能节约 Token? 模型里的 32K、128K、256K 是什么意思?简单聊聊上下文限制 Codex 一次对话到底会给模型发送什么?以 Spring Boot 项目为例讲清 Context、代码读取与 Token 消耗 AI Agent 中的 Rule 是什么?以 Codex 为例,小白也能看懂 我终于搞懂了 Harness:它不是论文,也不是标准,更不是 Codex 独有
我终于搞懂了 Tool、Function Calling 和 MCP:大模型到底怎...
人艰不拆_zmc · 2026-09-04 · via 博客园 - 人艰不拆_zmc

最近学习 Agent、Tool Calling、Function Calling 和 MCP 时,我一直有一个疑问:

用户只是问了一句话,大模型到底怎么知道系统里有哪些 Tool?
又是怎么知道哪个 Tool 是查订单、哪个 Tool 是查库存的?

真正搞懂以后,核心其实只有一句话:

不是把所有接口说明手工写进 Prompt,而是在每次调用模型 API 时,把当前可用的 Tool 定义通过专门的 tools 参数一起传给模型。

下面用一个“业务系统问答助手查询订单”的例子,从头讲清楚。


1. 先看最终效果

业务系统原来有一个订单接口:

GET /api/orders/123

用户在问答助手里说:

帮我查一下订单 123 现在什么状态

我们希望最终变成:

用户问题
↓
大模型判断应该调用 query_order
↓
业务系统真正执行 query_order("123")
↓
query_order 调订单接口
↓
返回“已发货”
↓
结果重新交给大模型
↓
大模型回答:订单 123 已发货

这里最关键的就是:

大模型为什么知道有一个叫 query_order 的 Tool?


对于业务系统来说,Tool 可以先理解成:

一个普通后端函数 + 一份给大模型看的函数说明。

例如我们先写一个普通 Python 函数:

def query_order(order_no: str):
    # 实际项目中,这里可以调用订单 Service 或 HTTP API
    return {
        "order_no": order_no,
        "status": "已发货"
    }

这只是一个普通函数。

此时大模型还完全不知道:

query_order 是什么
它有什么参数
什么时候应该调用它

所以还要再给模型一份 Tool 定义。


例如:

tools = [
    {
        "type": "function",
        "name": "query_order",
        "description": "根据订单号查询订单当前状态。",
        "parameters": {
            "type": "object",
            "properties": {
                "order_no": {
                    "type": "string",
                    "description": "订单号,例如 123"
                }
            },
            "required": ["order_no"]
        }
    }
]

可以把它翻译成人话:

name
=
这个 Tool 叫 query_order


description
=
这个 Tool 是用来查询订单状态的


parameters
=
调用这个 Tool 时,需要传哪些参数


order_no
=
订单号,字符串类型

所以真正让模型理解 Tool 的,不是 Python 函数代码本身,而是:

name
description
parameters / Schema

这些结构化信息。


不是你每次手工写:

你现在有三个工具:
query_order 是查订单的……
query_inventory 是查库存的……

而是你的程序调用模型 API 时,同时传:

用户问题
+
tools

例如使用 OpenAI Responses API,可以写成:

from openai import OpenAI

client = OpenAI()

tools = [
    {
        "type": "function",
        "name": "query_order",
        "description": "根据订单号查询订单当前状态。",
        "parameters": {
            "type": "object",
            "properties": {
                "order_no": {
                    "type": "string",
                    "description": "订单号,例如 123"
                }
            },
            "required": ["order_no"]
        }
    }
]

response = client.responses.create(
    model="gpt-6-astra",
    input="帮我查一下订单 123 现在什么状态",
    tools=tools,
    tool_choice="auto"
)

注意这一段:

tools=tools

这就是关键。

程序实际向模型服务发送的内容,可以简单理解成:

input:
帮我查一下订单 123 现在什么状态


tools:
- name: query_order
  description: 根据订单号查询订单当前状态
  parameters:
    order_no: string

所以:

模型确实拿到了 Tool 的说明,只是它不是普通 Prompt 文字,而是模型 API 请求里的结构化 tools 字段。


从概念上说:

模型这一轮要使用哪些 Tool,这一轮调用时就必须让模型知道这些 Tool。

但不是开发人员每次手工复制粘贴。

实际程序通常这样写:

TOOLS = [
    # 所有 Tool 定义
]

程序启动时就已经准备好了。

以后每次用户提问:

response = client.responses.create(
    model="gpt-6-astra",
    input=user_question,
    tools=TOOLS
)

也就是说:

开发一次 Tool 定义
↓
程序保存 Tool 定义
↓
用户每次提问
↓
程序自动把当前允许的 Tool 一起传给模型

用户完全感知不到这一过程。


假设模型看到:

用户:
帮我查一下订单 123 现在什么状态


可用 Tool:

query_order
作用:根据订单号查询订单状态
参数:order_no

模型会判断:

这个问题需要真实订单数据
↓
我不能自己编
↓
有 query_order 可以用
↓
应该调用 query_order
↓
order_no = 123

于是模型可能返回一个 Function Call:

{
  "type": "function_call",
  "name": "query_order",
  "arguments": "{\"order_no\":\"123\"}"
}

注意:

这时候大模型并没有真的执行 query_order()

它只是告诉你的后端:

“我决定调用 query_order,参数是 123。”


你的程序拿到模型返回的 Function Call 后:

import json

for item in response.output:
    if item.type == "function_call":
        arguments = json.loads(item.arguments)

        if item.name == "query_order":
            result = query_order(
                order_no=arguments["order_no"]
            )

真正执行的是这一句:

result = query_order(...)

也就是说:

大模型
=
决定“调用谁、传什么参数”


业务系统后端
=
真正执行函数

大模型本身不能直接进入你的数据库,也不会自动执行你的 Python 函数。


实际项目里,query_order() 可能是:

import httpx

def query_order(order_no: str):
    response = httpx.get(
        f"http://business-system/api/orders/{order_no}",
        timeout=10
    )

    response.raise_for_status()

    return response.json()

完整关系就是:

用户
↓
问答助手
↓
大模型
↓
Function Call:query_order("123")
↓
问答助手后端
↓
query_order()
↓
GET /api/orders/123
↓
业务系统

9. 接口结果怎么再告诉模型

假设 Tool 执行结果是:

{
  "order_no": "123",
  "status": "已发货"
}

还需要把这个结果再次交给模型。

例如:

tool_output = json.dumps(
    result,
    ensure_ascii=False
)

final_response = client.responses.create(
    model="gpt-6-astra",
    previous_response_id=response.id,
    tools=tools,
    input=[
        {
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": tool_output
        }
    ]
)

print(final_response.output_text)

模型这次看到的意思相当于:

你刚才调用的:

query_order("123")

执行结果:

{
  "order_no": "123",
  "status": "已发货"
}

于是模型最后回答:

订单 123 当前已经发货。

10. 把完整过程串起来

整个 Function Calling / Tool Calling 流程其实就是:

第一步
用户:
“帮我查订单 123”
↓


第二步
问答助手后端调用模型:

用户问题
+
Tool 定义
↓


第三步
模型判断:

应该调用 query_order
参数 order_no=123
↓


第四步
模型返回 Function Call:

query_order("123")
↓


第五步
业务系统后端真正执行:

query_order("123")
↓


第六步
Tool 调业务接口:

GET /api/orders/123
↓


第七步
接口返回:

已发货
↓


第八步
把 Tool Result 再交给模型
↓


第九步
模型生成最终回答:

“订单 123 当前已经发货。”

这就是最基本的 Agent Tool Calling。


假设系统里定义了:

query_order
= 查询订单


query_inventory
= 查询库存


create_ticket
= 创建工单

调用模型时:

tools = [
    query_order 的定义,
    query_inventory 的定义,
    create_ticket 的定义
]

用户问:

ABC001 还有多少库存?

模型会根据:

Tool 名称
Tool description
参数说明
用户当前问题

进行匹配。

所以它选择:

query_inventory

而不是:

query_order

因此 Tool 的 description 写得是否准确,非常重要。


小系统只有十几个 Tool 时,可以直接全部传。

但是如果以后有:

500 个 Tool
1000 个 Tool

就不适合每次全部给模型。

通常会增加一层 Tool Router,例如:

用户:
“查一下订单”
↓
先判断属于:
订单领域
↓
只取:
query_order
query_order_logistics
cancel_order
↓
把这几个 Tool 给模型

这样可以减少:

Context 占用
Tool 选择干扰
错误调用概率

小白阶段可以先理解为:

Function Calling 是 Tool Calling 最常见的一种实现方式。

例如:

Tool:
query_order

实际上就是:

让模型输出:

函数名
+
函数参数

所以经常会看到:

Function Calling
Tool Calling

两个词一起出现。

现在更常用 Tool Calling 这个更大的概念,因为 Tool 不一定只有普通 Function,还可能包括:

Web Search
File Search
MCP
Shell
Computer
……

普通 Tool Calling:

你的问答助手
↓
你自己在代码中维护 tools
↓
模型

例如:

tools = [
    {
        "name": "query_order",
        ...
    }
]

Tool 定义是你的问答助手自己写、自己维护的。


MCP 则是:

问答助手 / Codex
↓
MCP Client
↓
MCP Server

MCP Client 连接 MCP Server 后,会从 Server 获取:

有哪些 Tool
Tool 名称
Tool description
inputSchema

例如 MCP Server 返回:

query_order
= 查询订单

query_inventory
= 查询库存

于是 Harness / MCP Client 再把这些 Tool 提供给模型。

所以最终模型看到的东西其实还是:

Tool 名称
Tool 说明
Tool 参数

区别只是:

普通 Tool Calling
=
Tool 定义由你的应用自己维护


MCP
=
Tool 定义由 MCP Server 按统一标准提供

普通方式可能写:

tools = [
    query_order_schema,
    query_inventory_schema,
    create_ticket_schema
]

如果用了 MCP:

MCP Client
↓
连接 MCP Server
↓
获取 Tool 列表
↓
得到 name / description / inputSchema
↓
自动转成模型可以使用的 Tool 定义
↓
交给模型

所以:

MCP 帮你解决了 Tool 的标准化发现和接入问题。

不是说用了 MCP 后模型不需要 Tool 描述。

而是:

Tool 描述由 MCP Server 自动提供,而不是业务问答助手自己硬编码维护。


16. 什么时候不用 MCP

如果你的场景是:

一个业务系统
+
系统内部自己的问答助手
+
只有这个问答助手会调用这些接口

例如:

业务系统
├── 订单模块
├── 库存模块
└── 问答助手

最简单的方案就是:

问答助手
↓
普通 Tool Calling
↓
Tool 函数
↓
内部 Service / HTTP API

完全没有必要为了 MCP 而 MCP。

这样:

开发简单
链路短
调试方便

17. 什么时候应该用 MCP

如果以后希望同一套能力给:

业务问答助手
Codex
OpenClaw
DSH
其他 Agent

一起使用,就比较适合 MCP。

例如:

                ERP MCP Server
               /      |       \
              /       |        \
             ↓        ↓         ↓
          Codex    OpenClaw   内部助手

ERP MCP Server 统一提供:

query_order
query_inventory
create_purchase_request

所有支持 MCP 的 Agent 都按统一方式接入。

所以 MCP 更适合:

多 Agent 复用
跨系统接入
企业统一能力平台
Tool 数量持续增长
希望降低重复适配开发

这一点特别重要。

不是:

用了 MCP
就不用 Tool

而是:

MCP Server
里面提供的核心能力
本身仍然是 Tool

可以理解成:

Tool
=
具体能力


MCP
=
把这些 Tool 标准化提供出去的协议

例如:

query_order

始终是 Tool。

只给自己系统使用:

内部问答助手
↓
直接 Tool Calling
↓
query_order

提供给多个 Agent:

Codex
↓
MCP
↓
ERP MCP Server
↓
query_order

19. 最终把几个词放在一起

HTTP API
=
业务系统原来的接口


Tool 函数
=
真正执行某项业务能力的后端函数


Tool Schema
=
告诉模型 Tool 名称、用途和参数


Function Calling / Tool Calling
=
模型根据 Tool Schema 决定调用哪个 Tool


MCP
=
标准化发现和调用这些 Tool 的协议


MCP Server
=
按 MCP 标准提供 Tool 的服务

20. 最后记住这张图

不使用 MCP

用户
↓
业务系统问答助手
↓
把用户问题 + tools 参数交给模型
↓
模型选择 query_order
↓
问答助手后端执行 query_order
↓
业务 API
↓
返回结果
↓
再次交给模型
↓
最终回答

使用 MCP

MCP Server
↓
提供 Tool Schema
↓
MCP Client 自动获取 Tool
↓
Harness 把 Tool 提供给模型
↓
模型选择 Tool
↓
MCP Client 调 MCP Server
↓
执行业务能力
↓
返回结果
↓
模型最终回答

最后总结

最需要搞懂的其实就是这一点:

大模型并不会天然知道你的业务系统有哪些接口。

普通 Tool Calling 的做法是:

开发人员定义 Tool Schema
↓
程序调用模型时通过 tools 参数一起传进去
↓
模型根据 name / description / parameters 选择 Tool

MCP 的做法是:

MCP Server 提供 Tool Schema
↓
MCP Client 自动发现
↓
再提供给模型

所以:

只给一个业务系统自己的问答助手使用时,直接 Tool Calling 往往最简单;当同一批 Tool 需要被 Codex、OpenClaw、DSH 等多个 Agent 统一复用时,再使用 MCP。