一文吃透AI Agent、Skills与MCP:从概念到实战,不再傻傻分不清

在这里插入图片描述

前言

如果你最近在关注AI开发领域,一定被这三个词频繁刷屏:AgentSkillsMCP。它们看起来都跟“让AI做更多事”有关,但彼此之间到底是什么关系?什么时候该用哪个?为什么有了Agent还需要MCP?

今天这篇文章,我会用最直白的语言 + 真实的代码案例 + 清晰的流程图,一次性把这三大概念的本质、关系、实战应用讲清楚。无论你是AI应用开发者,还是对技术原理感兴趣的学习者,相信都能有所收获。


一、概念定位:一张图看懂三者关系

在深入细节之前,我们先建立整体认知。用餐厅经营来类比:

概念餐厅类比核心职责
Agent(智能体)餐厅经理理解目标、制定计划、调度资源、决策执行
Skills(技能)厨师、服务员具体完成某一类任务的能力单元
MCP(模型上下文协议)标准化接口(如餐具规格、点餐单格式)让不同工具能被统一调用,降低集成成本

一句话总结三者关系:

Agent是大脑,负责思考和决策;Skills是手脚,负责具体执行;MCP是神经网络,让手脚能被大脑标准化地指挥。

下面是更精确的架构关系图:

MCP 协议层

Agent 核心

技能层 Skills

内置技能
代码内置

文件技能
读写文件

搜索技能
网络检索

用户输入

AI Agent 智能体

规划模块
任务分解

记忆模块
上下文/历史

决策模块
选择下一步

MCP Server
工具注册/调用

天气API

数据库

第三方服务

执行结果

最终输出


二、Agent(智能体):有目标、能行动的AI

2.1 什么是Agent?

传统AI:你问一句,它答一句。没有记忆,不会主动做事。

AI Agent:你给它一个目标,它会自己思考“怎么达成”,然后调用工具、采取行动,并在过程中根据反馈调整策略,直到完成目标。

Agent的核心特征:

  • ✅ 自主性(Autonomy):无需人类每步指导
  • ✅ 反应性(Reactivity):能感知环境变化并响应
  • ✅ 主动性(Proactiveness):为达成目标主动采取行动
  • ✅ 社交性(Social ability):能与其他Agent或人类协作

2.2 Agent的工作原理(ReAct模式)

当前最主流的Agent架构是ReAct模式(Reasoning + Acting),即交替进行“推理”和“行动”。

工具/API Agent 用户 工具/API Agent 用户 loop [ReAct循环] 目标:帮我查北京的天气, 如果下雨就提醒带伞 Thought: 需要先获取天气数据 Action: 调用天气API(北京) Observation: 北京,中雨,22°C Thought: 天气是雨天,需要提醒带伞 最终回答:北京今天中雨,出门记得带伞!

2.3 手写一个极简Agent(Python实现)

下面的代码实现了一个具备思考-行动-观察循环的基础Agent,可以调用多个工具:

import json
from typing import Dict, List, Callable, Any
from datetime import datetime

class SimpleAgent:
    """
    极简AI Agent实现
    核心:ReAct循环(Reasoning + Acting)
    """
    
    def __init__(self, name: str, system_prompt: str = None):
        self.name = name
        self.memory = []  # 对话记忆
        self.tools: Dict[str, Callable] = {}  # 注册的工具
        self.max_iterations = 5  # 最大循环次数
        
        self.system_prompt = system_prompt or "你是一个能调用工具的智能助手。"
    
    def register_tool(self, name: str, func: Callable, description: str):
        """注册工具(Skill)"""
        self.tools[name] = {
            "func": func,
            "description": description
        }
        print(f"[Agent] 已注册工具: {name}")
    
    def _think(self, user_input: str) -> Dict:
        """
        思考阶段:判断是否需要调用工具
        实际生产中这里会调用LLM,我们简化模拟
        """
        # 模拟LLM的推理结果
        think_prompt = f"""
        用户需求: {user_input}
        可用工具: {list(self.tools.keys())}
        
        请判断:
        1. 是否需要调用工具?(是/否)
        2. 如果需要,调用哪个工具?参数是什么?
        """
        
        # 这里是模拟逻辑,真实情况会调用LLM API
        if "天气" in user_input:
            return {"need_tool": True, "tool": "get_weather", "args": {"city": "北京"}}
        elif "时间" in user_input:
            return {"need_tool": True, "tool": "get_current_time", "args": {}}
        else:
            return {"need_tool": False, "response": f"[{self.name}] 收到: {user_input}"}
    
    def _act(self, tool_name: str, args: Dict) -> Any:
        """行动阶段:执行工具调用"""
        if tool_name not in self.tools:
            return f"错误:未找到工具 {tool_name}"
        
        try:
            result = self.tools[tool_name]["func"](**args)
            return result
        except Exception as e:
            return f"执行出错: {str(e)}"
    
    def _observe(self, action_result: Any) -> str:
        """观察阶段:解释工具返回结果"""
        return f"观察到结果: {action_result}"
    
    def run(self, user_input: str) -> str:
        """
        Agent主循环:ReAct模式
        """
        print(f"\n{'='*50}")
        print(f"用户: {user_input}")
        print(f"{'='*50}")
        
        # 循环执行ReAct
        for i in range(self.max_iterations):
            print(f"\n--- 循环 {i+1} ---")
            
            # Step 1: 思考 (Reasoning)
            thought = self._think(user_input)
            
            if not thought.get("need_tool", False):
                # 不需要工具,直接返回
                return thought.get("response", "抱歉,我无法处理这个请求")
            
            # Step 2: 行动 (Acting)
            print(f"思考结果: 需要调用 {thought['tool']}")
            action_result = self._act(thought["tool"], thought["args"])
            print(f"行动结果: {action_result}")
            
            # Step 3: 观察 (Observing)
            observation = self._observe(action_result)
            print(f"观察: {observation}")
            
            # Step 4: 判断是否完成
            if isinstance(action_result, str) and not action_result.startswith("错误"):
                return f"最终回答: {action_result}"
        
        return "执行超时,请重试"


