Agent 研习舱

chapter 05 / 工具暴露层

工具调用:MCP 与 Skills

把世界暴露给模型:协议、技能与渐进式披露

模型只会输出文本和 tool_calls——它「说」要做什么,真正把世界接给它的是 Harness 的工具暴露层。这一章讲三个层层递进的问题:怎么连上外部系统(MCP)、怎么教会模型怎么用(Skills)、以及怎么在能力越来越多时不撑爆上下文(渐进式披露)。

连接问题: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 匹配加载。

Anthropic Engineering2025-10-16

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:

source walkthrough

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 按需去读。

  1. 1L1217披露的入口:告诉模型「有技能,自己去读」
    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 的设计。

  2. 2L92113目录长什么样:名字 + 描述 + 路径,仅此而已
    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 工具时会告警,因为披露链条断了。

以上为 Apache-2.0 许可的 openJiuwen 源码节选,仅截取教学所需片段;完整实现见 GitHub(固定 commit)

同样的思想在工具侧的对应物是 Tool Search:工具太多时不全量注入定义,而是给模型一个「搜索工具的工具」,用到什么搜什么。再往上一层是程序化工具调用:让模型写代码来编排多个工具调用,中间结果留在代码运行时里,不占对话上下文。

治理:Hooks 与工具注册表

暴露能力的同时要管住能力。Hooks 系统在工具调用前后挂确定性检查(如阻止 rm -rf、强制格式化);工具注册表统一管理工具的注册、发现与解析——openJiuwen 的 AbilityManager 就是这一角色:第 2 章循环里的 ability_manager.execute(...)、第 7 章把子代理注册成工具,走的都是它。

本章能力在 Harness 中处于什么位置?

harness positioning

本章能力在 Harness 中处于什么位置?

工具暴露层是循环的「行动」半径:循环驱动层发出 tool_calls,这一层解析、鉴权、执行、把结果写回上下文。它与上下文管理层直接对抗——每个工具定义、每段 Skill 说明都在消耗注意力预算,所以渐进式披露是两层之间的停火协议。

本章概念清单 / 点击进入概念卡片

章末测验

chapter quiz

0/4 已答

  1. Q1MCP(Model Context Protocol)解决的核心问题是?

  2. Q2Skills 与 MCP 的关系最准确的说法是?

  3. Q3openJiuwen 的 SkillUtil 如何实现渐进式披露?

  4. Q4为什么说「description 字段是触发条件,不是摘要」?