工具调用、MCP 与 Skill:从“模型能干什么”到“模型怎么干”

最近在啃 MCP、Skill 以及工具调用这几个概念,发现它们很容易混淆,而且在不同语境下含义还不一样。这篇文章先梳理清楚它们分别是什么,以及之间的区别。为了方便理解,所有例子都用客服场景来写(订单查询、退换货流程),比天气和旅行更贴近真实业务。
在这里插入图片描述

一、工具调用:模型“开口要函数”

工具调用(Tool Calling) 是 LLM 的一种能力,不是框架也不是协议。模型本身只会生成文本,但我们可以让它输出一种特殊格式的指令,告诉宿主程序:“我想调用这个函数,参数是这些”。程序收到后真正去执行,然后把结果塞回给模型,让它继续生成回答。

我第一次跑通的工具调用代码大概长这样:

import openai
import json

client = openai.OpenAI(api_key="your-api-key")

# 定义一个工具:查询订单详情
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_order_details",
            "description": "根据订单号查询订单状态、金额和物流信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "订单编号,例如 ORD-2024001"
                    }
                },
                "required": ["order_id"]
            }
        }
    }
]

# 用户问题(模拟客服场景)
messages = [
    {"role": "user", "content": "帮我查一下订单 ORD-2024001 现在到哪里了,还有总金额是多少"}
]

# 第一次请求,让模型决定是否调用工具
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

# 检查模型是否请求工具调用
message = response.choices[0].message
if message.tool_calls:
    tool_call = message.tool_calls[0]
    function_name = tool_call.function.name
    arguments = json.loads(tool_call.function.arguments)

    print(f"模型请求调用 {function_name},参数:{arguments}")

    # 模拟一个订单查询函数
    def get_order_details(order_id: str) -> str:
        # 实际项目中这里会连接订单数据库
        return (
            f"订单号:{order_id}\n"
            f"状态:已发货\n"
            f"总金额:299.00元\n"
            f"物流:快递单号 SF123456,预计明天送达"
        )

    # 执行函数,拿到结果
    result = get_order_details(**arguments)

    # 把工具调用结果附加到对话中
    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": result
    })

    # 第二次请求,让模型基于工具结果生成最终回复
    final_response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages
    )
    print(final_response.choices[0].message.content)

流程非常清晰:模型输出调用意图 → 宿主程序执行 → 返回结果 → 模型总结回复。模型自己一行代码都不跑,只负责“指挥”。

二、MCP:让工具连接标准化

工具调用很好,但每接入一个新服务都得写一套对接逻辑,而且不同 LLM 的工具定义格式都不一样。MCP(Model Context Protocol) 正是为了解决这个问题而诞生的。

MCP 由 Anthropic 开源,可以理解为“工具调用的 USB 接口”。它定义了一套标准协议,让任何兼容 MCP 的客户端都能以统一的方式发现、调用任何 MCP 服务器上的工具。

架构里有两个角色:

MCP Server:真正提供工具或数据的一方,暴露标准化接口。