# ============ 定义工具(Skills) ============

def get_weather(city: str) -> str:
    """模拟天气查询工具"""
    weather_db = {
        "北京": "晴,25°C,空气质量良",
        "上海": "多云,28°C,湿度65%",
        "广州": "雷阵雨,32°C,注意带伞"
    }
    return weather_db.get(city, f"{city}的天气数据暂不可用")

def get_current_time() -> str:
    """获取当前时间"""
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

def search_web(query: str) -> str:
    """模拟网络搜索"""
    return f"关于'{query}'的搜索结果: 这是模拟的搜索结果..."


# ============ 使用示例 ============

if __name__ == "__main__":
    # 创建Agent
    agent = SimpleAgent(name="小智")
    
    # 注册技能
    agent.register_tool("get_weather", get_weather, "查询指定城市的天气")
    agent.register_tool("get_current_time", get_current_time, "获取当前时间")
    agent.register_tool("search_web", search_web, "搜索网络信息")
    
    # 测试对话
    responses = []
    for query in ["北京天气怎么样?", "现在几点钟?"]:
        result = agent.run(query)
        responses.append(result)
        print(f"\n👉 {result}")

2.4 主流Agent框架对比

框架特点适用场景学习曲线
LangChain功能最全,生态最丰富复杂Agent、生产级应用陡峭
AutoGen多Agent协作,微软出品需要多个Agent对话的任务中等
CrewAI角色扮演式协作任务编排、工作流自动化平缓
Semantic Kernel轻量,微软风格.NET生态、企业应用中等
LlamaIndex专注RAG文档问答类Agent中等

三、Skills(技能):Agent的“能力单元”

3.1 什么是Skills?

Skills是Agent可以执行的具体能力单元。可以理解为Agent的“肌肉记忆”——完成某一类特定任务的函数或API调用。

典型Skill示例:

  • read_file(path):读取文件
  • send_email(to, subject, body):发送邮件
  • calculate(expression):数学计算
  • search_web(query):网络搜索
  • sql_query(sql):数据库查询

3.2 Skills的设计原则

一个好的Skill设计应该遵循以下原则:

Skills设计五原则

单一职责
一个Skill只做一件事

输入输出明确
类型清晰

可组合
多个Skill可串联

有错误处理
失败有兜底

自描述
有清晰的docstring

3.3 实战:构建一个“文件管理Skill包”

import os
import json
import hashlib
from pathlib import Path
from typing import List, Dict, Any, Optional
from datetime import datetime

