AI Agent扩展机制解析:Skill、MCP与插件如何协同应用 做 AI Agent 落地这段时间我几乎每隔两天就会被人问同一个问题MCP 都出来这么久了插件是不是该淘汰了或者是反过来——Skill 在老外圈子里火得不行到底和插件、MCP 是什么关系其实第一次被问的时候我还会认真解释后来慢慢意识到在大部分人心里Skill、MCP 和插件已经被默认摆在了同一个赛道里好像只能三选一。这个误区不解决后面所有工程决策都会跟着跑偏。这篇文章我会结合自己实际做 Agent 项目的经验把这三件事拆开讲清楚每个概念解决什么问题、为什么它们不是替代关系、怎么在同一个项目里同时用起来。内容适合正在做 Agent 工程化的人也适合刚接触这三个概念、被各种教程绕晕的开发者。我会尽量用大白话把原理讲透每一步都给出可以直接抄的代码和目录结构最后还会附上一些只有踩过坑才写得出来的注意事项。1. 先别急着选边三个概念到底在解决什么问题1.1 插件应用壳里的外挂模块最传统的扩展形态插件这个概念比 AI 古老得多。浏览器插件、IDE 插件、游戏模组本质都是同一个套路宿主程序定义好扩展点插件开发者按照契约实现一块能力运行时再加载进去。对话式 AI 的插件时代也是这么来的——ChatGPT 早期 Plugin 生态、各大 IDE 里的 AI 插件都是把外部服务的能力包装成宿主可以调用的函数。这套模式最大的特点是绑定关系强。插件是为某个具体宿主写的宿主换了插件基本要重写。比如你今天给 ChatGPT 写了 Plugin明天要迁到别的 Agent 上会发现契约完全不同。插件的优缺点都来自这里优点是离宿主近、加载快、能充分利用宿主能力缺点是复用性差一套能力换一个壳就要改一遍。如果打比方插件就像是电视的机顶盒。电视留了 HDMI 口机顶盒插上去就能看新频道但换一台不同规格的电视旧盒子可能就插不上了。插件解决的是这个宿主现在能接什么的问题它是应用层的封装不是协议层的标准。1.2 Skill把会做事的经验打包成可复用技能Skill 是这两年才火起来的概念典型代表是 Claude 生态里的 Agent Skills。一个 Skill 听起来很玄实际就是一个文件夹里面有一个 SKILL.md 文件描述这个技能怎么用、在什么场景下触发、有哪些执行步骤旁边可以放辅助脚本、示例数据、参考资源。模型在对话中根据用户请求去检索和加载对应的 Skill然后把里面的方法附着到当前任务上。Skill 和插件最本质的区别在于插件加的是能连什么Skill 加的是这件事该怎么做。一个 Skill 不一定要调外部接口它甚至可以只是一套审核清单、一份写作规范、一组字段字典。核心是帮模型把经验结构化——老员工带新人时给的不是一堆 API而是一本流程手册加工具包先确认需求、再查系统、遇到异常回滚重试。也正因为如此国内不少平台把类似概念叫技能工作流Claude 生态叫 Skill产品形态略不同但做的事情是同一件把做事的步骤和判断标准沉淀给模型让它在新的上下文里也能照着一套成熟方法干活。1.3 MCP统一连接协议像软件世界的 USB-CMCPModel Context Protocol是 Anthropic 在 2024 年底开源的应用层软件协议全称叫 Model Context Protocol。它解决的是 Agent 和外部工具、数据源之间怎么连的标准化问题。简单说MCP 定义了客户端和服务器之间的通信规则Agent 作为 MCP ClientMCP Server 对外暴露工具、资源和提示词客户端通过协议发现这些能力然后调用。有人分不清 MCP 到底是软件协议还是硬件协议——它当然是软件协议基于 JSON-RPC 2.0支持 stdio、HTTP SSE、Streamable HTTP 等传输方式。但它设计哲学上和硬件世界的 USB-C 非常像所以大家都拿 USB-C 打比方。USB-C 的意义不在于某一个扩展坞有多强而在于它定义了一个统一接口显示器、硬盘、手机都能插。MCP 也想干这件事让所有 Agent 用同一套方式去接所有工具服务。这个方向在过去一年跑得飞快。OpenAI 在 2025 年宣布支持 MCPGoogle、Microsoft 的生态也在跟进IDE、设计工具、游戏引擎甚至后台管理系统都在往 MCP 上靠。我一个做 Java 后端的朋友最近就在给公司内部的 RuoYi 后台管理框架写 MCP 功能合并方案把原来一堆零散的工具接口整理成标准 Server——这就是趋势在落地时的样子。1.4 一张表看本质差异维度插件SkillMCP本质宿主的扩展机制经验与方法包通信协议标准回答的问题这个宿主能接什么这件事该怎么做这个能力怎么被发现和调用绑定关系强绑定具体宿主可跨宿主迁移与宿主解耦通用典型代表ChatGPT Plugin、IDE 插件Claude Agent SkillsMCP Server 生态核心工作量代码封装文档、示例、流程沉淀协议服务实现与部署看这张表就不难理解为什么大家会混淆。三者都在给 Agent 加能力这个大概念下工作你如果只看表面确实都是往 Agent 里塞东西。但它们处在不同层级插件是宿主层Skill 是智能层MCP 是连接层。层级不同就不存在三选一的问题而是要像搭积木一样组合起来用。2. 三者根本不冲突一套 Agent 架构里的三层分工2.1 从一次真实调用看分工周报 Agent 是怎么跑起来的我拿自己做过的数据周报 Agent来举例。这个 Agent 的活儿很简单用户说一句帮我看一下本周核心指标然后出一份周报发到群里它需要完成三件事从数据仓库取数、按模板生成分析周报、把周报推到钉钉或者企业微信群。这三个子任务恰好对应三种扩展机制。取数我做成一个 MCP Server暴露get_daily_metrics之类的数据查询工具任何接进来的 Agent 都能用不绑死这一个项目写周报我做成一个 Skill里面写清楚周报要分哪几个章节、指标对比口径是什么、哪些结论必须标待验证推群聊消息我写成一个插件因为钉钉 SDK、企业微信 SDK 这类宿主服务耦合很紧没必要也不可能做成通用协议。流程串起来就是模型收到用户指令后先从 Skill 库里检索到周报生成这个技能包按技能包里的步骤指引去调用 MCP 工具取数最后在收尾阶段触发插件函数完成消息推送。一次看似简单的任务里三种扩展机制全用上了而且各干各的、互不打架。2.2 Skill 调度 MCP 工具方法层与连接层的协作很多人觉得 Skill 和 MCP 是对立的因为 Skill 里好像也可以带脚本MCP 也是个能力看起来功能重叠。实际用起来正好相反Skill 反而是 MCP 工具最好的调度说明书。模型光有一堆 MCP 工具列表是不够的它不知道什么时候该用哪个顺序是什么边界在哪里。而 SKILL.md 恰好可以提供这些上下文。我习惯在 SKILL.md 里直接写清楚首选调用哪个工具、参数怎么给、失败时用什么兜底。比如周报技能里就写着先调用get_daily_metrics获取数据时间范围优先取用户指定区间数据不完整时标注缺失而不是自动补全输出按摘要、指标表、异动分析三段来。模型照着这份说明去调 MCP 工具准确率明显高于裸给工具列表。换句话说Skill 负责教模型怎么想MCP 负责解决怎么连到那个工具。2.3 插件兼容 MCP传统封装向标准协议靠拢再回头看插件和 MCP 的关系。传统插件封装了大量 SDK比如我们内部封装的钉钉消息推送、工单系统操作原本都是直接写死在 Agent 代码里的函数。这种方式的麻烦在于每接一个新 Agent函数得复制一份每次 SDK 升级所有副本都要跟着改。后来我们把可复用的那部分往外抽钉钉、飞书、企业微信的推送统一抽象成消息发送能力做成一个 MCP ServerAgent 通过协议调用底层换成哪家 IM 对上层无感。而和宿主生命周期强绑定的东西比如某个 IDE 里的右键菜单、某个 Web 应用里的按钮回调依然用插件形式保留在宿主内部。所以现实的架构里MCP 会吃掉一部分跨系统共享工具但不会消灭插件因为它负责的是宿主特有的交互逻辑。2.4 框架落地LangChain LangGraph 里三种机制的存放位置那在代码层面怎么组织才不会乱我自己比较顺手的目录结构大致是这样的agent-project/ ├── agent/ │ ├── graph.py # LangGraph 编排 │ ├── prompt.py # 基础 system prompt │ └── tools/ │ ├── mcp_clients.py # 管理 MCP 连接与工具列表 │ ├── plugins/ # 传统插件封装宿主绑定逻辑 │ │ └── im_push.py │ └── skills/ # Skill 技能包纯内容 辅助脚本 │ ├── weekly_report/ │ │ ├── SKILL.md │ │ ├── scripts/ │ │ └── resources/ ├── mcp_servers/ │ ├── metrics_server/ # 独立部署的 MCP Server │ └── ... ├── app.py # FastAPI 入口对外提供 HTTP 接口原则很简单MCP Client 统一放在一个模块里基于协议发现工具插件保持轻量只做宿主专用函数的薄封装Skill 本身不含业务代码它只是给模型看的文档 必要时被调用的辅助脚本。这样分层以后哪个环节出了问题都能快速定位不用在一个巨大的工具注册表里翻来翻去。3. 实操让同一个 Agent 同时用上 Skill、MCP 和插件3.1 场景设计一个数据周报助手需要拆成哪几层前面讲了那么多理论这一节直接上手。我们做一个最小可跑的数据周报助手技术栈就用很多团队已经在用的 FastAPI LangChain LangGraph。它要满足三个功能点从本地 SQLite 数据库查询每日指标、按既定格式生成周报 Markdown、推送到企业 IM 群。对应的三种扩展机制分别是MCP Server 提供指标查询能力、Skill 提供周报写作方法、插件封装 IM 推送。这个场景刻意把三件事拆得非常清楚方便你看到它们之间是怎么配合的。真实项目里边界可能要再调但骨架是一样的。3.2 第一步用 FastMCP 搭数据源定义标准 MCP 服务MCP Server 我用 FastMCP 这个库来写它把协议细节封得比较干净适合快速实现工具服务。下面这段代码暴露了一个查询指标的工具from fastmcp import FastMCP import sqlite3 mcp FastMCP(metrics-server) mcp.tool() def get_daily_metrics(start_date: str, end_date: str) - list[dict]: 查询指定日期区间内的核心运营指标。 参数 start_date: 开始日期格式 YYYY-MM-DD end_date: 结束日期格式 YYYY-MM-DD 返回 list每个元素包含 day、uv、order_cnt、gmv 四个字段。 conn sqlite3.connect(metrics.db) cur conn.cursor() cur.execute( SELECT day, uv, order_cnt, gmv FROM daily_metrics WHERE day BETWEEN ? AND ?, (start_date, end_date), ) rows cur.fetchall() conn.close() return [ {day: r[0], uv: r[1], order_cnt: r[2], gmv: r[3]} for r in rows ] if __name__ __main__: mcp.run(transporthttp)这里有几个值得留意的细节。工具的 docstring 不是给人看的注释而是给模型做意图识别用的所以一定要写清功能、参数格式和返回结构这直接影响模型能不能在正确的时候选中它。另外transport参数本地调试用stdio很方便但生产环境一定要切到http后面讲并发的时候我还会再展开。3.3 第二步用插件封装企业 IM 推送能力IM 推送不需要做成 MCP Server它就是我们说的宿主级插件——因为推送逻辑和企业群机器人的 Webhook 强绑定属于这个 Agent 项目特有的能力。封装一个类就够了# agent/tools/plugins/im_push.py import requests class ImPushPlugin: 宿主级插件把消息推送到企业 IM 群。绑定具体平台 Webhook。 def __init__(self, webhook_url: str): self.webhook_url webhook_url def send_markdown(self, title: str, text: str): payload { msgtype: markdown, markdown: {title: title, text: text}, } # 超时时间必须设置避免 Agent 工作线程被外部接口拖死 resp requests.post(self.webhook_url, jsonpayload, timeout5) resp.raise_for_status() return resp.json()这段代码本身平淡无奇但定位很重要它只负责推送到当前宿主能感知的地方不承担任何数据处理和分析职责。如果你发现一个插件越写越复杂开始揉进业务逻辑了那就是该抽 MCP Server 的信号。3.4 第三步把分析流程沉淀成 Skill 包Skill 包就是一组文件核心是 SKILL.md--- name: weekly_report description: 当用户需要生成周报、日报或数据总结时使用。适用于运营、产品、数据团队的指标分析场景。 --- ## 适用场景 - 用户要求生成周报/日报/数据总结 - 涉及指标对比、环比、异动归因的分析任务 ## 执行步骤 1. 先调用 MCP 工具 get_daily_metrics 获取原始数据时间范围优先取用户指定区间。 2. 核对数据是否完整缺失日期的数据要标注数据缺失不要自动补全。 3. 用本周 vs 上周对比口径撰写异动指标要给出可能原因假设。 4. 输出 Markdown 周报包含摘要、核心指标表、异动分析三个部分。 ## 禁忌 - 不要编造数据没有数据支撑的结论必须标注为待验证。 - 周报正文中不要出现模型推测字样直接表述分析结论即可。 - 如果用户没有指定时间范围默认最近 7 天。SKILL.md 开头的description字段决定了模型什么时候加载这个技能包所以触发条件要写清楚宁可稍微宽一点也不能让模型该用时想不起来。执行步骤最好有编号明确顺序和默认值这样模型就不用临场猜逻辑。3.5 第四步在 LangGraph 里完成编排最后用 LangGraph 把三个节点串起来。为了让代码可读我把工具调用简化为本地函数语义实际项目中是通过 MCP SDK 建立连接后调用概念相同from typing import TypedDict from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, END class AgentState(TypedDict): messages: list raw_data: list report: str llm ChatOpenAI(modelgpt-4o) def fetch_data(state: AgentState) - AgentState: # 这一步调用 MCP Server 暴露的工具 data call_mcp_tool(get_daily_metrics, start_date2025-05-05, end_date2025-05-11) return {raw_data: data} def write_report(state: AgentState) - AgentState: # 这一步把 SKILL.md 内容注入上下文作为模型写作的指导规范 with open(agent/tools/skills/weekly_report/SKILL.md, encodingutf-8) as f: skill_content f.read() prompt f{skill_content}\n\n数据如下\n{state[raw_data]}\n请生成周报。 resp llm.invoke(prompt) return {report: resp.content} def push_to_group(state: AgentState) - AgentState: # 这一步触发宿主级插件 plugin ImPushPlugin(WEBHOOK_URL) plugin.send_markdown(title本周数据周报, textstate[report]) return {messages: [(assistant, state[report])]} graph StateGraph(AgentState) graph.add_node(fetch_data, fetch_data) graph.add_node(write_report, write_report) graph.add_node(push_to_group, push_to_group) graph.set_entry_point(fetch_data) graph.add_edge(fetch_data, write_report) graph.add_edge(write_report, push_to_group) graph.add_edge(push_to_group, END) app graph.compile()这个图把整个链路表达得很直观数据从 MCP 来报告按 Skill 规则写推送靠插件完成。三者不是谁替代谁而是像流水线上的三道工序各司其职。4. 选型与架构决策什么时候用谁、并发与安全怎么管4.1 场景选型速查表遇到需求先查这张表做技术决策时我习惯先问自己一个问题这个能力未来会给几个 Agent、几个系统共用如果答案是可能很多那优先考虑 MCP如果答案是就现在这个宿主用插件更快如果诉求是让 Agent 按一套 SOP 高质量完成任务那就该写 Skill。你的诉求首选原因多个 Agent 共享同一数据源MCP协议标准化Client 通用让 Agent 学会一套复杂流程/规范Skill沉淀方法论模型才知道怎么做快速接入某个宿主的私有 SDK插件与宿主生命周期最匹配把已有工具开放给整个生态MCP一次开发多端接入IDE 或应用内加自定义入口插件宿主扩展点原生支持团队新人快速按标准产出交付物Skill经验直接注入减少临场发挥我见过有些团队一上来就追求全 MCP 化把所有东西都往标准协议上套结果一个 Web 应用的右键菜单、表单按钮这种纯宿主交互也强行做成 MCP维护成本反而更高。反过来只图快、全部写成插件后面接新 Agent 的时候就要哭。所以选型不是问哪个更好而是问这个能力长在哪个层次。另外如果你是 Java 技术栈Spring AI 生态内部也已经集成了 MCP 支持方案基本可以平移。做 Agent 中台的时候MCP 是比维护一堆私有插件更值得投资的方向因为中台的核心价值就是让多个业务方复用同一套技术底座。4.2 Agent 怎么扛并发瓶颈往往在工具层不在模型层聊到 Agent 并发不少人第一反应是把模型请求打满或者花钱买更高的 RPM 配额。但实际压测下来最先挂掉的往往不是模型接口而是工具层。原因在于 Agent 的一个任务不是一次请求而是推理 — 调工具 — 再推理 — 再调工具的多轮循环一次用户请求可能产生十几次工具调用。并发用户一多MCP Server、插件里的外部 API 就全成了瓶颈。我踩过的坑可以列几个典型的开发环境图省事用stdio跑 MCP Server每次工具调用都是新起子进程并发一上来 CPU 直接爆炸。生产环境必须换 Streamable HTTPAgent 进程复用长连接。MCP Server 没做结果缓存同样的查询参数被反复请求。数据指标这类读多写少的场景完全可以在 Server 内部加一个内存缓存或者 Redis 缓存。插件里的外部 HTTP 请求不设超时一次 Webhook 卡住工作线程全被占住。这个在上面的示例代码里我已经加了timeout5这是最基础的防御。没做工具调用限流。最可怕的情况是工具描述不清楚模型在循环里反复调用同一个查询工具QPS 瞬间打满。后来我在 MCP Server 层加了同参数结果缓存 每会话调用次数上限再用优化后的工具描述这个问题才解决。所以Agent 怎么扛并发这个问题我现在的答案很明确Agent 服务本身要异步化用 FastAPI 起异步 Worker把任务丢进队列MCP Server 做成无状态可横向扩容的服务插件里的外部调用统一走超时、重试、熔断三件套。模型层的 RPM 配额当然也要买够但那是另一个话题工具层的稳定性才是真正的生命线。4.3 权限与安全边界三种扩展各有各的雷MCP 的权限模型还在快速演进但现在做生产系统就可以开始注意几点。第一把 MCP Server 当成不可信外部服务对待不能因为它是自己部署的就默认安全连接要做白名单、鉴权用 API Key第二MCP Client 在首次连接时弹出授权确认类似手机 App 的权限弹窗这个环节不能省第三Server 暴露的工具集要遵循最小化原则——只给 Agent 它真正需要的那几个工具别把整个数据库都挂在协议上。很多团队做完数据问答 Agent就发现模型虽然不会主动干坏事但工具太多时错误的调用概率会成倍上升。Skill 的风险在于它是自然语言 脚本的混合体。一个恶意的第三方 Skill 可以在 SKILL.md 里写一段误导模型的指令也可以在辅助脚本里放不安全的代码。我在团队里定的规矩是外部来的 Skill 一律当不可信文本处理加载前做代码评审脚本统一放沙箱环境执行不允许 Skill 里的脚本直接访问生产凭据。插件这边则是供应链问题。公开市场的插件质量参差不齐闭源插件你根本不知道里面装了什么。我的建议是依赖锁版本插件市场里的包统一走内部仓库二次分发对外部来源保持怀疑。这跟前面讲的所有技术选型一样扩展能力越强越要看好自己的边界。5. 常见问题与排查技巧实录5.1 MCP 工具时而找得到、时而找不到一个很典型的现象模型在对话里明明提到了某个指标却没有调用对应的 MCP 工具或者上周还能调这周忽然不调了。我排查这类问题的顺序一般是三条。第一确认工具列表里到底有没有这个工具。很多 MCP Client 有工具列表缓存Server 新增了工具但没通知 Client 刷新重启连接后才会同步。第二检查工具描述是否足够具体。模型是靠描述做意图匹配的如果你写的是查询数据它很可能在多个相似工具之间犯迷糊写成查询指定日期区间内的核心运营指标UV、订单数、GMV返回列表结构命中率立刻不一样。第三看 Server 日志里连接是否被异常重建有时网络波动导致 stdio 传输的进程退出工具就消失了。实测下来大部分时灵时不灵其实不是协议问题而是描述和缓存的工程问题。先看工具列表再看描述文案最后才是协议本身。5.2 Skill 文件编码与路径的坑凡是把 Skill 仓库放到不同系统间共享的团队大概率会撞上文件编码相关的怪问题。Windows 环境下保存的 SKILL.md 偶尔会带上 GBK 编码或者被编辑器写成带 BOM 的 UTF-8模型读出来就是乱码技能加载直接失败。还有换行符问题CRLF 和 LF 混在一起的时候脚本文件在 Linux 容器里跑起来经常出现找不到命令或者路径不对的诡异报错。我的铁律是Skill 目录文件名统一用 ASCII 命名不要放中文名和空格所有内容文件统一 UTF-8 无 BOM换行符统一 LF仓库根目录放.gitattributes强制*.md text eollf。另外在加载 Skill 时打印文件哈希版本回滚、问题定位都方便很多。这些细节看着不起眼但一旦踩中排查起来非常耗时间。5.3 插件与 MCP 工具重名冲突项目规模变大后插件函数和 MCP 工具可能因为名字太像而互相干扰。比如插件里有个send_reportMCP 工具里也有个send_report_to_im模型就很容易选错。问题一旦出现表现形式就是调了但没完全调对——该发到企业微信的消息发到了钉钉该写周报的工具却触发了推送。解决思路有三层命名上加前缀做能力域隔离比如 MCP 工具统一用data_开头、插件统一用im_开头再在系统提示词里点一句不同前缀对应不同能力域不要交叉调用如果低优先级工具仍然持续干扰模型判断该禁用就先禁用掉。别觉得这是笨办法在模型意图理解还没那么精准的今天显式的命名约束就是最有效的防错手段。5.4 工具一多 Agent 就变笨先想清楚再堆工具热词里大家都在问工具怎么加其实真正的反面问题是工具加太多。我见过一个团队把 100 多个工具一股脑全注册进去结果是模型选择准确率肉眼可见地往下掉。原因也好理解所有工具描述都塞在上下文里注意力被稀释了模型像在一个塞满杂物的工具箱里找一把螺丝刀翻半天拿出来的却是开瓶器。我的做法是两级结构Skill 管路由MCP 管执行。模型先判断当前任务属于哪个技能包加载对应的 SKILL.md 后再按需挂载相关工具而不是一开始就什么都给。同一域内的工具保持在 10 个以内描述精炼把类型细节放进 input schema 而不是塞进描述正文。工具库不是越大越好够用且清晰Agent 才会聪明。6. 我的实操心得与扩展思路6.1 先建连接再沉淀技能最后处理宿主绑定我的建议是不要反过来。如果一开始就在宿主里写插件业务逻辑很快会渗进插件代码里后面想抽 MCP 就得动刀子。反过来先把共享能力用 MCP 暴露出来让 Agent 能连到一个中立的数据源和工具服务再在真实使用过程中沉淀 Skill——你会发现那些模型老不会用的问题恰恰是最值得写进 SKILL.md 的东西。等到跑稳定了再去处理宿主的特殊交互用插件做薄薄的一层适配就够了。这套顺序还有一个额外好处Skill 沉淀得越早团队的经验越不容易流失。老员工脑子里那套遇到异常先回滚再核对的判断逻辑写进 SKILL.md 之后新模型、新人都能照着跑。6.2 可以继续往哪个方向扩展顺着这个思路往下走下一步比较值得投入的方向是Skill 资产化。把内部文档、SOP、典型问答记录转成 Skill 包形成一套团队私有的方法库再叠加检索逻辑让 Agent 按相关性自动选择加载哪几个技能。有人把这类实践叫 book-to-skill本质就是让知识从静态文档变成动态可执行的方法。还有一个容易被忽视的方向是工具治理。MCP Server 多了以后谁来发布、谁来上线、调用量怎么统计、故障怎么追踪都需要纳入到规范里。用 Agent 中台统一管理各种 Server 的接入和发布这件事比单点做工具要有价值得多。最后多说一句经常有人问个人能不能用 Agent 做自动交易、期货套利这类事。我的态度很明确用来做数据分析和策略研究没问题但真实资金操作风险极高没有专业风控体系之前千万别让 Agent 直接下单。工具是工具责任边界得有人兜底——这跟今天聊的 Skill、MCP、插件一样再强的能力也得在设计好的边界里用。