本文聚焦三个问题:Agent 是什么,MCP 扮演什么角色,以及在 AI IDE 中如何通过 Rules / Commands / Skills 做好上下文管理。
一、从 LLM 到 Agent:缺了什么,补了什么
从最早的 GPT-3.5、Claude、DeepSeek-R1,这些大语言模型(LLM)本身就具备思考与规划的能力。
但那个阶段,它们有几个明显的局限:
| 问题 | 本质原因 |
|---|---|
| 几天前的对话记不住 | 缺少 Memory |
| 给它一个链接只能给建议,不能实际处理 | 缺少 Tools |
| 无法获取最新信息 | 知识有训练截止日期 |
| 不能基于私有文档稳定回答 | 缺少 RAG / 私有知识上下文 |
后来,Copilot、Cursor 等 AI IDE 出现,带来了 Agent 模式, 本质上就是在 LLM 外再补几层能力:
- Tools:读写文件、执行命令、调用浏览器、访问远端服务
- Memory:保留任务状态与历史上下文
- RAG:接入项目文档、私有知识库
- Loop:模型可以反复“思考 -> 调工具 -> 拿结果 -> 再思考”
可以直接把 Agent 理解为:
Agent = LLM + Tools + Memory + RAG + 可迭代执行循环
二、用一段代码理解 Agent 的最小闭环
拿 LangChain.js 举例,创建一个 Agent 的核心结构非常清晰:
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { HumanMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
import { z } from "zod";
import fs from "node:fs/promises";
// 1. 初始化大语言模型
const model = new ChatOpenAI({
model: "gpt-5",
temperature: 0,
});
// 2. 定义工具:读取文件内容
const readFileTool = tool(
async ({ filePath }) => {
const content = await fs.readFile(filePath, "utf-8");
return `文件内容:\n${content}`;
},
{
name: "read_file",
description: "读取文件内容,输入文件路径(相对或绝对路径均可)",
schema: z.object({
filePath: z.string().describe("要读取的文件路径"),
}),
}
);
// 3. 将工具绑定到模型,Agent 可自主决定是否调用
const modelWithTools = model.bindTools([readFileTool]);
// 4. 发起对话,模型自动决策是否调用工具
const messages = [
new SystemMessage("你是一个代码助手,可以使用工具读取并解释文件内容。"),
new HumanMessage("请读取 ./index.ts 并解释这段代码"),
];
let response = await modelWithTools.invoke(messages);
messages.push(response);
// 5. 循环处理工具调用,直到模型不再需要工具
while (response.tool_calls?.length > 0) {
for (const toolCall of response.tool_calls) {
const result = await readFileTool.invoke(toolCall.args);
messages.push(
new ToolMessage({ content: result, tool_call_id: toolCall.id })
);
}
// 将工具结果传回模型,继续推理
response = await modelWithTools.invoke(messages);
}
console.log(response.content); // 最终回复
关键理解:model 是大脑,tools 是手脚。bindTools 并不是强制模型调用工具,而是告知模型"有这些能力可用",由模型自主决策是否调用。
工具调用的完整闭环是:模型返回 tool_calls → 宿主代码执行对应工具 → 将结果以 ToolMessage 写回消息历史 → 模型再次推理,直到不再需要工具为止。 这个循环,就是 Agent "自主迭代"能力的核心。
三、MCP:让工具调用从“私有接线”变成“标准协议”
在 MCP 出现之前,模型调用外部能力主要依赖 Function Calling。
问题在于:不同模型厂商的接入方式不同,工具很难复用,IDE 与 Agent 之间也缺少统一标准。
MCP(Model Context Protocol) 解决的就是这个问题。
可以把它理解为:
MCP 是一套让 Agent 发现、调用外部工具的标准协议。
它和 Function Calling 做的是同一类事,但更标准化、更可复用、也更适合跨工具链协作。
MCP 的两种常见模式
MCP 支持两种通信方式,适用场景不同:
| 模式 | 通信方式 | 典型场景 |
|---|---|---|
| Stdio(标准输入输出) | 本地进程间通信 | 本地文件操作、命令执行、浏览器桥接 |
| HTTP(远程服务) | 网络请求 | 云端 API、跨机器调用,如 Context7 |
对应配置示例:
{
"mcpServers": {
// Stdio 模式:启动一个本地 Node.js 子进程
"browsermcp": {
"command": "npx",
"args": ["@browsermcp/mcp@latest"]
},
// HTTP 模式:直连远端服务
"context7": {
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "xxxxxxx"
}
}
}
}
Cursor 启动时做了什么
当 Cursor 启动时,会读取本地 MCP 配置文件,并立即将每个 Stdio 类型的 MCP 作为一个子进程启动。以 browsermcp 为例,实际上是在后台执行了:
npx @browsermcp/mcp@latest
npx node /Users/streaker303/.nvm/versions/node/v22.17.0/lib/node_modules/mcp-chrome-bridge/dist/mcp/mcp-server-stdio.js
这个 Node.js 进程持续运行,通过 stdin/stdout 与 Cursor 保持通信。Cursor(作为 MCP Client)发送工具调用请求,MCP Server 处理后将结果写回 stdout,再返回给大模型。
下图以
mcp-chrome-bridge这类"浏览器桥接型" MCP 为例。 它的特殊之处在于:不创建新浏览器实例,而是复用当前正在使用的浏览器及其登录状态,通过 Chrome Extension 作为中间桥接层传递消息。
┌──────────┐ stdio ┌──────────────────────┐ HTTP MCP ┌────────────────────────────┐ Native Messaging ┌────────────────┐ Chrome API ┌─────────────┐
│ │ ────────► │ │ ───────────► │ │ ───────────────────► │ │ ─────────────► │ │
│ Agent │ │ mcp-server-stdio.js │ │ Native Host + HTTP Server │ │ Chrome Ext │ │ Chrome Tab │
│ / IDE │ ◄──────── │ (stdio adapter) │ ◄─────────── │ (127.0.0.1:12306) │ ◄─────────────────── │ (插件后台) │ ◄───────────── │ / 页面 │
└──────────┘ └──────────────────────┘ └────────────────────────────┘ └────────────────┘ └─────────────┘
sequenceDiagram
participant U as User
participant A as Agent IDE
participant S as mcp-server-stdio.js
participant H as 127.0.0.1:12306 MCP Server
participant N as Native Host
participant E as Chrome Extension
participant B as Browser Tab/Page
U->>A: 使用 MCP 打开链接 / 获取页面内容
A->>S: tools/call(stdio)
S->>H: POST /mcp(HTTP)
H->>N: 调用工具
N->>E: Native Messaging 请求
E->>B: 调用 Chrome API / 注入脚本
B-->>E: 返回执行结果
E-->>N: Native Messaging 响应
N-->>H: 工具结果
H-->>S: MCP 响应(HTTP)
S-->>A: tools/call 响应(stdio)
四、为什么 Agent 工程化一定会遇到“上下文污染”
Agent 虽强,但上下文窗口始终有限。
如果把所有规范、组件文档、脚本说明、团队约定全部直接塞进 Prompt,很快就会出现三个问题:
- 信噪比下降:真正关键的信息被淹没
- 推理效率下降:Token 增加,响应变慢
- 幻觉风险上升:上下文太杂,模型更容易误判
因此,AI IDE 不会把所有信息都做成“永远注入”。
更合理的做法是分层管理上下文,这也是 Rules / Commands / Skills 存在的原因。
五、Rules、Commands、Skills:三种上下文机制怎么分工
| 机制 | 触发方式 | 适合放什么 | 上下文成本 | 常见误用 |
|---|---|---|---|---|
| Rules | 宿主强制注入 | 项目红线、技术栈、绝对约束 | 始终消耗 | 把长篇教程塞进去 |
| Commands | 用户显式触发 | 高频、稳定、可命名的工作流入口 | 调用时消耗 | 命令越拆越多 |
| Skills | Agent 按需匹配 | 可复用的工程策略、SOP、领域能力 | 命中时渐进加载 | 做成“伪全局规则” |
1. Rules
Rules 更像系统级 Prompt,适合放:
- 项目技术栈
- 代码红线
- 输出语言
- 不能违反的工程约束
不适合放:
- 长篇开发教程
- 低频场景说明
- 大段外部文档
2. Commands
Commands 是用户主动触发的入口,适合放:
- 重复性强的工作流
- 希望开发者显式启动的流程
- 需要固定输入输出边界的任务
例如:
- /code-review
- /refactor
- /opsx-apply
3. Skills
Skills 适合承载复杂策略,而不是做成“另一个命令系统”。
它的价值有两个:
- 可复用:同一个能力可以被不同任务复用
- 渐进加载:先匹配,再按需展开,不会一上来污染上下文
你可以把 Skill 理解成:
面向 Agent 的工程能力模块。
六、以 OpenSpec 为例:Command 和 Skill 应该怎么拆
这是最容易设计失衡的部分。
如果 Command 设计得太多
很容易演变成:
- quick-feat
- feature-design
- doc-driven-feat
- ui-change-design
问题是:
- 难记
- 边界会重叠
- 命令列表持续膨胀
- 维护成本越来越高
如果 Command 设计得太少,但又太厚
又会变成一个巨型流程脚本,在里面塞满判断:
- 有没有接口文档
- 有没有需求文档
- 是小改动还是大改动
- 是否涉及 UI 风格读取
- 是否要先做 explore
- 是否要调用 MCP 查版本
结果是 Command 自己承担了过多复杂度,也变得脆弱。
更合理的拆法
原则很简单:
Command 管入口,Skill 管判断,MCP 查事实。
以 OpenSpec 为例,适合做成 Command 的,是这些稳定、可命名的用户意图:
- new
- continue
- apply
- verify
- archive
它们解决的是: “我要做什么” 。
而适合沉淀到 Skill 的,是这些依赖上下文判断的逻辑:
- 是否先读取 wiki / 钉钉 / 需求文档
- 是轻量变更,还是需要完整 proposal / spec / design / tasks
- 是否涉及 UI 调整,需要先读取现有页面风格
- 是否遇到版本/API 分歧,需要调用 MCP
- 当前更适合 explore,还是直接 implement
它们解决的是: “具体该怎么做” 。
七、一个更稳的实践模式
可以把这一套总结成下面的分层:
- OpenSpec Command:提供主流程入口
- 文档类 Skills:负责读取 wiki、钉钉、接口文档、需求文档
- 工程类 Skills:负责设计深度判断、UI 风格分析、任务拆解
- MCP:在版本、API、外部事实不确定时提供权威信息
这套组合有几个直接好处:
- 开发者只需要记住少量命令
- 团队规范集中沉淀在 Skills
- 复杂判断不会散落在多个命令中重复维护
- 框架升级或流程调整时,优先改 Skill,而不是重写一堆 Command
- 模型保留必要的自主判断空间,不会被过硬的流程绑死
核心不是“把流程写得越细越好”,而是:
固定关键节点,不固定模型的推理路径。
八、结论
如果只保留一句实践结论,可以是下面这句:
Rules 放红线,Commands 放入口,Skills 放策略,MCP 负责查外部事实。
对应到 OpenSpec 这类场景,就是:
- 少量 Command 保持稳定、好记
- 厚 Skill 吸收复杂判断和工程策略
- MCP 只在需要权威依据时介入
这比“为每个场景发明一个命令”更稳,也比“把所有复杂度都塞进一个命令”更可维护。
太固化的流程,可能会限制大模型的输出。(比如 Anthropic 成员采访中说的,todolist 设计反而会限制模型,要相信大模型的能力)
真正限制模型效果的,往往不是“有没有计划”,而是:计划写得过死,边界又写得不清。