class FileSystemSkills:
    """
    文件系统技能包
    为Agent提供文件操作能力
    """
    
    def __init__(self, workspace_root: str = "./agent_workspace"):
        self.workspace_root = Path(workspace_root)
        self.workspace_root.mkdir(exist_ok=True)
        
        # 技能注册表
        self.skills = {
            "list_files": self.list_files,
            "read_file": self.read_file,
            "write_file": self.write_file,
            "delete_file": self.delete_file,
            "search_in_files": self.search_in_files,
            "get_file_info": self.get_file_info
        }
    
    def get_skills_description(self) -> List[Dict]:
        """返回所有技能的描述,供Agent理解"""
        return [
            {
                "name": "list_files",
                "description": "列出指定目录下的所有文件",
                "parameters": {
                    "path": "目录路径(相对于工作区)",
                    "recursive": "是否递归列出子目录(默认False)"
                }
            },
            {
                "name": "read_file",
                "description": "读取文件内容",
                "parameters": {"path": "文件路径"}
            },
            {
                "name": "write_file",
                "description": "写入内容到文件",
                "parameters": {"path": "文件路径", "content": "文件内容"}
            },
            {
                "name": "search_in_files",
                "description": "在文件中搜索关键词",
                "parameters": {"keyword": "搜索关键词", "file_pattern": "文件匹配模式,如*.txt"}
            }
        ]
    
    def list_files(self, path: str = ".", recursive: bool = False) -> List[str]:
        """技能1: 列出文件"""
        target_path = self.workspace_root / path
        if not target_path.exists():
            return [f"错误:路径不存在 {path}"]
        
        if recursive:
            result = [str(p.relative_to(self.workspace_root)) 
                     for p in target_path.rglob("*") if p.is_file()]
        else:
            result = [p.name for p in target_path.iterdir() if p.is_file()]
        
        return result
    
    def read_file(self, path: str) -> str:
        """技能2: 读取文件"""
        file_path = self.workspace_root / path
        if not file_path.exists():
            return f"错误:文件不存在 {path}"
        
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                return f.read()
        except Exception as e:
            return f"读取失败:{str(e)}"
    
    def write_file(self, path: str, content: str) -> Dict:
        """技能3: 写入文件"""
        file_path = self.workspace_root / path
        file_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(content)
            return {
                "success": True,
                "path": str(file_path),
                "size": len(content),
                "message": f"成功写入 {len(content)} 字节"
            }
        except Exception as e:
            return {"success": False, "error": str(e)}
    
    def delete_file(self, path: str) -> Dict:
        """技能4: 删除文件"""
        file_path = self.workspace_root / path
        try:
            if file_path.is_file():
                file_path.unlink()
                return {"success": True, "message": f"已删除 {path}"}
            elif file_path.is_dir():
                file_path.rmdir()
                return {"success": True, "message": f"已删除目录 {path}"}
            else:
                return {"success": False, "error": f"路径不存在 {path}"}
        except Exception as e:
            return {"success": False, "error": str(e)}
    
    def search_in_files(self, keyword: str, file_pattern: str = "*.txt") -> List[Dict]:
        """技能5: 搜索文件内容"""
        results = []
        for file_path in self.workspace_root.rglob(file_pattern):
            try:
                content = file_path.read_text(encoding='utf-8')
                if keyword.lower() in content.lower():
                    # 找到匹配行
                    lines = content.split('\n')
                    matches = [(i+1, line.strip()) for i, line in enumerate(lines) 
                              if keyword.lower() in line.lower()]
                    results.append({
                        "file": str(file_path.relative_to(self.workspace_root)),
                        "matches": matches[:3]  # 只返回前3个匹配
                    })
            except:
                continue
        
        return results
    
    def get_file_info(self, path: str) -> Dict:
        """技能6: 获取文件元信息"""
        file_path = self.workspace_root / path
        if not file_path.exists():
            return {"error": "文件不存在"}
        
        stat = file_path.stat()
        return {
            "name": file_path.name,
            "size": stat.st_size,
            "created": datetime.fromtimestamp(stat.st_ctime).isoformat(),
            "modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
            "is_file": file_path.is_file(),
            "hash": self._compute_hash(file_path) if file_path.is_file() else None
        }
    
    def _compute_hash(self, file_path: Path) -> str:
        """计算文件MD5"""
        return hashlib.md5(file_path.read_bytes()).hexdigest()[:8]


# 演示Skills如何被Agent调用
def demo_skills():
    fs = FileSystemSkills()
    
    # 模拟Agent调用多个Skill完成任务
    print("=== Agent正在使用Skills完成任务 ===\n")
    
    # 任务1: 创建笔记文件
    print("📝 任务1: 创建今日笔记")
    result = fs.write_file("notes/2024-01-15.txt", 
                          "今日学习: Agent、Skills、MCP的概念\n记录时间: 2024-01-15")
    print(f"   {result['message']}")
    
    # 任务2: 列出所有文件
    print("\n📂 任务2: 列出工作区文件")
    files = fs.list_files(recursive=True)
    for f in files:
        print(f"   - {f}")
    
    # 任务3: 搜索内容
    print("\n🔍 任务3: 搜索关键词 'Agent'")
    results = fs.search_in_files("Agent")
    for r in results:
        print(f"   在 {r['file']} 中找到:")
        for line_num, line in r['matches']:
            print(f"     第{line_num}行: {line}")

if __name__ == "__main__":
    demo_skills()

3.4 Skills与Function Calling的关系

很多人混淆Skills和Function Calling,这里厘清一下:

维度Function CallingSkills
定义方LLM厂商定义的标准格式开发者自定义的能力单元
粒度细粒度,单个API调用可粗可细,可组合
执行者LLM本身或外部代码Agent框架负责调度
关系可以是Skill的一种实现方式Skill是更高层的抽象