MCP Client:AI 宿主程序,通过协议与 Server 通信。`

我用 Python 实现了一个客服场景的 MCP Server,包含订单查询和创建工单两个工具:

# customer_service_server.py
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationCapabilities
import mcp.server.stdio
import mcp.types as types

server = Server("customer-service-server")

@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="get_order_details",
            description="查询订单详细信息,包括状态、金额和物流",
            inputSchema={
                "type": "object",
                "properties": {
                    "order_id": {"type": "string", "description": "订单编号"}
                },
                "required": ["order_id"]
            }
        ),
        types.Tool(
            name="create_support_ticket",
            description="为客户创建一条新的售后工单",
            inputSchema={
                "type": "object",
                "properties": {
                    "customer_name": {"type": "string", "description": "客户姓名"},
                    "issue_type": {
                        "type": "string",
                        "description": "问题类型:退货/换货/投诉/咨询"
                    },
                    "description": {"type": "string", "description": "问题描述"}
                },
                "required": ["customer_name", "issue_type", "description"]
            }
        )
    ]

@server.call_tool()
async def handle_call_tool(
    name: str, arguments: dict
) -> list[types.TextContent]:
    if name == "get_order_details":
        order_id = arguments["order_id"]
        result = (
            f"订单号:{order_id}\n"
            f"状态:已签收\n"
            f"金额:158.00元\n"
            f"物流:已送达"
        )
        return [types.TextContent(type="text", text=result)]
    elif name == "create_support_ticket":
        ticket_id = f"TK-{arguments['customer_name'][:2]}-2024001"
        result = f"工单已创建,编号:{ticket_id},类型:{arguments['issue_type']}"
        return [types.TextContent(type="text", text=result)]
    raise ValueError(f"未知工具: {name}")

async def main():
    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationCapabilities(
                sampling={},
                experimental={},
            ),
        )

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

客户端调用也很直接:

# mcp_client_demo.py
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    server_params = StdioServerParameters(
        command="python",
        args=["customer_service_server.py"]
    )

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            tools = await session.list_tools()
            print("可用工具:", [tool.name for tool in tools.tools])

            result = await session.call_tool("get_order_details", {"order_id": "ORD-2024001"})
            print("查询结果:", result.content[0].text)

            result = await session.call_tool("create_support_ticket", {
                "customer_name": "张明",
                "issue_type": "退货",
                "description": "收到的商品有破损,需要退货退款"
            })
            print("工单结果:", result.content[0].text)

asyncio.run(main())

到这里,我对 MCP 的理解就一句话:MCP 是一套标准协议,让工具可以被统一发现、描述和调用,把工具调用能力做了一层工程化封装,实现即插即用。

三、Skill:把多个工具和流程打包成一个“技能”

Skill 这个词在不同语境下含义差别很大,但核心思想一致:对一个完整能力的封装,内部可能组合了多次工具调用、流程控制甚至特定的 prompt 逻辑。
在 Claude 生态中,Skill 常常以 Markdown 文件 的形式存在。文件包含 YAML frontmatter 声明名字、描述和所需工具,正文则是写给模型的系统指令,告诉它按什么步骤完成任务。名称和描述一定要定义清楚,不然调用会出错。

---
name: customer-refund-handler
description: 处理客户的退货退款请求,自动查询订单、创建工单、发起退款并回复客户
tools:
  - get_order_details
  - create_support_ticket
  - initiate_refund
---

你是电商客服的自动处理助理。当用户提出退货退款请求时,请按以下步骤操作:

1. 向用户确认订单号,然后使用 get_order_details 工具查询订单状态。
2. 如果订单符合退货条件(已签收且在退货期内),使用 create_support_ticket 创建一条退货工单。
3. 使用 initiate_refund 工具发起退款,参数使用订单号及订单金额。
4. 把工单编号和退款预计到账时间整合成一段安抚性话术,回复给客户。
5. 如果订单不符合退货条件,请礼貌地解释原因,并提供进一步咨询的渠道。
这种 Skill 本质上是一段“指导模型如何干活”的 prompt,依赖于底层工具(可能通过 MCP 提供),模型无关且易于分发。

四、三者核心区别

在这里插入图片描述

一句话区别:

工具调用:模型说“我要调用 xxx 函数”。

MCP:为这种调用铺了一条标准化的路,让任何模型和工具都能用同一套语言沟通。

Skill:把多个工具调用和流程打包成一个完整的“技能”,更贴近真实业务需求。

这三者不是互斥的,而是层层递进的关系。在实际搭建客服系统时,我会先用 MCP 封装订单查询、工单创建、退款等外部能力,再在上层构建 Skill,最后让 LLM 通过工具调用或直接触发 Skill 来完成用户的退换货、咨询等指令。整个架构会清晰很多。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