第 5 章 · 工具暴露层
工具调用:MCP 与 Skills
把世界暴露给模型:协议、技能与渐进式披露
学完你能
核心 · 约 20 分钟- 说清 MCP、Skills、渐进式披露分别解决连接、知识、预算中的哪个问题
- 判断一项能力该做成工具、MCP 服务器还是 Skill
- 说出 Hooks 与工具注册表在工具治理里各管什么
建议先读:第 3 章 Harness 是什么
模型只会输出文本和 tool_calls——它「说」要做什么,真正把世界接给它的是 Harness 的工具暴露层。这一章讲三个层层递进的问题:怎么连上外部系统(MCP)、怎么教会模型怎么用(Skills)、以及怎么在能力越来越多时不撑爆上下文(渐进式披露)。
01
连接
MCP:把集成变成协议
02
知识
Skills:教模型怎么干
03
预算
渐进式披露:目录常驻,正文按需
连接问题:MCP 把集成变成协议
在 MCP(Model Context Protocol)之前,「Agent × 外部系统」的每对组合都要各自重做认证、发现、集成。三条接入路径的竞争里,协议层胜出了:
- 直接 API:每对组合一次性集成,无法复用;
- CLI:瘦层,依赖本地文件系统,出不了本机;
- MCP:客户端(Agent)连服务器(能力暴露方),认证、发现、语义全部标准化——一台远程服务器可被任何兼容客户端使用(Claude、Cursor、VS Code……)。
生产 Agent 跑在云上,要触达的系统也在云上、在认证之后——远程优先的协议层是分发的前提。openJiuwen 在 agent-core 里内置了 MCP 客户端(core/foundation/tool/mcp/client/,SSE 与 streamable HTTP 两种传输),agent-protocol 仓库还提供了 C++ 的 MCP/A2A SDK。
知识问题:Skills 教模型「怎么干」
连上了系统不等于会干活。Skills 是另一层:文件夹形式的能力包——Markdown 指令、脚本、资源数据,Agent 在运行时按 description 匹配加载。
Equipping agents for the real world with Agent Skills ↗
Skills 是文件夹形式的能力包(指令 + 脚本 + 资源),Agent 按 description 动态发现与加载——程序性知识的渐进式披露机制。
两者的关系一句话讲清:MCP 给能力,Skills 给「如何用」的程序性知识。 一个 MCP 服务器让你能操作数据库;一个 Skill 告诉你本团队的数据库变更要先跑迁移脚本、再更新 schema 文档。
写 Skill 的核心技巧(详见概念卡片 Skill 五种设计模式):
- description 是触发条件,不是摘要——它回答「什么情况下用我」,模型靠它决定何时加载;
- 不陈述显而易见的,重点写 Gotchas(坑);
- 利用文件系统做递归引用——这就引出第三个问题。
预算问题:渐进式披露
能力包越来越多,全部塞进系统提示就回到了第 4 章的 Context Rot。渐进式披露(Progressive Disclosure)的原则:常驻上下文的只有目录,正文按需加载。
看 openJiuwen 的最小实现——整个机制的枢纽只有六行 prompt:
源码走读
SkillUtil:一段 prompt 就把「渐进式披露」讲清楚了
openJiuwen-ai/agent-core @ 1e3a5c7a3d · openjiuwen/core/single_agent/skills/skill_util.py:12-113 · 提取于 2026-07-18
Skills 的核心问题:领域知识很多,上下文很贵,怎么办?openJiuwen 的答案与 Claude Code 一致——系统提示里只放每个 Skill 的名字、描述和目录,正文让 Agent 用 read_file 按需去读。
- 段 1L12–17披露的入口:告诉模型「有技能,自己去读」
SKILL_PROMPT_CONTENT = ''' To help you better complete tasks, the following skill knowledge is equipped: {{skills}} You can use the read_file tool to read the corresponding SKILL.md file to obtain the relevant skill. ''' skill_prompt = PromptTemplate(content=SKILL_PROMPT_CONTENT)整个渐进式披露机制的枢纽就这六行:系统提示不内联任何 SKILL.md 正文,只声明技能存在,并指出获取方式(read_file)。知识的「目录」常驻上下文,知识的「正文」按需加载——这正是 Anthropic Agent Skills 的设计。
- 段 2L92–113目录长什么样:名字 + 描述 + 路径,仅此而已
def get_skill_prompt(self) -> str: """Generate a formatted prompt string containing information about all registered skills.""" system_prompt = ( "You are an agent equipped with various skills to solve problems.\n" "Before attempting any task, read the relevant skill document (SKILL.md) " "using read_file and follow its workflow.\n" ) skills = self._skill_manager.get_all() skills_info = [] for index, skill in enumerate(skills): skills_info.append( f"{index}.Skill name: {skill.name}; " f"Skill description: {skill.description}; " f"Skill directory: {skill.directory}" ) skill_text = skill_prompt.format({"skills": "\n".join(skills_info)}).content return system_prompt + "\n" + skill_text每个技能进入上下文的成本被压到一行:name + description + directory。description 在这里不是摘要,而是给模型看的触发条件——它写得好不好,直接决定模型会不会在正确的时机去读这个 Skill。这也解释了第 2 章 ReActAgent 里那个细节:注册了 Skill 却没有 read_file 工具时会告警,因为披露链条断了。
同样的思想在工具侧的对应物是 Tool Search:工具太多时不全量注入定义,而是给模型一个「搜索工具的工具」,用到什么搜什么。再往上一层是程序化工具调用:让模型写代码来编排多个工具调用,中间结果留在代码运行时里,不占对话上下文。
治理:Hooks 与工具注册表
暴露能力的同时要管住能力。Hooks 系统在工具调用前后挂确定性检查(如阻止 rm -rf、强制格式化);工具注册表统一管理工具的注册、发现与解析——openJiuwen 的 AbilityManager 就是这一角色:第 2 章循环里的 ability_manager.execute(...)、第 7 章把子代理注册成工具,走的都是它。
本章能力在 Harness 中处于什么位置?
在 Harness 中的位置
本章能力在 Harness 中处于什么位置?
工具暴露层是循环的「行动」半径:循环驱动层发出 tool_calls,这一层解析、鉴权、执行、把结果写回上下文。它与上下文管理层直接对抗——每个工具定义、每段 Skill 说明都在消耗注意力预算,所以渐进式披露是两层之间的停火协议。
本章概念
章末测验
本章测验
0/4 已答
Q1MCP(Model Context Protocol)解决的核心问题是?
Q2Skills 与 MCP 的关系最准确的说法是?
Q3openJiuwen 的 SkillUtil 如何实现渐进式披露?
Q4为什么说「description 字段是触发条件,不是摘要」?
四家怎么做
这一章的设计,四家怎么落地
每个问题点进去,是四家带行号的源码与按需求的场景建议。
工具定义方式
工具新增一个工具要写什么?谁负责校验参数和生命周期?
- openJiuwen
- 抽象基类 + ToolCard 元数据,元类织入生命周期
- Codex
- trait 对象注册表,显式处理重名冲突
- DeepSeek Harness
- defineTool 接口,强制声明规范化输出
- Pi
- TypeBox schema + 工厂函数,内核只有四个工具
3 条场景建议 →
MCP 接入
工具外部 MCP 服务器以什么方式成为可用工具?
- openJiuwen
- MCP 工具包成普通 Tool,和本地工具走同一条管线
- Codex
- mcp__服务器__工具 命名,冲突时追加身份哈希
- DeepSeek Harness
- 插件把 MCP 工具注册进 ToolRuntime,公开名是身份的纯函数
- Pi
- 刻意不支持:内核无 MCP,交给扩展
3 条场景建议 →
Skills 与渐进披露
工具能力多到放不进上下文时,怎么按需暴露?
- openJiuwen
- 系统提示只列名字、描述、目录,正文靠 read_file 自取
- Codex
- 技能目录有预算:默认占上下文窗口的 2%,超了先截描述再删
- DeepSeek Harness
- 专用 skill 加载工具,目录作为会话消息发布、变了才重发
- Pi
- 系统提示里一段 XML 列表,读文件即加载
3 条场景建议 →