四、MCP(模型上下文协议):让工具调用的“通用语言”

4.1 为什么需要MCP?

在MCP出现之前,每接入一个工具,都要写大量的适配代码:

# 没有MCP时:每个工具都要单独写适配器
class WeatherTool:
    def call(self, city):
        return requests.get(f"https://api.weather.com/{city}")
    
    def to_function_call_format(self):
        # 为不同LLM适配格式...
        pass

class DatabaseTool:
    def call(self, sql):
        return db.query(sql)
    
    def to_function_call_format(self):
        # 又是不同的格式...
        pass

# 每加一个新工具,就要写一遍胶水代码 ❌

MCP的目标:定义一套统一的协议,让任何工具都能被标准化的方式调用,实现即插即用。

4.2 MCP核心架构

MCP采用客户端-服务器架构:

后端服务

MCP服务器

MCP客户端

工具注册表

JSON-RPC

stdio/HTTP/WebSocket

AI应用 / Agent

MCP Client SDK

MCP协议层

路由分发

工具A

工具B

工具C

REST API

数据库

文件系统

4.3 MCP协议详解

MCP使用JSON-RPC作为消息格式,定义了三类核心消息:

1. 初始化消息
// 客户端 → 服务器:握手
{
  "jsonrpc": "2.0",
  "method": "initialize",
  "params": {
    "protocolVersion": "0.1.0",
    "clientInfo": {
      "name": "my-agent",
      "version": "1.0.0"
    }
  },
  "id": 1
}

// 服务器 → 客户端:返回能力
{
  "jsonrpc": "2.0",
  "result": {
    "protocolVersion": "0.1.0",
    "capabilities": {
      "tools": true,
      "resources": true,
      "prompts": true
    },
    "serverInfo": {
      "name": "weather-mcp-server",
      "version": "1.0.0"
    }
  },
  "id": 1
}
2. 工具调用消息
// 客户端 → 服务器:列出可用工具
{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": 2
}

// 服务器 → 客户端:返回工具列表
{
  "jsonrpc": "2.0",
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "description": "获取城市天气",
        "inputSchema": {
          "type": "object",
          "properties": {
            "city": {"type": "string"},
            "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
          },
          "required": ["city"]
        }
      }
    ]
  },
  "id": 2
}

// 客户端 → 服务器:调用工具
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {"city": "北京", "unit": "celsius"}
  },
  "id": 3
}
3. 资源访问消息
// MCP还支持资源(文件、数据库表等)的标准化访问
{
  "jsonrpc": "2.0",
  "method": "resources/read",
  "params": {
    "uri": "file:///data/config.json"
  },
  "id": 4
}

4.4 从零实现一个MCP Server(Python)

# mcp_server_demo.py
import json
import sys
import asyncio
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, asdict
from datetime import datetime

@dataclass
class MCPRequest:
    """MCP请求格式"""
    jsonrpc: str = "2.0"
    method: str = ""
    params: Dict = None
    id: int = None

@dataclass
class MCPResponse:
    """MCP响应格式"""
    jsonrpc: str = "2.0"
    result: Any = None
    error: Optional[Dict] = None
    id: int = None

