资讯动态

Prompt、Skill、MCP与SubAgent:Coding Agent四层架构解析

发布时间:2026/10/14 23:17:58 来源:尧图企业网站定制
最近连续好几个做 Coding Agent 的朋友都在问同一个问题Skills 和 MCP 到底什么关系有个团队内部甚至为此吵了起来一派说 MCP 是 Skills 的下一代另一派说 Skills 是 MCP 的封装壳。我每次都得从 Prompt 讲起一路讲到 SubAgent才能把话说清楚。之所以这么费劲是因为这四个词在社区里被并列讨论得太久了大家默认它们是同一层的东西可以互相替代。可真到工程落地的时候你会发现它们根本不在一个维度上。这篇文章我不打算只丢一张对比表就完事。我会先把四个东西各自的定位用一句话钉死再讲清楚两两之间真正的边界然后给一个完整的真实场景选型PR 审查助手最后附上我整理的一套最小可运行源码。适合两类人看一类是正在搭 Coding Agent、被这几个词绕晕的开发者另一类是已经在用其中某个方案、想知道另外几个能不能一起用进来的朋友。文档只会告诉你是什么很少告诉你为什么这么分层以及实际项目里怎么组合后面这块才是踩坑最多的地方。1. 先想明白一点这四个词不在同一层别直接对比无论哪个框架、哪篇文档只要把这四个词并列出来就已经在制造误解了。因为其中两个是规范/实体一个是内容还有一个是架构模式。硬要放在天平上比谁替代谁注定比不出结果。1.1 Prompt 是地基也是唯一内容层的东西Prompt 说白了就是塞进模型上下文窗口的那段文本系统提示词、用户消息、few-shot 示例全是 Prompt。它的本质是告诉模型此时此刻应该怎么想、怎么做。这是所有 Agent 行为的起点没有它模型只是一坨参数矩阵什么也干不了。所以你会发现一个很有意思的事实后面讲的 Skill、MCP、SubAgent最终都会以某种形式变成Prompt的一部分——Skill 被加载后是加进上下文的指令MCP 工具的描述会写进工具列表让模型看到SubAgent 更是自带一整条系统提示词。这个万变不离其宗的规律是整篇文章最重要的一根线。1.2 Skill 是能力封装包面向复用设计Skill 是把一段可复用的指令打包成文件文件名、描述、触发条件、指令正文可能还附带脚本。它的核心价值在于按需加载和复用——不在上下文里躺着占位置等需要时通常是描述匹配命中才被塞进去。举个很普通的例子你们团队有一套代码审查规范两千字。如果你把它写成 Prompt 硬塞进系统提示词那每一轮对话都要为这两千字付钱但如果把它做成一个 Skill平时它根本不出现在上下文里只在用户真的要求审查代码时才会被加载进来。这一进一出省掉的可能是一整条链路的 token 开销。1.3 MCP 是连接协议面向互通设计MCPModel Context Protocol模型上下文协议解决的是 Agent 和外部工具之间怎么接的问题。它定义了一套标准接口工具是什么、参数长什么样、结果怎么返回。只要两边都遵循这套协议任何 Agent 客户端都能接任何 MCP 服务端就像任何设备都能插同一个标准的充电口。所以 MCP 不是能力本身它是能力的插座。同一个 MCP 服务端可以同时服务多个不同的 Agent也可以被同一套系统反复复用。它不关心你的 Agent 怎么思考只负责把外部世界的操作安全、标准地递到你面前。1.4 SubAgent 是运行模式面向编排设计SubAgent 不是一种绑定在 Agent 上的能力而是一个架构模式主 Agent 觉得当前任务太杂就新建一个独立运行的 Agent 实例给它独立的上下文、独立的系统提示词、甚至独立的工具集让它专注解决某一块然后把结果交回主 Agent。这个模式是包含关系而不是平级关系。一个 SubAgent 可以加载自己的 Skill也可以通过 MCP 客户端调用工具它的一切行为同样由 Prompt 驱动。你甚至可以把主 Agent 也理解成一个特殊的 SubAgent——只是它多了一个调度其他 Agent 的权限。1.5 一个不太正经但好用的记忆锚点把四个东西放到餐厅场景里Prompt 是你对厨师说的每一句话Skill 是厨师手边的 SOP 手册遇事翻开照做MCP 是后厨所有电器统一的插座标准什么牌子都能插SubAgent 是你临时雇来的配菜师傅他也有自己的手册和工具干活独立做完把成果端给你。为了方便对照我把它们放在一张表里概念本质抽象层级典型用途Prompt发给模型的指令文本内容层控制模型行为、设定角色、传递任务Skill可复用的能力封装包能力层按需加载团队规范、工作流、审查规则MCP工具连接的开放协议连接层对接代码托管平台、数据库、浏览器等外部系统SubAgent独立运行的子任务执行进程编排层并行审查、上下文隔离、专业化分工提示下次再有人问Skill 是不是 MCP 的下一代就把这张表拿给他看。一个管做什么一个管怎么接完全没有可比性。2. Prompt 与 Skill一次性指令和可复用能力包的分界线很多人觉得 Skill 就是结构化一点的 Prompt这个说法只对了一半。Skill 里确实装着 Prompt 文本但 Skill 多出来的东西是 Prompt 根本给不了的。2.1 长时间挂在大上下文里的 Prompt其实很贵先说 Prompt 的隐藏成本。我们把一套完整代码审查规范写进系统提示词看起来没问题但它有个致命缺陷无论模型当前在处理什么任务这段规范都在上下文里。每一轮对话、每一次工具调用、每一次模型思考都要重新处理这两到三千 token 的文本。我做过一次粗略估算。一个中等活跃的 Coding Agent 会话一天可能来回几百轮假设上下文里常年挂着 2,500 token 的规范一天额外消耗可能就是几十万 token。更麻烦的是这些规范还会抢注意力——模型在处理一个帮我写个 README的任务时还要同时 hold 着审查代码要注意 SQL 注入这类高优先级指令反而容易答非所问。2.2 Skill 多出来的三样东西按需加载、文件化、可执行Skill 的第一个差异是按需加载。它平时躺在磁盘上只有描述匹配到用户意图时才被注入上下文。这就解决了常年占位的问题。第二个差异是文件化。Prompt 活在对话里是一次性的Skill 活在仓库里可以被版本管理、被团队评审、被跨项目复制。审查规范从 A 项目复制到 B 项目Prompt 时代你得重新粘一遍Skill 时代直接引用同一个文件。第三个差异是我认为最关键的可执行。Prompt 只能说你可以检查一下代码复杂度Skill 的配套脚本可以直接调起复杂度计算工具把结果贴进上下文让模型判断。前者是让模型看着办后者是让模型基于真实数据做决策。2.3 什么样的情况该从 Prompt 升级到 Skill如果你的 Agent 出现下面任一种情况就该考虑把某段 Prompt 抽成 Skill同一段指令在多个项目、多个子任务里反复出现这段指令超过了大约 2,000 token而且有条件分支像如果变更涉及鉴权接口则需要额外检查这种指令里出现了需要真实计算结果的诉求而不只是要求模型脑补指令需要多人维护、定期更新。反过来如果只是一次性任务比如帮我润色这段注释老老实实写 Prompt 就行别为了炫技硬造一个 Skill 文件。2.4 常见误区把 Skill 当成万能 Prompt 收纳箱有些人把所有的规则全塞进一个超级 Skill里结果触发时一加载又是几千 token。Skill 的灵魂是按需意味着粒度要适中。我会把 code-review 拆成 security-review、perf-review、style-review 三个小 Skill而不是一个大而全的审查总纲。这也正好为后面 SubAgent 的分工埋了个伏笔。评判标准很简单如果在一次任务里两个 Skill 总是成对出现、且其中一份明显只在特定子环节有用那就说明粒度拆大了。3. MCP它解决的是连接问题不是能力定义问题如果说 Skill 是在怎么教这件事上做了升级那 MCP 就是在怎么接这件事上立了规矩。3.1 没有 MCP 之前工具接入的混乱账我早期搭过一个内部 Agent需要接三个外部系统代码托管平台、内部数据库、浏览器自动化。当时每个系统都要单独写一个连接器而且连接器的写法跟 Agent 的实现强耦合——换一个 Agent 框架三个连接器全部重写。这就是经典的 N×M 问题N 个工具 × M 个 Agent每个组合都要单独开发。MCP 把这道题解成了 NM工具方实现一个 MCP 服务端任何支持 MCP 客户端的 Agent 拿去就能用。它是一个 JSON-RPC 2.0 协议支持 stdio 和 HTTP 传输服务端会暴露三类对象工具可调用的函数、资源可读的数据、提示词模板可复用的 Prompt。3.2 Skill 与 MCP 的真正边界一个管怎么做一个管怎么接经常有人把 Skill 和 MCP 放在一起比较因为它们看起来都像给 Agent 加能力。但边界其实很清楚Skill 定义的是内部工作流触发这个 Skill 后Agent 应该按什么步骤走、遵循什么规则MCP 定义的是外部接口契约某个操作叫什么名字、传什么参数、返回什么结构。两者是协作关系不是替代关系。一个 Skill 的内部步骤完全可以写着调用 MCP 工具 fetch_pr_diff 获取变更内容。Skill 提供的是判断逻辑和流程约束MCP 提供的是真正能把活干完的手。我在实际项目里的经验是凡是团队独有的规则、流程、判断标准优先做成 Skill凡是要和外部系统打交道、且将来可能给多个 Agent 复用的优先做成 MCP。一个偏内部一个偏外部方向完全不同。3.3 什么能力适合做成 MCP Server判断标准其实就三条有没有标准的对外接口有比如代码托管平台的 REST API就适合包一个 MCP是不是多个项目、多个 Agent 都要用是就值得做成通用的有没有状态和资源需要管理有比如数据库连接、浏览器会话MCP 可以在服务端管理而不是把状态塞进模型上下文。反过来如果一个能力只是某团队内部的一次性流程没有任何外部系统交互那做成 MCP 纯属浪费——用 Skill 就够了。4. SubAgent它是编排层不是功能层聊完三个静态的东西该聊那个最容易被误解的 SubAgent 了。它不是一个可以绑定的功能而是一种组织架构。4.1 工具调用和 SubAgent完全是两回事很多初学者以为 SubAgent 就是Agent 调用一个更复杂的工具。差远了。工具调用是同步的——主 Agent 的上下文里发起一个函数结果传回同一个上下文所有中间过程都在主上下文里展开。SubAgent 则是主 Agent 开了一个子进程它有独立的上下文窗口、独立的系统提示词、独立的工具列表、独立的循环步数上限。这个区别带来一个直接后果主 Agent 不会被子 Agent 的中间过程污染。拿 PR 审查举例主 Agent 只需要知道安全审查结果3 个高危项、2 个中危项而不需要关心安全审查 Agent 内部是怎么读 diff、怎么翻文档的。子 Agent 那几千步思考永远不会出现在主对话里。4.2 SubAgent 的三个核心价值隔离、并行、专业化隔离前面说了再补一个反面案例。我曾经在主上下文里连续做三种审查——安全、性能、风格差一点把上下文撑爆。三套审查规范加一份 5,000 行的 diff主上下文很快就逼近长度限制后面审查质量直线下降。后来拆成三个 SubAgent每个只拿自己需要的规则和 diff 片段主上下文始终保持在一个轻量状态。并行也容易理解安全、性能、风格三个审查互不依赖天然是并行任务。三个 SubAgent 同时跑整体耗时从串行的十几分钟压缩到三四分钟具体取决于模型服务的并发能力。专业化则是另一个角度SubAgent 可以配不同的模型、不同的温度、不同的工具集。安全审查想用更严谨的模型风格审查想用快而便宜的模型。这在主 Agent 里很难做到在 SubAgent 层是小菜一碟。4.3 SubAgent 与其他三者的组合关系理清这一层四个词就全部串起来了主 Agent 收到任务后命中某个 Skill比如 pr-reviewSkill 给出流程定义流程里要求调用 MCP 工具获取外部数据比如 diff 文本主 Agent 决定派三个 SubAgent 分工每个 SubAgent 的 Prompt 由主 Agent 生成通常来自子 Skill 的加载每个 SubAgent 内部又可以有自己的 MCP 工具调用和循环最后结果以文本或结构化对象的形式返回主 Agent由主 Agent 汇总。4.4 别把 SubAgent 当成万能药SubAgent 不是免费的它需要时间启动、会消耗独立上下文配额、还可能引入结果格式不一致的问题。如果任务本来一个工具调用就能解决没必要拆出一个完整子进程如果任务是高度耦合的硬拆反而会丢上下文。我的判断原则很简单要不要上 SubAgent就看任务是否同时满足可独立拆分涉及大量中间上下文需要多视角对比。缺一条都建议先留在主流程里解决。5. 真实场景一个 PR 审查助手的完整选型与落地链路纸上谈兵聊了四层概念下面看一个具体的例子——我最近在内部搭的 PR 审查助手。5.1 需求是什么团队代码都托管在一个代码托管平台上希望有个 Coding Agent 能在 PR 提交后自动做三件事安全审查找注入、越权、硬编码密钥、性能审查找 N1 查询、大循环里的 IO、风格审查对照团队规范。审查结果要能发回 PR 评论区。需求听起来很普通但它刚好把四个概念全用上了是天然的对照实验。5.2 为什么这样选型每一层选择的理由先说 MCP。代码托管平台是一个外部系统有公开 API且我们不止一个项目要用这个连接能力。所以第一件事是写一个代码托管平台 MCP Server暴露 fetch_pr_diff 和 comment_on_pr 两个工具。这个 Server 写好之后任何 Agent 都能复用它这是典型的接口标准化收益。再说 Skill。团队的审查规范是文本性质的、团队独有的而且会不断演进。它不应该写死在哪个 Agent 的代码里于是我们把三套审查规则分别做成 security-review、perf-review、style-review 三个 Skill 文件。平时不加载审查时按需注入。最后说 SubAgent。三套审查规则差异极大、关联性很弱而且审查过程会产生大量中间分析每套规则对每段 diff 的分析结果如果全放在主上下文中上下文会被迅速撑爆。所以我们让主 Agent 并行派三个 SubAgent各自加载对应 Skill、对着 diff 独立工作。至于 Prompt它永远是底层主 Agent 的编排逻辑靠系统提示词实现SubAgent 的初始化 Prompt 也是生成出来的。它像胶水一样粘住其他三层。5.3 单个 PR 的完整执行链路流程大概是用户对主 Agent 说审查一下 PR #42主 Agent 的系统提示词里定义了审查编排流程同时按描述匹配加载 pr-review SkillSkill 指令要求先调用 MCP 工具 fetch_pr_diff 拉取 diff——这一步把外部数据拉进主上下文只有一份 diff体积可控主 Agent 拆分任务创建 security-review、perf-review、style-review 三个 SubAgent每个 SubAgent 初始化时加载自己对应的 Skill 指令拿到 diff 文本然后独立运行若干轮调用必要的 MCP 工具之后输出结构化结论主 Agent 等待全部 SubAgent 返回按严重程度去重、合并、排序生成一份统一报告最后调用 MCP 工具 comment_on_pr 把报告发到 PR 评论区。第三步有一个设计细节值得说diff 由主 Agent 统一拉取而不是每个 SubAgent 各自拉取。因为拉取操作本身有频率限制让三个 SubAgent 轮询同一个接口既慢又容易触发限流。主 Agent 拉完之后切成片段传给子 Agent是更工程化的做法。5.4 踩过的坑把三套规则全部塞进主 Prompt 的后果这项目一开始不是这么设计的。第一版我图省事把三套审查规则直接拼进主 Agent 的系统提示词审查流程也在同一个上下文里顺序执行。结果遇到大型 PR 就出问题5,000 行 diff 加上三套规则主上下文很快就逼近上限模型开始忘记后面的规则审查质量肉眼可见地下滑而且安全审查的中间分析会把大量无关内容带进对话历史彻底稀释注意力。后来改成 SubAgent 分工后才想明白审查这种任务天然适合上下文隔离 并行主 Agent 只做调度和汇总。改造完以后同一个大 PR 的审查耗时大概是原来的三分之一上下文占用峰值降了六到八倍。6. 源码级参考一套最小可运行的组合实现下面给你看一个能直接跑起来的骨架。它不依赖任何重型框架核心逻辑就三个部分Skill 文件、MCP Server、SubAgent 调度器。接上真实的模型 API 就能当脚手架用。6.1 目录结构pr-review-demo/ ├── skills/ │ ├── pr-review/SKILL.md │ ├── security-review/SKILL.md │ ├── perf-review/SKILL.md │ └── style-review/SKILL.md ├── mcp_server.py ├── subagent.py └── main.py6.2 Skill 文件长什么样以 pr-review 这个编排 Skill 为例--- name: pr-review description: 从安全、性能、风格三个维度审查一个 PR。 when: 用户提到审查 PR、code review或给出 PR 编号时 --- # PR Review 编排流程 当本 Skill 被加载时按以下步骤执行 1. 确定仓库名与 PR 编号。 2. 调用 MCP 工具 fetch_pr_diff 获取 diff 原文。 3. 并行创建三个 SubAgentsecurity-review / perf-review / style-review。 4. 将 diff 按文件切块分发给各 SubAgent。 5. 汇总三份结论按严重程度排序、去重。 6. 调用 MCP 工具 comment_on_pr 提交报告。安全审查 Skill 长这样--- name: security-review description: 分析 diff 中的注入、越权、硬编码密钥等安全问题。 --- 你对传入的 diff 做安全审查。逐文件检查 - SQL 注入是否拼接外部输入到 SQL/查询语句。 - 命令注入是否拼接外部输入到命令行执行。 - 越权新增接口是否缺失权限校验。 - 硬编码密钥是否出现 token、密码、AK/SK 字面量。 输出格式 ## 风险等级高/中/低 ### 文件路径:行号 - 问题描述 - 修复建议这里的关键是 frontmatter 里的 description 字段。加载器靠它来判断什么场景该加载哪个 Skill所以描述必须写得能命中用户意图而不是写用于审查这种废话。6.3 MCP Server 的最小实现MCP 官方 Python SDK 提供了声明式的接口写起来非常直白。核心是暴露两个工具拉取 diff 和提交评论。# mcp_server.py from mcp.server.fastmcp import FastMCP mcp FastMCP(code-host-tools) mcp.tool() def fetch_pr_diff(pr_number: int, repo: str) - str: 从代码托管平台拉取指定 PR 的 diff 文本。 import requests url fhttps://git.example.com/api/v1/repos/{repo}/pulls/{pr_number} headers {Authorization: fBearer {TOKEN}} r requests.get(url, headersheaders) r.raise_for_status() detail r.json() diff_url detail.get(diff_url) return requests.get(diff_url, headersheaders).text mcp.tool() def comment_on_pr(pr_number: int, repo: str, body: str) - bool: 把审查结论作为评论提交到 PR 页面。 import requests url fhttps://git.example.com/api/v1/repos/{repo}/issues/{pr_number}/comments r requests.post(url, json{body: body}, headers{Authorization: fBearer {TOKEN}}) return r.status_code 201 if __name__ __main__: mcp.run()这段代码里的核心是函数签名和 docstring。MCP 的协议层会自动把函数名转成工具名、把参数转成 JSON Schema、把 docstring 转成工具描述——模型就是靠这些描述来决定要不要调用、传什么参数的。所以 docstring 一定要写清楚别偷懒。进程启动后这个 Server 会通过 stdio 等待 MCP 客户端连接。你可以直接把它交给任何懂 MCP 的 Agent 去接不需要为每个 Agent 重写一遍。6.4 SubAgent 的最小实现SubAgent 的核心是一个带独立上下文的 agent loop# subagent.py class SubAgent: def __init__(self, name, system_prompt, tools, max_steps12): self.name name self.system_prompt system_prompt self.tools tools # dict: 工具名 - 可调用函数 self.max_steps max_steps self.messages [{role: system, content: system_prompt}] def run(self, task: str) - str: self.messages.append({role: user, content: task}) for _ in range(self.max_steps): resp llm_call(self.messages, toolslist(self.tools)) self.messages.append( {role: assistant, content: resp[content]} ) if not resp.get(tool_calls): return resp[content] for call in resp[tool_calls]: fn self.tools[call[name]] result fn(**call[arguments]) self.messages.append({ role: tool, tool_call_id: call[id], content: str(result), }) return 达到最大步数任务未完成注意几个细节llm_call 这里泛指你的模型调用入口可以在里面处理流式输出、错误重试messages 是子 Agent 私有的主 Agent 的 message 列表永远看不到它的内部历史max_steps 是硬上限防止某个子 Agent 陷入死循环烧钱。6.5 主流程加载 Skill、启动 MCP、派子 Agent、汇总下面这段伪码省掉了 MCP 客户端的握手细节只保留编排骨架方便你对着结构理解# main.py import asyncio SKILL_DIR skills def load_skill(name: str) - str: path f{SKILL_DIR}/{name}/SKILL.md with open(path) as f: return f.read() def retrieve_skills(user_msg: str) - list[str]: # 简化版用关键词触发。真实实现建议用 embedding 检索。 table { pr-review: [审查, review, PR], security-review: [安全, 注入, 密钥], perf-review: [性能, 慢, N1], style-review: [风格, 规范, format], } hit [] for skill, keywords in table.items(): if any(kw in user_msg for kw in keywords): hit.append(skill) return hit async def main(): # 1. 连接 MCP Serverstd::stdio 启动子进程握手细节省略 # available 是工具名 - 可调用函数的映射 async with _mcp_session() as session: available await collect_mcp_tools(session) # 2. 主 Agent 编排 user_msg 请审查 PR #42 active_skills retrieve_skills(user_msg) orchestration \n\n.join(load_skill(s) for s in active_skills) main_prompt f{MAIN_SYSTEM_PROMPT}\n\n{orchestration} diff await session.call_tool( fetch_pr_diff, {pr_number: 42, repo: demo}, ) # 3. 根据 pr-review Skill 的指令派三个 SubAgent sub_specs [security-review, perf-review, style-review] agents [ SubAgent(spec, load_skill(spec), available) for spec in sub_specs ] # 4. 并行执行实际并行要考虑限流和失败重试 results await asyncio.gather( *[asyncio.to_thread(a.run, str(diff)) for a in agents] ) # 5. 汇总并写入 PR report merge_results(*results) await session.call_tool(comment_on_pr, { pr_number: 42, repo: demo, body: report, })这段代码省掉了不少模型调用细节和连接细节但骨架和真实项目是一致的MCP 把外部能力接进来Skill 提供流程与规则SubAgent 负责隔离式并行执行Prompt 在每一层做驱动。你把这几个文件拆开、各自精化就是一个可以上线的雏形。6.6 这套 Demo 的局限和扩展方向先说局限。第一Skill 触发用的是关键词表真实场景里用户表达千奇百怪建议换成向量检索或者让模型自己做路由判断。第二MCP Server 用的 stdio 传输只适合本地进程多机部署要换 HTTP 传输。第三SubAgent 的并行没有做熔断和失败重试真实环境里某个子 Agent 崩了主流程最好能降级处理。扩展方向也顺手列一下你可以给 SubAgent 加上结果校验器让它在返回前自检格式也可以把加载 Skill 再派 SubAgent的流程抽成一个通用编排框架让不同的 Skill 定义不同的子分工策略。到这一步你手里的就不再是 Demo而是一个有血肉的小型 Agent 平台了。最后说个我自己的设计心法。现在每接到一个 Coding Agent 的能力设计我都会先画一张上下文带宽图主 Agent 的上下文里到底要常驻什么、按需加载什么、哪些内容应该隔离到子 Agent 里去。画完这张图用哪个方案基本就自动出来了。某个能力要跟外部系统打交道、且将来会被多个项目复用那它基本该做成 MCP一套团队独有的判断规则或流程那它该是一个 Skill任务链条长、中间上下文多那它该被拆成 SubAgent而 Prompt 永远是兜底的那个——它是唯一不需要额外基础设施的东西也是一切抽象的第一层地基。把这四个层级想清楚了再看任何 Agent 架构文档基本不会被名词绕晕。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