
文章目录
前言
如果你最近在关注AI开发领域,一定被这三个词频繁刷屏:Agent、Skills、MCP。它们看起来都跟“让AI做更多事”有关,但彼此之间到底是什么关系?什么时候该用哪个?为什么有了Agent还需要MCP?
今天这篇文章,我会用最直白的语言 + 真实的代码案例 + 清晰的流程图,一次性把这三大概念的本质、关系、实战应用讲清楚。无论你是AI应用开发者,还是对技术原理感兴趣的学习者,相信都能有所收获。
一、概念定位:一张图看懂三者关系
在深入细节之前,我们先建立整体认知。用餐厅经营来类比:
| 概念 | 餐厅类比 | 核心职责 |
|---|---|---|
| Agent(智能体) | 餐厅经理 | 理解目标、制定计划、调度资源、决策执行 |
| Skills(技能) | 厨师、服务员 | 具体完成某一类任务的能力单元 |
| MCP(模型上下文协议) | 标准化接口(如餐具规格、点餐单格式) | 让不同工具能被统一调用,降低集成成本 |
一句话总结三者关系:
Agent是大脑,负责思考和决策;Skills是手脚,负责具体执行;MCP是神经网络,让手脚能被大脑标准化地指挥。
下面是更精确的架构关系图:
二、Agent(智能体):有目标、能行动的AI
2.1 什么是Agent?
传统AI:你问一句,它答一句。没有记忆,不会主动做事。
AI Agent:你给它一个目标,它会自己思考“怎么达成”,然后调用工具、采取行动,并在过程中根据反馈调整策略,直到完成目标。
Agent的核心特征:
- ✅ 自主性(Autonomy):无需人类每步指导
- ✅ 反应性(Reactivity):能感知环境变化并响应
- ✅ 主动性(Proactiveness):为达成目标主动采取行动
- ✅ 社交性(Social ability):能与其他Agent或人类协作
2.2 Agent的工作原理(ReAct模式)
当前最主流的Agent架构是ReAct模式(Reasoning + Acting),即交替进行“推理”和“行动”。
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设计应该遵循以下原则:
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 Calling | Skills |
|---|---|---|
| 定义方 | 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采用客户端-服务器架构:
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 什么时候用什么?
决策指南:
- 🎯 简单场景(单次问答):直接调用LLM,不需要Agent
- 🎯 需要调用1-3个API:用Function Calling或Skills
- 🎯 复杂多步任务(订机票+查天气+写邮件):用Agent
- 🎯 工具数量>5个,或需要跨语言调用:引入MCP
- 🎯 构建可扩展的AI应用平台:三者都用,各司其职
6.3 学习路径建议
6.4 关键启发
- 不要把概念神化:Agent本质上就是“LLM + 循环 + 工具”,没有黑魔法
- Skills是核心价值:你的业务能力最终体现在Skills上,Agent只是调度者
- MCP解决实际问题:当你的工具超过10个时,你会发现MCP的价值
- 没有银弹:不是所有应用都需要Agent,简单场景用Function Calling就够了
写在最后
AI Agent、Skills、MCP这三个概念,本质上是在解决同一个问题:如何让AI安全、可靠、高效地与外部世界交互。
- Agent回答了“谁来决策”
- Skills回答了“能做什么”
- MCP回答了“怎么标准化对接”
希望这篇文章能帮你彻底理清这三者的关系。代码都已实测可用,建议你动手运行一遍,体会数据流动的全过程。
如果觉得有帮助,欢迎点赞、收藏、转发!也欢迎在评论区讨论你的实践心得。

265

被折叠的 条评论
为什么被折叠?



