AI Agent 平台 CoPaw 源码学习:系统核心——MCP 和 Skills 模块

前言
大家好,我是高双喜。
前面咱们连着聊了三期 QwenPaw 源码:Agent 模块、记忆模块、浏览器自动化模块。
一个成熟的 AI Agent 平台,必须具备强大的扩展能力。QwenPaw 通过两个核心机制来实现:MCP(Model Context Protocol)模块负责集成外部工具,Skills 模块负责知识包管理。
今天咱们就把这两个模块一次聊透。
一、MCP 模块:外部工具集成
1.1 什么是 MCP?
MCP(Model Context Protocol)是 AI 工具集成的标准协议。QwenPaw 通过 StdIO 管道与 MCP 服务端进程通信,自动发现和注册服务端暴露的工具。Agent 在 ReAct 循环中可以像使用内置工具一样调用 MCP 工具。
简单理解:MCP 就是一个”工具市场”,各种外部服务通过 MCP 协议接入 QwenPaw,Agent 就能调用它们。
1.2 核心组件
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1.3 配置模型
# MCPClientConfigclassMCPClientConfig(BaseModel): name: str# 客户端显示名称 description: str# 描述信息 enabled: bool# 是否启用 command: str# MCP 服务端启动命令(如 "npx", "python") args: List[str] # 命令行参数 env: Dict[str, str] # 环境变量
默认内置了一个客户端 tavily_search,仅在 TAVILY_API_KEY 环境变量存在时自动启用:
# config.py 默认配置clients: Dict[str, MCPClientConfig] = Field( default_factory=lambda: {"tavily_search": MCPClientConfig( name="tavily_mcp", enabled=bool(os.getenv("TAVILY_API_KEY")), # 自动检测 command="npx", args=["-y", "tavily-mcp@latest"], env={"TAVILY_API_KEY": os.getenv("TAVILY_API_KEY", "")}, ), },)
1.4 MCPClientManager 核心方法
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这里有个很巧妙的设计:replace_client 两阶段操作:
阶段 1 (锁外,可能耗时): new_client = StdIOStatefulClient(name, command, args, env) await asyncio.wait_for(new_client.connect(), timeout=60.0)阶段 2 (锁内,原子操作): async with _lock: old_client = _clients.get(key) _clients[key] = new_client if old_client: await old_client.close()
锁外连接、锁内替换,避免长时间持锁导致并发下降。
1.5 热重载机制
MCPConfigWatcher 每 2 秒轮询一次 config.json:
_poll_loop → _check():1. 检查 config.json mtime → 未变则跳过 (快速拒绝)2. 加载配置,计算 mcp section hash → 相同则跳过3. 检查 _reload_task 是否在进行 → 是则跳过4. 创建后台 asyncio.Task → _reload_changed_clients()
Diff 处理逻辑:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
重试机制也很完善:同一配置 hash 最多 3 次重试,配置变更时自动重置计数,重载失败不阻塞主流程。
1.6 Agent 集成
每次查询的流程:
# AgentRunner.query_handler() - 每次查询mcp_clients = await mcp_manager.get_clients() # 获取最新列表agent = QwenPawAgent(..., mcp_clients=mcp_clients, ...) # 注入到 Agentawait agent.register_mcp_clients() # 注册到 Toolkit# Agent ReAct 循环中自动发现并调用 MCP 工具
热重载原理很简单:每次查询创建新 QwenPawAgent 实例 → 从 get_clients() 获取最新客户端列表。MCPConfigWatcher 更新管理器中的客户端后,下一次查询自动使用新客户端,无需重启应用。
1.7 REST API
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
安全特性:API 响应自动掩码 env 值(如 sk-****1234),防止 API Key 泄露。
二、Skills 模块:知识包管理
2.1 Skills 与 Tools 的区别
这是两个层次的概念:
-
Tools(工具层):Python 异步函数,实现原子操作(文件读写、Shell 执行、浏览器控制等),直接注册到 agentscope 的 Toolkit -
Skills(技能层):Markdown 知识包(SKILL.md),通过 YAML Front Matter + Markdown 正为 Agent 注入领域知识,指导如何组合使用 Tools
核心设计理念:Skills 不是插件代码,而是 Prompt 注入式的知识包。每个 Skill 本质上是一份结构化的 Markdown 文档,Agent 在回答用户问题时参考这些文档来决定使用哪些 Tools 以及如何使用。
2.2 目录结构
⚠️ 修正:源码实际位于
QwenPaw/agents/(venv site-packages 下),而非src/QwenPaw/agents/
QwenPaw/agents/├── tools/ # 工具层(Python 异步函数)│ ├── browser_control.py # 浏览器自动化(3481 行)│ ├── browser_snapshot.py # 浏览器快照/无障碍树解析│ ├── desktop_screenshot.py # 桌面截图│ ├── file_io.py # 文件读写编辑(395 行)│ ├── file_search.py # 文件搜索(grep/glob)│ ├── get_current_time.py # 获取当前时间│ ├── memory_search.py # 记忆语义搜索│ ├── send_file.py # 向用户发送文件│ ├── shell.py # Shell 命令执行│ └── view_media.py # 图片/视频查看│├── skills/ # 技能层(Markdown 知识包,15 个)│ ├── browser_cdp/ # CDP 浏览器控制│ ├── browser_visible/ # 可见浏览器模式│ ├── channel_message/ # 频道消息│ ├── QwenPaw_source_index/ # QwenPaw 源码索引│ ├── cron/ # 定时任务管理│ ├── dingtalk_channel/ # 钉钉频道配置│ ├── docx/ # Word 文档处理│ ├── file_reader/ # 文件阅读│ ├── guidance/ # 安装配置引导│ ├── himalaya/ # 邮件 CLI 管理│ ├── multi_agent_collaboration/ # 多 Agent 协作│ ├── news/ # 新闻查询│ ├── pdf/ # PDF 处理│ ├── pptx/ # PPT 演示文稿│ └── xlsx/ # Excel 电子表格│├── skills_manager.py # 技能管理器(2620 行)├── skills_hub.py # 技能市场客户端(1691 行)└── react_agent.py # Agent 主类(集成点)
2.3 Tools 工具层
工具注册机制
所有工具通过 __init__.py 统一导出,分为两类来源:
-
agentscope 内置工具(3 个):
-
execute_python_code — Python 代码执行 -
view_text_file — 查看文本文件 -
write_text_file — 写入文本文件 -
QwenPaw 自定义工具(16 个):
-
read_file / write_file / edit_file / append_file — 文件 I/O -
grep_search / glob_search — 文件搜索 -
execute_shell_command — Shell 命令执行 -
send_file_to_user — 文件推送 -
browser_use — 浏览器自动化 -
desktop_screenshot — 桌面截图 -
view_image / view_video — 媒体查看 -
create_memory_search_tool — 记忆搜索(工厂模式) -
get_current_time / set_user_timezone — 时间相关 -
get_token_usage — Token 用量统计
工具接口规范
from agentscope.tool import ToolResponsefrom agentscope.message import TextBlockasyncdeftool_function(param1: type, ...) -> ToolResponse:"""工具描述(docstring 即 LLM 的 function description)。 Args: param1 (`type`): 参数说明。 """# 实现逻辑return ToolResponse( content=[TextBlock(type="text", text="结果文本")] )
关键约定:
-
所有工具函数为 async 异步函数 -
返回值必须是 ToolResponse 对象 -
函数的 docstring 直接作为 LLM 调用时的工具描述 -
参数类型注解将映射为 JSON Schema 供 LLM 使用
2.4 Skills 技能层
SKILL.md 文件格式
每个技能是一个目录,必须包含 SKILL.md 文件。格式为 YAML Front Matter + Markdown:
---name:skill_name# 必填:技能标识名description:"技能描述..."# 必填:LLM 匹配触发的描述homepage:https://...# 可选:项目主页license:Proprietary# 可选:许可证声明metadata:QwenPaw:# 或 openclawemoji:"📄"# 展示图标requires:bins: ["himalaya"] # 需要的可执行文件install:-id:brewkind:brewformula:himalayabins: ["himalaya"]---# 技能标题正文内容:操作指南、代码示例、参考表格等Agent会将此内容作为上下文来理解如何完成相关任务
内置技能清单(共 15 个)
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2.5 三级目录体系
|
|
|
|
|
|---|---|---|---|
|
|
|
QwenPaw/agents/skills/ |
|
|
|
|
~/.QwenPaw/customized_skills/ |
|
|
|
|
~/.QwenPaw/active_skills/ |
|
优先级:customized > builtin(同名时自定义技能覆盖内置)
2.6 SkillsManager 核心方法
# SkillService 静态方法list_all_skills() # 列出 builtin + customized 所有技能list_available_skills() # 列出 active 目录中已激活的技能create_skill() # 在 customized 目录创建新技能disable_skill() # 从 active 目录删除(禁用)enable_skill() # 同步到 active 目录(启用)delete_skill() # 从 customized 目录永久删除sync_from_active_to_customized() # 反向同步load_skill_file() # 加载 references/ 或 scripts/ 中的文件
2.7 SkillsHub:在线技能市场
SkillsHub 是 QwenPaw 的在线技能市场客户端,支持从多个来源搜索和安装技能:
-
ClawHub (clawhub.ai) — QwenPaw 官方技能市场 -
Skills.sh (skills.sh) — 基于 GitHub 仓库的技能注册表 -
GitHub (github.com) — 直接从 GitHub 仓库导入 -
SkillsMP (skillsmp.com) — 第三方技能市场
URL 来源解析策略
install_skill_from_hub(bundle_url)├── _extract_skills_sh_spec(url) → (owner, repo, skill)├── _extract_github_spec(url) → (owner, repo, branch, path_hint)├── _extract_skillsmp_slug(url) → slug├── _resolve_clawhub_slug(url) → slug└── 兜底: 直接 JSON GET
2.8 Agent 集成点
classQwenPawAgent(ReActAgent):def__init__(self, ...): toolkit = self._create_toolkit() # 步骤 1: 创建工具包self._register_skills(toolkit) # 步骤 2: 注册技能 sys_prompt = self._build_sys_prompt() # 步骤 3: 构建提示词super().__init__(..., toolkit=toolkit) # 步骤 4: 初始化 ReAct Agent
工具注册(实际注册 19 个工具):
def_create_toolkit(self) -> Toolkit: toolkit = Toolkit()# agentscope 内置(3 个) toolkit.register_tool_function(execute_python_code) toolkit.register_tool_function(view_text_file) toolkit.register_tool_function(write_text_file)# QwenPaw 自定义(16 个) toolkit.register_tool_function(execute_shell_command) toolkit.register_tool_function(read_file) toolkit.register_tool_function(write_file) toolkit.register_tool_function(edit_file) toolkit.register_tool_function(append_file) toolkit.register_tool_function(grep_search) toolkit.register_tool_function(glob_search) toolkit.register_tool_function(send_file_to_user) toolkit.register_tool_function(desktop_screenshot) toolkit.register_tool_function(view_image) toolkit.register_tool_function(view_video) toolkit.register_tool_function(browser_use) toolkit.register_tool_function(get_current_time) toolkit.register_tool_function(set_user_timezone) toolkit.register_tool_function(get_token_usage)# create_memory_search_tool 是工厂函数,单独处理return toolkit
技能注册:
def_register_skills(self, toolkit: Toolkit): ensure_skills_initialized() working_skills_dir = get_working_skills_dir() # ~/.QwenPaw/active_skills/ available_skills = list_available_skills()for skill_name in available_skills: skill_dir = working_skills_dir / skill_nameif skill_dir.exists(): toolkit.register_agent_skill(str(skill_dir))
register_agent_skill() 会读取 SKILL.md,解析 YAML Front Matter 获取 name 和 description,将 Markdown 正文作为知识上下文注入 Agent 的系统提示。
三、架构模式总结
3.1 Skills 与 Tools 关系
┌─────────────────────────────────────────────────────┐│ QwenPawAgent ││ ┌───────────────┐ ┌────────────────────────────┐ ││ │ Toolkit │ │ System Prompt │ ││ │ (agentscope) │ │ (AGENTS.md + SOUL.md + │ ││ │ │ │ Skills 知识注入) │ ││ │ Tools: │ │ │ ││ │ • shell │ │ Skills 注入后 Agent 知道: │ ││ │ • read_file │ │ • 如何用 browser_use 查新闻 │ ││ │ • write_file │ │ • 如何用 himalaya 管邮件 │ ││ │ • edit_file │ │ • 如何用 shell 管 cron │ ││ │ • browser_use│ │ • 如何创建 docx/pdf/pptx │ ││ │ • screenshot │ │ • 如何进行 multi_agent │ ││ │ • send_file │ │ • 如何用 CDP 控制浏览器 │ ││ │ • get_time │ │ • ... │ ││ └───────────────┘ └────────────────────────────┘ │└─────────────────────────────────────────────────────┘
3.2 技能生命周期
┌──────────────┐│ 代码内置 ││ (builtin) │└──────┬───────┘│ sync_skills_to_working_dir()┌──────────────┐ ││ Hub 安装 │ ──→ ▼│ (online) │ ┌──────────────┐ ┌──────────────┐└──────────────┘ │ 自定义 │ ──→ │ 激活运行 │┌──────────────┐ │ (customized) │ │ (active) ││ CLI 创建 │ ──→ │ │ ││ API 创建 │ 覆盖 builtin │ │ Agent 加载此 │└──────────────┘ └──────────────┘ └──────────────┘▲ ││ sync_from_active... │└─────────────────────┘
总结
QwenPaw 的 MCP 和 Skills 模块设计得相当精妙:
MCP 模块亮点:
-
StdIO 通信:通过进程间标准输入输出通信,稳定可靠 -
热重载:每 2 秒检测配置变更,客户端独立替换 -
并发安全:锁外连接 + 锁内替换,避免长持锁 -
容错设计:初始化失败不中断、重载失败不阻塞
Skills 模块亮点:
-
Prompt 注入:用 Markdown 文档作为知识包,不含可执行代码 -
三级分层:builtin → customized → active,支持覆盖和隔离 -
多来源 Hub:支持 ClawHub、Skills.sh、GitHub、SkillsMP 四种安装来源 -
文件驱动:技能发现完全基于目录结构和 SKILL.md 文件存在性
数据一览:
-
MCP 模块:manager.py (266 行) + watcher.py (330 行) + stateful_client.py (600+ 行) -
Skills 模块:skills_manager.py (2620 行) + skills_hub.py (1691 行) -
工具总数:19 个(3 个 agentscope 内置 + 16 个 QwenPaw 自定义) -
内置技能:15 个
这两个模块共同构成了 QwenPaw 的能力扩展体系:MCP 负责接入外部服务,Skills 负责注入领域知识。有了它们,Agent 才能”上知天文地理,下知鸡毛蒜皮”。
好了,MCP 和 Skills 模块就聊到这里。觉得有帮助的点个赞、在看,转发给需要的朋友。咱们下期见。
夜雨聆风