class MCPServer:
    """
    MCP服务器实现
    遵循Model Context Protocol规范
    """
    
    def __init__(self, name: str = "mcp-demo-server", version: str = "1.0.0"):
        self.name = name
        self.version = version
        self.tools: Dict[str, Dict] = {}  # 注册的工具
        self.resources: Dict[str, Dict] = {}  # 注册的资源
        self.request_counter = 0
    
    def register_tool(self, name: str, handler, description: str, 
                     input_schema: Dict = None):
        """注册工具到MCP服务器"""
        self.tools[name] = {
            "handler": handler,
            "description": description,
            "inputSchema": input_schema or {
                "type": "object",
                "properties": {},
                "required": []
            }
        }
        print(f"[MCP Server] 注册工具: {name}")
    
    def register_resource(self, uri: str, handler, mime_type: str = "text/plain"):
        """注册资源(文件、数据等)"""
        self.resources[uri] = {
            "handler": handler,
            "mimeType": mime_type
        }
        print(f"[MCP Server] 注册资源: {uri}")
    
    def handle_request(self, request_dict: Dict) -> MCPResponse:
        """处理MCP请求"""
        request = MCPRequest(**request_dict)
        
        # 初始化握手
        if request.method == "initialize":
            return self._handle_initialize(request)
        
        # 列出工具
        elif request.method == "tools/list":
            return self._handle_tools_list(request)
        
        # 调用工具
        elif request.method == "tools/call":
            return self._handle_tools_call(request)
        
        # 列出资源
        elif request.method == "resources/list":
            return self._handle_resources_list(request)
        
        # 读取资源
        elif request.method == "resources/read":
            return self._handle_resources_read(request)
        
        else:
            return MCPResponse(
                id=request.id,
                error={"code": -32601, "message": f"Method not found: {request.method}"}
            )
    
    def _handle_initialize(self, request: MCPRequest) -> MCPResponse:
        """处理初始化请求"""
        return MCPResponse(
            id=request.id,
            result={
                "protocolVersion": "0.1.0",
                "capabilities": {
                    "tools": len(self.tools) > 0,
                    "resources": len(self.resources) > 0
                },
                "serverInfo": {
                    "name": self.name,
                    "version": self.version
                }
            }
        )
    
    def _handle_tools_list(self, request: MCPRequest) -> MCPResponse:
        """返回所有可用工具"""
        tools_list = [
            {
                "name": name,
                "description": info["description"],
                "inputSchema": info["inputSchema"]
            }
            for name, info in self.tools.items()
        ]
        return MCPResponse(id=request.id, result={"tools": tools_list})
    
    def _handle_tools_call(self, request: MCPRequest) -> MCPResponse:
        """执行工具调用"""
        tool_name = request.params.get("name")
        arguments = request.params.get("arguments", {})
        
        if tool_name not in self.tools:
            return MCPResponse(
                id=request.id,
                error={"code": -32001, "message": f"Tool not found: {tool_name}"}
            )
        
        try:
            result = self.tools[tool_name]["handler"](**arguments)
            return MCPResponse(id=request.id, result={"content": result})
        except Exception as e:
            return MCPResponse(
                id=request.id,
                error={"code": -32000, "message": str(e)}
            )
    
    def _handle_resources_list(self, request: MCPRequest) -> MCPResponse:
        """列出所有资源"""
        resources_list = [
            {"uri": uri, "mimeType": info["mimeType"]}
            for uri, info in self.resources.items()
        ]
        return MCPResponse(id=request.id, result={"resources": resources_list})
    
    def _handle_resources_read(self, request: MCPRequest) -> MCPResponse:
        """读取资源"""
        uri = request.params.get("uri")
        if uri not in self.resources:
            return MCPResponse(
                id=request.id,
                error={"code": -32002, "message": f"Resource not found: {uri}"}
            )
        
        try:
            content = self.resources[uri]["handler"](uri)
            return MCPResponse(
                id=request.id,
                result={"contents": [{"uri": uri, "text": content}]}
            )
        except Exception as e:
            return MCPResponse(
                id=request.id,
                error={"code": -32000, "message": str(e)}
            )
    
    def run_stdio(self):
        """通过标准输入输出运行(最常用模式)"""
        print(f"[MCP Server] {self.name} v{self.version} 已启动", file=sys.stderr)
        
        for line in sys.stdin:
            line = line.strip()
            if not line:
                continue
            
            try:
                request = json.loads(line)
                response = self.handle_request(request)
                print(json.dumps(asdict(response), ensure_ascii=False))
                sys.stdout.flush()
            except json.JSONDecodeError:
                continue
            except Exception as e:
                error_response = MCPResponse(
                    id=None,
                    error={"code": -32603, "message": str(e)}
                )
                print(json.dumps(asdict(error_response)))


# ============ 实现具体的工具 ============

# 天气查询工具
def get_weather(city: str, unit: str = "celsius") -> Dict:
    """模拟天气API"""
    weather_data = {
        "北京": {"temp": 25, "condition": "晴", "humidity": 45},
        "上海": {"temp": 28, "condition": "多云", "humidity": 65},
        "深圳": {"temp": 32, "condition": "雷阵雨", "humidity": 80}
    }
    
    data = weather_data.get(city, {"temp": 22, "condition": "未知", "humidity": 50})
    
    if unit == "fahrenheit":
        data["temp"] = data["temp"] * 9/5 + 32
    
    return {
        "city": city,
        "temperature": data["temp"],
        "unit": unit,
        "condition": data["condition"],
        "humidity": data["humidity"],
        "timestamp": datetime.now().isoformat()
    }

# 计算器工具
def calculate(expression: str) -> Dict:
    """安全计算数学表达式"""
    allowed_names = {
        "abs": abs, "round": round, "min": min, "max": max,
        "pow": pow, "sum": sum
    }
    try:
        result = eval(expression, {"__builtins__": {}}, allowed_names)
        return {"expression": expression, "result": result}
    except Exception as e:
        return {"error": str(e)}

# 资源读取:配置文件
def read_config(uri: str) -> str:
    """模拟读取配置"""
    configs = {
        "file:///config/app.json": '{"name": "MyApp", "version": "1.0.0"}',
        "file:///config/db.json": '{"host": "localhost", "port": 5432}'
    }
    return configs.get(uri, "{}")


