从工具调用到 MCP、Skill完整学习记录
在实际搭建客服系统时,我会先用 MCP 封装订单查询、工单创建、退款等外部能力,再在上层构建 Skill,最后让 LLM 通过工具调用或直接触发 Skill 来完成用户的退换货、咨询等指令。整个架构会清晰很多。模型本身只会生成文本,但我们可以让它输出一种特殊格式的指令,告诉宿主程序:“我想调用这个函数,参数是这些”。到这里,我对 MCP 的理解就一句话:MCP 是一套标准协议,让工具可以被统一发现
文章目录
工具调用、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 来完成用户的退换货、咨询等指令。整个架构会清晰很多。
更多推荐




所有评论(0)