从工具调用到 MCP、Skill完整学习记录
文章目录工具调用、MCP 与 Skill从“模型能干什么”到“模型怎么干”一、工具调用模型“开口要函数”二、MCP让工具连接标准化三、Skill把多个工具和流程打包成一个“技能”四、三者核心区别工具调用、MCP 与 Skill从“模型能干什么”到“模型怎么干”最近在啃 MCP、Skill 以及工具调用这几个概念发现它们很容易混淆而且在不同语境下含义还不一样。这篇文章先梳理清楚它们分别是什么以及之间的区别。为了方便理解所有例子都用客服场景来写订单查询、退换货流程比天气和旅行更贴近真实业务。一、工具调用模型“开口要函数”工具调用Tool Calling是 LLM 的一种能力不是框架也不是协议。模型本身只会生成文本但我们可以让它输出一种特殊格式的指令告诉宿主程序“我想调用这个函数参数是这些”。程序收到后真正去执行然后把结果塞回给模型让它继续生成回答。我第一次跑通的工具调用代码大概长这样importopenaiimportjson clientopenai.OpenAI(api_keyyour-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 现在到哪里了还有总金额是多少}]# 第一次请求让模型决定是否调用工具responseclient.chat.completions.create(modelgpt-4o,messagesmessages,toolstools,tool_choiceauto)# 检查模型是否请求工具调用messageresponse.choices[0].messageifmessage.tool_calls:tool_callmessage.tool_calls[0]function_nametool_call.function.name argumentsjson.loads(tool_call.function.arguments)print(f模型请求调用{function_name}参数{arguments})# 模拟一个订单查询函数defget_order_details(order_id:str)-str:# 实际项目中这里会连接订单数据库return(f订单号{order_id}\nf状态已发货\nf总金额299.00元\nf物流快递单号 SF123456预计明天送达)# 执行函数拿到结果resultget_order_details(**arguments)# 把工具调用结果附加到对话中messages.append({role:tool,tool_call_id:tool_call.id,content:result})# 第二次请求让模型基于工具结果生成最终回复final_responseclient.chat.completions.create(modelgpt-4o,messagesmessages)print(final_response.choices[0].message.content)流程非常清晰模型输出调用意图 → 宿主程序执行 → 返回结果 → 模型总结回复。模型自己一行代码都不跑只负责“指挥”。二、MCP让工具连接标准化工具调用很好但每接入一个新服务都得写一套对接逻辑而且不同 LLM 的工具定义格式都不一样。MCPModel Context Protocol正是为了解决这个问题而诞生的。MCP 由 Anthropic 开源可以理解为“工具调用的 USB 接口”。它定义了一套标准协议让任何兼容 MCP 的客户端都能以统一的方式发现、调用任何 MCP 服务器上的工具。架构里有两个角色MCP Server真正提供工具或数据的一方暴露标准化接口。MCP ClientAI 宿主程序通过协议与 Server 通信。我用 Python 实现了一个客服场景的 MCP Server包含订单查询和创建工单两个工具# customer_service_server.pyfrommcp.serverimportServer,NotificationOptionsfrommcp.server.modelsimportInitializationCapabilitiesimportmcp.server.stdioimportmcp.typesastypes serverServer(customer-service-server)server.list_tools()asyncdefhandle_list_tools()-list[types.Tool]:return[types.Tool(nameget_order_details,description查询订单详细信息包括状态、金额和物流,inputSchema{type:object,properties:{order_id:{type:string,description:订单编号}},required:[order_id]}),types.Tool(namecreate_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()asyncdefhandle_call_tool(name:str,arguments:dict)-list[types.TextContent]:ifnameget_order_details:order_idarguments[order_id]result(f订单号{order_id}\nf状态已签收\nf金额158.00元\nf物流已送达)return[types.TextContent(typetext,textresult)]elifnamecreate_support_ticket:ticket_idfTK-{arguments[customer_name][:2]}-2024001resultf工单已创建编号{ticket_id}类型{arguments[issue_type]}return[types.TextContent(typetext,textresult)]raiseValueError(f未知工具:{name})asyncdefmain():asyncwithmcp.server.stdio.stdio_server()as(read_stream,write_stream):awaitserver.run(read_stream,write_stream,InitializationCapabilities(sampling{},experimental{},),)if__name____main__:importasyncio asyncio.run(main())客户端调用也很直接# mcp_client_demo.pyimportasynciofrommcpimportClientSession,StdioServerParametersfrommcp.client.stdioimportstdio_clientasyncdefmain():server_paramsStdioServerParameters(commandpython,args[customer_service_server.py])asyncwithstdio_client(server_params)as(read,write):asyncwithClientSession(read,write)assession:awaitsession.initialize()toolsawaitsession.list_tools()print(可用工具,[tool.namefortoolintools.tools])resultawaitsession.call_tool(get_order_details,{order_id:ORD-2024001})print(查询结果,result.content[0].text)resultawaitsession.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 来完成用户的退换货、咨询等指令。整个架构会清晰很多。