ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent 工具调用、MCP 与 Skill 三层架构实战指南

Agent 工具调用、MCP 与 Skill 三层架构实战指南 1. 从一次翻车现场说起Agent 的能力到底从哪来去年冬天我接了个私活帮一个做跨境电商的朋友搭一套自动处理客服工单的 Agent。需求听起来不复杂读工单、查订单、判断退换货政策、生成回复、必要时转人工。我信心满满地选了个当时口碑不错的框架把提示词写得漂漂亮亮结果上线第一天就翻车了——Agent 在查订单这一步卡了整整四十分钟因为它根本不知道要去调哪个接口只能一遍遍地在对话里假装自己查过了然后编出一个订单号。那次事故让我彻底想明白一件事Agent 的能力不是从模型里长出来的是从工具调用这条管道里流出来的。模型再聪明它也只是个被困在文本框里的脑子没有手、没有眼睛、没有记忆。而工具调用Function Calling、MCPModel Context Protocol、Skill 这三样东西本质上就是在给这个脑子装手、装眼睛、装肌肉记忆。这篇东西我想聊的就是这三者的分工与配合。市面上讲 Agent 的文章大多停留在Agent LLM 记忆 工具这种公式层面但真到落地的时候你会发现工具怎么描述、MCP 怎么接、Skill 怎么封装每一个细节都能决定你的 Agent 是能干活的员工还是个只会说漂亮话的实习生。我会把这三层拆开讲清楚再讲它们怎么拼在一起最后给一套可以直接抄的实操方案。不管你是刚接触 Agent 开发的新手还是已经踩过几个坑的老手应该都能从里面捞到点东西。2. 三层能力模型工具调用、MCP、Skill 各管什么2.1 工具调用是手解决模型与外部世界的连接问题先说最底层的工具调用。它的核心逻辑特别朴素模型在生成回复的时候如果判断需要外部信息或执行外部动作就不再输出自然语言而是输出一段结构化的调用请求比如{name: get_order, arguments: {order_id: 12345}}。宿主程序拿到这段请求去真正执行对应的函数再把结果塞回对话历史模型基于结果继续往下走。这里有个很多人一开始会误解的点模型本身并不执行任何东西。它只是提议要调用什么真正执行的是你的代码。所以工具调用的可靠性一半取决于模型的判断能力另一半取决于你写的工具描述和参数 schema 够不够清晰。我见过太多人把工具描述写成查询订单信息然后抱怨模型老是传错参数——你换成根据订单号查询订单的物流状态、金额和退换货截止日期订单号格式为纯数字字符串命中率立刻不一样。工具调用的价值在于它把模型的语言能力和程序的确定性接了起来。模型负责理解意图、决定调什么程序负责精确执行。这个分工是整个 Agent 体系的地基后面讲的 MCP 和 Skill 都是在这个地基上盖的楼。2.2 MCP 是标准插座解决工具复用与跨应用的问题工具调用有个天然的痛点每换一个宿主应用工具就得重写一遍。你在 A 项目里写了个查数据库的工具换到 B 项目又得复制粘贴改一遍参数格式、鉴权方式、错误处理全都要重新对齐。MCP 就是冲着这个问题来的。MCP 全称 Model Context Protocol你可以把它理解成AI 应用和外部能力之间的 USB-C 接口。它定义了一套标准的通信协议工具提供方MCP Server按照协议暴露自己的能力工具使用方MCP Client通常是你的 Agent 宿主按照协议去发现和调用这些能力。中间走的是 JSON-RPC传输层可以是标准输入输出也可以是 HTTP。它带来的最大变化是解耦。以前工具和 Agent 是绑死的现在工具可以独立成一个 MCP Server任何支持 MCP 的客户端都能接。你在本地跑一个文件系统的 MCP Server那么所有支持 MCP 的编辑器、IDE、Agent 框架都能用它读写文件不用各写一遍。这也是为什么最近一年 MCP 生态爆发得这么快——数据库、浏览器、设计工具、逆向工具、GIS 分析几乎每个领域都有人在做自己的 MCP Server。MCP 还定义了三种能力原语Tools可调用的函数、Resources可读取的数据比如文件内容、数据库记录、Prompts预置的提示词模板。很多人只用了 Tools其实 Resources 在处理把某个文件内容喂给模型这类场景时更自然Prompts 则适合把常用工作流固化下来。2.3 Skill 是肌肉记忆解决复杂任务的封装与复用问题如果说工具调用是手、MCP 是插座那 Skill 更像是肌肉记忆——它封装的不是单个动作而是一整套完成某类任务的流程、知识和判断标准。Skill 这个概念在不同生态里叫法不太一样有的叫 Agent Skill有的叫 Skill 插件但内核是一致的把一段可复用的专业能力打包成一个模块让 Agent 在需要的时候加载并执行。一个 Skill 通常包含几部分触发条件什么时候用这个 Skill、执行步骤怎么做、依赖的工具需要调哪些函数或 MCP、输出规范结果长什么样、以及边界说明什么情况下不该用。举个具体的例子。假设你要做一个论文精读的 Skill它可能包含先用文件工具读取 PDF再用检索工具定位关键章节然后按研究问题—方法—结论—局限的固定结构输出摘要最后把引用格式统一成某一种。这一整套流程如果每次都靠提示词临时拼模型很容易漏步骤封装成 Skill 之后它就变成了一份可版本管理、可测试、可复用的作业指导书。Skill 和工具调用的区别在于粒度工具是原子操作Skill 是编排好的操作序列。Skill 和 MCP 的区别在于关注点MCP 关心能力怎么接进来Skill 关心任务怎么做完。三者不是替代关系是叠加关系。层级解决的问题典型形态复用范围工具调用模型与外部世界的连接函数 schema 执行代码单个应用内MCP工具的标准化与跨应用复用MCP Server / Client跨应用、跨框架Skill复杂任务的封装与复用提示词 流程 工具组合跨任务、跨项目3. 工具调用实操从 schema 设计到错误处理3.1 工具描述怎么写才能让模型不犯迷糊工具调用的成败八成在描述上。我总结了一套写工具描述的检查清单实测下来能显著降低误调用率。第一名字要动词开头且唯一。get_order_status比order好search_documents比docs好。模型是靠名字做第一轮筛选的名字含糊它就会乱猜。第二描述要写清楚做什么、什么时候用、什么时候不用。很多人只写做什么结果模型在不该用的时候也调。比如一个send_email工具描述里必须写仅在用户明确要求发送邮件时调用不要用于生成邮件草稿。第三参数 schema 要严格。能用 enum 就别用 string能加格式说明就别省。比如日期参数写成format: YYYY-MM-DD模型传错的概率会低很多。必填参数和可选参数要分清可选参数要给默认值说明。第四返回值结构要稳定。模型是根据返回值继续推理的如果这次返回{status: ok, data: {...}}下次返回{result: [...]}模型就会懵。统一成一种结构错误也走同一个通道。{ name: get_order_status, description: 根据订单号查询订单的当前状态、物流信息和退换货截止日期。仅在用户提供了明确订单号时调用。, parameters: { type: object, properties: { order_id: { type: string, description: 纯数字订单号长度 12-18 位 }, include_logistics: { type: boolean, description: 是否包含物流轨迹默认 false, default: false } }, required: [order_id] } }3.2 并行调用与串行依赖怎么编排才不浪费 token工具调用有个容易被忽略的性能点能并行的别串行。如果 Agent 需要同时查订单、查库存、查用户等级这三个调用之间没有依赖就应该一次性发出去而不是等第一个回来再发第二个。主流框架现在都支持并行工具调用你只需要在提示词里明确以下信息可以同时获取。但并行不是无脑并行。有依赖关系的调用必须串行比如先查订单拿到商品 ID再根据商品 ID 查库存。这种依赖链如果让模型自己判断它有时候会搞错顺序。我的做法是在 Skill 层面把依赖关系写死或者用工具描述里的前置条件说明来约束。还有一个 token 层面的坑工具返回结果别一股脑全塞回上下文。一个查询接口返回 5000 行 JSON全塞进去既贵又容易让模型抓不住重点。正确做法是在工具执行层做裁剪只返回模型真正需要的字段或者返回摘要加一个需要详情请再调一次的句柄。3.3 错误处理让 Agent 优雅地失败而不是编造工具调用最危险的情况不是报错是报错之后模型开始编。接口超时了模型不说超时反而编一个查询成功订单状态为已发货。这种幻觉在客服、金融、医疗场景里是灾难性的。我的处理原则是三条。第一所有工具错误都必须以结构化形式返回比如{error: true, code: TIMEOUT, message: 订单服务超时请稍后重试}而不是抛异常让框架吞掉。第二在系统提示词里明确禁止编造工具结果写清楚如果工具返回错误必须如实告知用户并给出重试建议不得虚构任何数据。第三对关键工具做结果校验比如订单号查出来的商品 ID 必须和输入对得上对不上就判定为异常。提示给工具加一个调用次数上限很有必要。我遇到过 Agent 陷入调用失败—重试—再失败死循环一小时烧掉几十万 token 的情况。设个上限超了就转人工或降级回复。4. MCP 落地从本地 Server 到跨应用复用4.1 MCP 的通信模型与三种原语MCP 的架构是典型的客户端-服务端模型。Client 通常是你的 Agent 宿主Server 是能力提供方。两者通过 JSON-RPC 2.0 通信传输方式有两种stdio标准输入输出适合本地进程和 HTTP with SSE适合远程服务。本地开发一般用 stdio部署到生产环境用 HTTP 更灵活。三种原语里Tools 用得最多Resources 和 Prompts 经常被忽略但其实很有用。Resources 适合把数据暴露给模型读取的场景比如把一个目录下的文件、一个数据库的表结构、一份配置暴露成 Resource模型可以按 URI 去读。Prompts 适合把常用工作流固化成模板用户一键触发。一个最小可用的 MCP Server 大概长这样以 Python 为例from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app Server(demo-server) app.list_tools() async def list_tools(): return [ Tool( nameread_file, description读取指定路径的文件内容, inputSchema{ type: object, properties: { path: {type: string, description: 文件绝对路径} }, required: [path] } ) ] app.call_tool() async def call_tool(name: str, arguments: dict): if name read_file: with open(arguments[path], r, encodingutf-8) as f: return [TextContent(typetext, textf.read())] async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options())这段代码跑起来任何支持 MCP 的客户端都能通过配置接上它然后就能调用read_file了。注意inputSchema用的是标准 JSON Schema和前面讲的工具调用 schema 是一套东西这也是 MCP 设计得聪明的地方——它复用了已有的约定学习成本低。4.2 接入 MCP 的常见姿势与配置要点不同客户端的 MCP 配置方式略有差异但核心都是告诉客户端去哪里启动或连接这个 Server。以配置文件为例通常是这样的结构{ mcpServers: { filesystem: { command: npx, args: [-y, modelcontextprotocol/server-filesystem, /path/to/allowed/dir] }, database: { command: python, args: [-m, my_db_server], env: { DB_URL: postgresql://localhost:5432/mydb } } } }几个实操要点。第一路径权限要收窄。文件系统类的 Server 一定要限定允许访问的目录别图省事给根目录否则模型一个误操作就能删你整个项目。第二环境变量别硬编码在配置里用 env 字段注入敏感信息走系统环境变量。第三stdio 类型的 Server 启动失败往往没有明显报错排查时先手动在终端跑一遍启动命令看能不能正常起来。4.3 MCP 生态现状哪些领域已经有现成轮子MCP 生态这一年扩张得很快很多领域已经有现成的 Server 可以直接用不用自己从零写。文件系统、Git、数据库Postgres、SQLite、浏览器自动化、搜索、地图这些是通用型的。垂直领域里设计工具、逆向分析工具、GIS 空间分析、办公套件也陆续有了 MCP 实现。我的建议是能用现成的就别自己写。自己写 Server 的成本不只是代码还有后续的维护、协议升级、安全修补。但用现成的也要做评估重点看三件事维护活跃度最近有没有更新、权限模型能不能限制访问范围、错误处理失败时返回什么。一个半年没更新、权限全开的 Server接进生产环境就是个定时炸弹。注意MCP Server 本质上是给模型开了个后门它能访问什么模型就能通过它访问什么。接入前一定要问自己如果模型被恶意提示词操控这个 Server 最坏能造成什么后果5. Skill 设计把专业能力封装成可复用的模块5.1 Skill 的组成要素与触发机制一个设计良好的 Skill应该像一份写给新员工的作业指导书什么情况下用、按什么步骤做、每步用什么工具、做完输出什么、什么情况要停下来问人。我通常把 Skill 拆成五个部分来写。触发条件决定 Skill 什么时候被激活。可以是关键词触发用户提到精读论文可以是意图触发模型判断用户想做的事属于这个 Skill 的范畴也可以是显式调用用户直接点名。触发条件写得太宽会导致 Skill 乱触发写得太窄又用不上需要在测试中反复调。执行步骤是 Skill 的骨架。每一步要写清楚做什么、用什么工具、输入从哪来、输出到哪去、失败怎么办。步骤之间如果有依赖要明确标出顺序。依赖声明列出这个 Skill 需要哪些工具或 MCP Server。这样在加载 Skill 的时候可以检查依赖是否满足缺了就提前报错而不是执行到一半才发现工具不存在。输出规范定义结果的结构。是纯文本、Markdown、JSON 还是特定格式的文件字段有哪些这样下游的流程才能稳定消费。边界说明是最容易被忽略但最重要的部分。写清楚这个 Skill 不处理什么情况比如不处理跨语言的论文翻译遇到非中文非英文的论文直接转人工。5.2 从提示词到 Skill一次论文精读的封装实录我拿论文精读这个 Skill 做个完整示范。最早我是用一段长提示词实现的问题很多步骤会漏、格式不稳定、换个模型就崩。后来封装成 Skill稳定性提升明显。触发条件写成当用户上传 PDF 或提供论文链接并表达精读总结分析意图时激活。执行步骤分五步第一步用文件工具读取 PDF 并提取文本第二步用检索工具定位摘要、方法、实验、结论四个章节第三步按固定结构生成摘要结构为研究问题 / 核心方法 / 主要结论 / 局限性 / 可借鉴点第四步提取所有参考文献并按 GB/T 7714 格式化第五步输出一份 Markdown 文件。依赖声明文件读取工具、文本检索工具、文件写入工具。输出规范Markdown 文件包含五个固定二级标题参考文献单独一节。边界说明不处理扫描版 PDF无文本层遇到时提示用户先做 OCR不处理超过 100 页的论文超过则只精读前 30 页并说明。封装完之后同样的任务在不同模型上的表现差异小了很多因为流程被固定住了模型只需要在每一步做局部判断不需要自己规划全局。5.3 Skill 的版本管理与测试Skill 一旦多了管理就成了问题。我的做法是把 Skill 当代码管每个 Skill 一个目录里面放skill.md定义、examples/示例输入输出、tests/测试用例。用 Git 做版本控制每次改动都记录变更原因。测试方面我建议至少覆盖三类用例正常路径标准输入看输出是否符合规范、边界情况超长输入、空输入、格式异常、对抗情况诱导模型跳过步骤、伪造工具结果。第三类最重要很多 Skill 在正常输入下表现完美一遇到忽略之前的指令这种提示词注入就崩了。提示给 Skill 加一个自检步骤很值。在输出前让模型对照输出规范检查一遍缺字段、格式错、步骤漏都能在这一步拦下来。实测能减少三成左右的返工。6. 三者协同搭一个能干活的 Agent6.1 分层架构谁调用谁谁依赖谁把三层拼起来一个完整的 Agent 架构大概是这样最上层是 Skill 层负责理解任务、规划步骤、编排流程中间是 MCP 层负责把外部能力标准化地接进来最下层是工具调用层负责具体的函数执行和结果返回。数据流是这样的用户输入进来Agent 先判断该激活哪个 SkillSkill 按步骤执行每一步需要外部能力时通过 MCP Client 去调用对应的 MCP ServerMCP Server 内部再调用真正的工具函数把结果按协议返回结果回到 Skill 层Skill 判断是否继续下一步直到任务完成。这个分层的好处是每一层可以独立演进。换模型不影响 MCP 和工具加新工具不影响 Skill 逻辑改 Skill 流程不影响底层实现。我见过不少项目把三层揉在一起结果改一处崩三处维护成本高得吓人。6.2 一个完整案例自动化工单处理 Agent回到开头那个翻车的项目重构之后我是这么搭的。Skill 层定义了一个工单处理Skill步骤是读取工单内容 → 提取订单号 → 查询订单状态 → 匹配退换货政策 → 生成回复草稿 → 判断是否需要转人工。MCP 层接了两个 Server一个是订单系统的 MCP Server暴露get_order_status和get_return_policy两个工具一个是知识库的 MCP Server暴露search_policy_docs工具和policy://current这个 Resource。工具调用层就是这两个 Server 内部的具体实现连数据库、查缓存、调内部 API。关键改动有三个。第一订单号提取从让模型自由发挥改成用正则先提取提取不到再让模型判断准确率从 70% 提到 98%。第二退换货政策查询从模型凭记忆答改成必须调get_return_policy工具杜绝了政策幻觉。第三转人工判断加了硬规则金额超过 500 元、涉及投诉、政策查询失败一律转人工不交给模型判断。重构后上线工单自动处理率从 40% 提到 78%人工介入的工单里也没有再出现编造订单号的情况。6.3 性能与成本token 花在哪怎么省Agent 的 token 消耗主要在三块系统提示词、工具描述、对话历史。系统提示词和工具描述是固定开销每轮都要带对话历史是增长开销越聊越贵。省 token 的几个实操手段。第一工具描述按需加载。不是所有工具每轮都需要可以按 Skill 激活情况动态注入相关工具的描述无关的不带。第二对话历史做摘要压缩。超过一定轮数后把早期对话压缩成摘要只保留关键信息。第三工具返回结果裁剪。前面提过只返回必要字段。第四并行调用减少轮次。能一轮拿到的信息别分三轮。我实测过一个客服 Agent做了这四项优化后单次会话的平均 token 消耗降了约 55%响应速度也快了不少。省下来的钱够再养一个 Agent 了。7. 踩坑记录与排查速查7.1 工具调用类问题问题模型不调用工具直接编答案。排查顺序先看工具描述是否清晰再看系统提示词有没有强制要求最后看模型本身的能力。有些小模型对工具调用的支持就是弱换模型比调提示词有效。问题模型调用工具但参数传错。八成是 schema 不够严格。加 enum、加格式说明、加示例值。如果参数是嵌套结构考虑拆成扁平参数模型处理扁平结构更稳。问题工具调用陷入死循环。加调用次数上限加失败重试次数上限加连续失败两次就转人工的规则。别指望模型自己意识到在绕圈。7.2 MCP 类问题问题MCP Server 连不上。先手动跑启动命令确认能起来再检查配置文件路径和参数stdio 类型注意工作目录很多 Server 是相对路径启动的工作目录不对就找不到资源。问题工具列表里看不到某个工具。检查 Server 的list_tools是否正常返回检查客户端有没有做工具过滤检查工具名有没有和已有工具冲突。问题调用超时。远程 MCP Server 注意网络和超时配置本地 Server 注意是不是卡在某个阻塞操作上。给每个工具调用设独立超时别用全局超时。7.3 Skill 类问题问题Skill 不触发。触发条件写太窄或者和系统提示词的优先级冲突。测试时把触发条件单独拎出来验证确认模型能识别。问题Skill 执行到一半跑偏。步骤描述不够具体模型自由发挥空间太大。把每一步的输入输出写死减少模型的判断点。问题Skill 之间互相干扰。多个 Skill 的触发条件有重叠或者依赖的工具冲突。给 Skill 加优先级明确冲突时的处理规则。问题类型典型表现首选排查方向快速修复工具调用编造结果工具描述与系统提示词加禁止编造约束工具调用参数错误schema 严格度加 enum 与示例MCP连接失败启动命令与配置手动验证启动MCP工具缺失list_tools 返回检查过滤与命名Skill不触发触发条件放宽或加显式调用Skill执行跑偏步骤具体度写死输入输出8. 我个人的几点体会搭 Agent 这件事我踩过的坑比写过的代码多。最大的体会是别指望模型自己变聪明要靠架构把它的不确定性框住。工具描述写清楚、MCP 权限收窄、Skill 步骤写死这些看起来笨的办法恰恰是让 Agent 稳定干活的关键。第二个体会是分层要趁早。一开始图快把三层揉在一起后面每加一个功能都要动全身。早点分层哪怕初期多写点胶水代码长期看是省事的。第三个体会是测试要覆盖对抗场景。正常输入下跑通不算本事能扛住提示词注入、能优雅处理工具失败、能在信息不足时主动问人这才是一个能上生产的 Agent。我现在的习惯是每个 Skill 上线前至少跑二十个对抗用例跑不过就不上。最后分享一个小技巧给 Agent 加一个思考日志把每一步的判断依据、调用了什么工具、拿到了什么结果都记下来。出问题的时候翻日志比盯着对话历史猜快得多。这个日志不用给用户看但对你排查问题价值极大。
返回列表