# 主程序
if __name__ == "__main__":
    # 创建MCP服务器
    server = MCPServer("demo-mcp-server", "1.0.0")
    
    # 注册工具
    server.register_tool(
        "get_weather", get_weather,
        "获取指定城市的天气信息",
        {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名称"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    )
    
    server.register_tool(
        "calculate", calculate,
        "计算数学表达式",
        {
            "type": "object",
            "properties": {
                "expression": {"type": "string", "description": "数学表达式"}
            },
            "required": ["expression"]
        }
    )
    
    # 注册资源
    server.register_resource("file:///config/app.json", read_config, "application/json")
    
    # 启动服务器(等待stdin输入)
    server.run_stdio()

4.5 MCP客户端实现(与Server配合)

# mcp_client_demo.py
import json
import subprocess
import asyncio
from typing import Dict, Any, List

class MCPClient:
    """
    MCP客户端
    用于连接MCP Server并调用工具
    """
    
    def __init__(self, server_command: List[str]):
        self.server_command = server_command
        self.process = None
        self.request_id = 0
    
    def start(self):
        """启动MCP Server子进程"""
        self.process = subprocess.Popen(
            self.server_command,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        print(f"[MCP Client] 已连接到服务器")
    
    def send_request(self, method: str, params: Dict = None) -> Dict:
        """发送JSON-RPC请求"""
        self.request_id += 1
        request = {
            "jsonrpc": "2.0",
            "method": method,
            "params": params or {},
            "id": self.request_id
        }
        
        # 发送请求
        self.process.stdin.write(json.dumps(request) + "\n")
        self.process.stdin.flush()
        
        # 读取响应
        response_line = self.process.stdout.readline()
        return json.loads(response_line)
    
    def initialize(self) -> Dict:
        """握手初始化"""
        return self.send_request("initialize", {
            "protocolVersion": "0.1.0",
            "clientInfo": {"name": "demo-client", "version": "1.0.0"}
        })
    
    def list_tools(self) -> List[Dict]:
        """获取可用工具列表"""
        response = self.send_request("tools/list")
        return response.get("result", {}).get("tools", [])
    
    def call_tool(self, name: str, arguments: Dict) -> Any:
        """调用工具"""
        response = self.send_request("tools/call", {
            "name": name,
            "arguments": arguments
        })
        return response.get("result", {}).get("content")
    
    def close(self):
        """关闭连接"""
        if self.process:
            self.process.terminate()


# 演示MCP完整流程
def demo_mcp():
    print("="*60)
    print("MCP 完整流程演示")
    print("="*60)
    
    # 1. 启动MCP Server(在另一个进程)
    client = MCPClient(["python", "mcp_server_demo.py"])
    client.start()
    
    # 2. 初始化握手
    print("\n📡 Step 1: 初始化握手")
    init_resp = client.initialize()
    print(f"   服务器能力: {init_resp.get('result', {}).get('capabilities')}")
    
    # 3. 列出可用工具
    print("\n🔧 Step 2: 列出可用工具")
    tools = client.list_tools()
    for tool in tools:
        print(f"   - {tool['name']}: {tool['description']}")
    
    # 4. 调用天气工具
    print("\n🌤️ Step 3: 调用工具 'get_weather'")
    result = client.call_tool("get_weather", {"city": "北京", "unit": "celsius"})
    print(f"   结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
    
    # 5. 调用计算器
    print("\n🧮 Step 4: 调用工具 'calculate'")
    result = client.call_tool("calculate", {"expression": "(3 + 5) * 2"})
    print(f"   结果: {result}")
    
    # 清理
    client.close()
    print("\n✅ MCP演示完成")

if __name__ == "__main__":
    demo_mcp()

五、三者协同实战:构建一个智能文件助手

下面我们把三者结合起来,构建一个完整的应用:

"""
完整实战:基于Agent + Skills + MCP的智能文件助手
- Agent负责理解和规划
- Skills提供文件操作能力
- MCP作为标准化工具调用协议
"""

import os
import json
from typing import Dict, Any, List, Optional
from dataclasses import dataclass
from enum import Enum

# ============ 1. MCP层:标准化工具接口 ============

class MCPToolServer:
    """简化版MCP Server"""
    
    def __init__(self):
        self.tools = {}
    
    def register(self, name: str, func, schema: Dict):
        self.tools[name] = {"func": func, "schema": schema}
    
    def call(self, tool_name: str, args: Dict) -> Any:
        if tool_name not in self.tools:
            raise ValueError(f"Tool {tool_name} not found")
        return self.tools[tool_name]["func"](**args)
    
    def get_schemas(self) -> List[Dict]:
        return [{"name": n, "schema": s} for n, s in self.tools.items()]

# ============ 2. Skills层:具体能力实现 ============

class FileSkills:
    """文件操作技能集"""
    
    @staticmethod
    def create_file(path: str, content: str = "") -> Dict:
        """创建文件"""
        try:
            with open(path, 'w', encoding='utf-8') as f:
                f.write(content)
            return {"success": True, "path": path, "size": len(content)}
        except Exception as e:
            return {"success": False, "error": str(e)}
    
    @staticmethod
    def read_file(path: str) -> Dict:
        """读取文件"""
        try:
            with open(path, 'r', encoding='utf-8') as f:
                content = f.read()
            return {"success": True, "content": content, "size": len(content)}
        except Exception as e:
            return {"success": False, "error": str(e)}
    
    @staticmethod
    def list_files(directory: str = ".") -> List[str]:
        """列出文件"""
        return [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]
    
    @staticmethod
    def delete_file(path: str) -> Dict:
        """删除文件"""
        try:
            os.remove(path)
            return {"success": True, "path": path}
        except Exception as e:
            return {"success": False, "error": str(e)}

# ============ 3. Agent层:智能决策与执行 ============

class SmartAgent:
    """智能Agent - 连接用户意图与Skills"""
    
    def __init__(self, mcp_server: MCPToolServer):
        self.mcp = mcp_server
        self.memory = []
        
        # 注册所有技能到MCP
        self._register_skills()
    
    def _register_skills(self):
        """将Skills注册到MCP Server"""
        skills = FileSkills()
        
        self.mcp.register(
            "create_file",
            skills.create_file,
            {"description": "创建新文件", "params": ["path", "content"]}
        )
        self.mcp.register(
            "read_file", 
            skills.read_file,
            {"description": "读取文件内容", "params": ["path"]}
        )
        self.mcp.register(
            "list_files",
            skills.list_files,
            {"description": "列出目录中的文件", "params": ["directory"]}
        )
        self.mcp.register(
            "delete_file",
            skills.delete_file,
            {"description": "删除文件", "params": ["path"]}
        )
    
    def _understand_intent(self, user_input: str) -> Dict:
        """
        理解用户意图(模拟LLM)
        真实场景会调用大模型进行意图识别和参数提取
        """
        user_input_lower = user_input.lower()
        
        if "创建" in user_input_lower or "新建" in user_input_lower:
            # 提取文件名
            import re
            path_match = re.search(r'[a-zA-Z0-9_/\\]+\.\w+', user_input)
            path = path_match.group(0) if path_match else "untitled.txt"
            
            # 提取内容(引号内)
            content_match = re.search(r'["\'](.*?)["\']', user_input)
            content = content_match.group(1) if content_match else ""
            
            return {"action": "create_file", "params": {"path": path, "content": content}}
        
        elif "读取" in user_input_lower or "查看" in user_input_lower:
            path_match = re.search(r'[a-zA-Z0-9_/\\]+\.\w+', user_input)
            path = path_match.group(0) if path_match else ""
            return {"action": "read_file", "params": {"path": path}} if path else {"action": "unknown"}
        
        elif "列出" in user_input_lower or "列表" in user_input_lower:
            return {"action": "list_files", "params": {"directory": "."}}
        
        elif "删除" in user_input_lower:
            path_match = re.search(r'[a-zA-Z0-9_/\\]+\.\w+', user_input)
            path = path_match.group(0) if path_match else ""
            return {"action": "delete_file", "params": {"path": path}} if path else {"action": "unknown"}
        
        return {"action": "unknown"}
    
    def execute(self, user_input: str) -> str:
        """Agent主执行流程"""
        print(f"\n{'='*50}")
        print(f"👤 用户: {user_input}")
        print(f"{'='*50}")
        
        # Step 1: 理解意图
        intent = self._understand_intent(user_input)
        print(f"🧠 Agent理解: {intent.get('action')}")
        
        if intent["action"] == "unknown":
            return "抱歉,我没理解你的意图。请尝试:'创建文件 test.txt','读取文件 test.txt','列出文件','删除文件 test.txt'"
        
        # Step 2: 通过MCP调用Skill
        tool_name = intent["action"]
        params = intent["params"]
        
        print(f"🔧 通过MCP调用工具: {tool_name}({params})")
        result = self.mcp.call(tool_name, params)
        
        # Step 3: 处理结果
        print(f"📦 执行结果: {result}")
        
        # 格式化输出
        if tool_name == "create_file":
            if result.get("success"):
                return f"✅ 已创建文件 {result['path']},大小 {result['size']} 字节"
            else:
                return f"❌ 创建失败: {result.get('error')}"
        
        elif tool_name == "read_file":
            if result.get("success"):
                return f"📄 文件内容:\n{result['content']}"
            else:
                return f"❌ 读取失败: {result.get('error')}"
        
        elif tool_name == "list_files":
            if result:
                return f"📁 文件列表:\n" + "\n".join(f"  - {f}" for f in result)
            else:
                return "📁 目录为空"
        
        elif tool_name == "delete_file":
            if result.get("success"):
                return f"🗑️ 已删除文件 {result['path']}"
            else:
                return f"❌ 删除失败: {result.get('error')}"
        
        return str(result)

# ============ 4. 完整演示 ============

def demo_full():
    """完整的Agent + Skills + MCP协同演示"""
    
    print("""
╔══════════════════════════════════════════════════════════════╗
║     Agent + Skills + MCP 协同工作演示                         ║
║     智能文件助手 - 展示三者如何无缝配合                          ║
╚══════════════════════════════════════════════════════════════╝
    """)
    
    # 初始化MCP服务器和Agent
    mcp = MCPToolServer()
    agent = SmartAgent(mcp)
    
    # 准备测试目录
    test_dir = "./agent_demo"
    os.makedirs(test_dir, exist_ok=True)
    os.chdir(test_dir)
    
    # 对话交互
    commands = [
        "创建一个文件 hello.txt 内容为 'Hello, Agent!'",
        "创建一个文件 note.md 内容为 '# 学习笔记\n今天学习了Agent、Skills和MCP'",
        "列出文件",
        "读取文件 hello.txt",
        "读取文件 note.md",
        "删除文件 hello.txt",
        "列出文件"
    ]
    
    for cmd in commands:
        response = agent.execute(cmd)
        print(f"🤖 Agent: {response}\n")
    
    # 清理
    os.chdir("..")
    import shutil
    shutil.rmtree(test_dir, ignore_errors=True)
    
    print("="*50)
    print("演示完成!展示了整个流程:")
    print("  用户输入 → Agent理解意图 → MCP协议调用 → Skills执行 → 返回结果")
    print("="*50)

if __name__ == "__main__":
    demo_full()

运行上述代码的输出示例:

╔══════════════════════════════════════════════════════════════╗
║     Agent + Skills + MCP 协同工作演示                         ║
╚══════════════════════════════════════════════════════════════╝

==================================================
👤 用户: 创建一个文件 hello.txt 内容为 'Hello, Agent!'
==================================================
🧠 Agent理解: create_file
🔧 通过MCP调用工具: create_file({'path': 'hello.txt', 'content': "Hello, Agent!"})
📦 执行结果: {'success': True, 'path': 'hello.txt', 'size': 14}
🤖 Agent: ✅ 已创建文件 hello.txt,大小 14 字节

==================================================
👤 用户: 列出文件
==================================================
🧠 Agent理解: list_files
🔧 通过MCP调用工具: list_files({'directory': '.'})
📦 执行结果: ['hello.txt', 'note.md']
🤖 Agent: 📁 文件列表:
  - hello.txt
  - note.md

...

六、总结与选型建议

6.1 核心要点回顾

概念一句话概括关键特征代码体现
Agent能自主决策的智能体ReAct循环、目标驱动、记忆意图识别 + 循环调用
Skills具体能力的封装单一职责、可组合、可复用函数实现 + 参数校验
MCP标准化的工具调用协议JSON-RPC、即插即用、语言无关Server/Client架构

6.2 什么时候用什么?

开始

需要AI自主决策
和规划任务?

使用Agent

只是需要
调用外部API?

需要调用
多个不同工具?

配合MCP
统一管理工具

直接使用Skills

工具数量多
需要标准化?

普通LLM调用即可

决策指南:

  • 🎯 简单场景(单次问答):直接调用LLM,不需要Agent
  • 🎯 需要调用1-3个API:用Function Calling或Skills
  • 🎯 复杂多步任务(订机票+查天气+写邮件):用Agent
  • 🎯 工具数量>5个,或需要跨语言调用:引入MCP
  • 🎯 构建可扩展的AI应用平台:三者都用,各司其职

6.3 学习路径建议

掌握LLM基础
API调用

学习Function Calling
单工具调用

构建第一个Agent
ReAct模式

设计Skills
能力拆分

引入MCP
标准化工具

完整应用
生产部署

6.4 关键启发

  1. 不要把概念神化:Agent本质上就是“LLM + 循环 + 工具”,没有黑魔法
  2. Skills是核心价值:你的业务能力最终体现在Skills上,Agent只是调度者
  3. MCP解决实际问题:当你的工具超过10个时,你会发现MCP的价值
  4. 没有银弹:不是所有应用都需要Agent,简单场景用Function Calling就够了

写在最后

AI Agent、Skills、MCP这三个概念,本质上是在解决同一个问题:如何让AI安全、可靠、高效地与外部世界交互

  • Agent回答了“谁来决策”
  • Skills回答了“能做什么”
  • MCP回答了“怎么标准化对接”

希望这篇文章能帮你彻底理清这三者的关系。代码都已实测可用,建议你动手运行一遍,体会数据流动的全过程。

如果觉得有帮助,欢迎点赞、收藏、转发!也欢迎在评论区讨论你的实践心得。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北辰alk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值