Agent 的工具调用机制:从 Function Calling、MCP 到 Skill

拆开一次 Agent 工具调用:模型只提出结构化调用意图,运行时负责校验、执行和回填;MCP 标准化外部能力,Skill 标准化可复用工作流。

Agent 的工具调用机制:从 Function Calling、MCP 到 Skill

如果只记一句话,可以这么理解:

Agent 不是模型自己在操作世界,而是模型、运行时、工具注册表、执行器和状态日志组成的闭环。

模型负责判断“下一步该调用什么”。
运行时负责决定“这个调用能不能执行、怎么执行、结果怎么回填”。
MCP 把外部工具和上下文标准化。
Skill 把可复用工作流和领域经验标准化。

本文基于 2026-07-10 查到的公开资料写成,主要参考 OpenAI function calling 文档OpenAI Agents SDK 运行循环Codex customizationCodex SkillsCodex MCPMCP 官方架构说明MCP 2025-11-25 规范Agent Skills specification。这里讲的是可观察、可实现、可设计的工程机制,不声称揭示任何闭源产品的私有内部实现。

1. 先把 Agent 拆成五个部件

平时说“Agent 会调用工具”,容易把事情说得太像魔法。更准确的拆法是五层:

User / task
  -> Host / runtime
      -> instruction stack
      -> tool and skill registry
      -> model call
  <- model output: final answer or tool call intent
  -> executor / MCP client / local harness
  <- observation
  -> next model call

第一层是模型。模型读上下文,做规划,生成自然语言,或者生成一个结构化的 tool call。它本身并没有直接访问文件、浏览器、数据库、网络和公司系统的能力。

第二层是 host 或 runtime。它可以是你写的应用、OpenAI Agents SDK runner、Codex CLI、IDE 扩展、桌面 app,或者某个内部 agent 平台。它负责把用户消息、系统指令、项目规则、可用工具、已加载技能、历史状态组装成一次模型调用。

第三层是工具注册表。这里放的是模型“看得见”的能力清单:函数工具、内置工具、MCP server 暴露的工具、子 agent、浏览器自动化、shell、代码编辑器等。模型通常看到的是工具名、描述、输入 schema 和少量约束。

第四层是执行器。模型不会真的执行 delete_filequery_databasesend_email。它只是输出“我要调用这个工具,参数是这些”。运行时校验 schema、权限、沙箱、审批和速率限制,然后由本地代码、远程服务或 MCP server 真正执行。

第五层是状态与轨迹。工具结果会作为 observation 回到对话或 run history 里。下一轮模型调用会看到这些结果,再决定继续调用工具还是结束。

这就是 Agent 工具调用的基本事实:模型提出行动,运行时执行行动,结果再变成模型的输入。

2. 最小工具调用循环

OpenAI function calling 文档把工具调用描述为一个多步对话:请求模型时提供可调用工具,模型返回 tool call,应用侧执行工具,把工具输出发回模型,模型再给最终回答或继续调用工具。Agents SDK 的 running agents 文档也把 agent run 描述成循环:调用模型,检查输出,有工具调用就执行并继续,有 handoff 就切换 agent,没有更多工具工作才返回结果。

写成伪代码,大概是这样:

history = [user_task]

while True:
    visible_context = assemble(
        instructions,
        project_guidance,
        selected_skills,
        tool_definitions,
        history,
    )

    output = model(visible_context)

    if output.is_final_answer():
        return output.text

    for call in output.tool_calls:
        checked = runtime.validate(call.name, call.arguments)
        approved = runtime.apply_policy(checked)
        result = runtime.execute(approved)
        history.append(tool_result(call.id, result))

关键点有三个。

第一,工具调用是“模型输出的一种格式”,不是模型内部真的打开了一个浏览器。模型只是在当前上下文里判断“这个工具可能有用”,然后生成调用意图和参数。

第二,执行权在应用侧。工具能不能跑、在哪跑、是否需要用户确认、能访问哪些文件和网络、错误如何处理,都由 runtime 决定。

第三,工具结果会进入后续上下文。Agent 的多步能力来自这个 observe、think、act 的循环,而不是单次模型调用本身。

3. 工具如何进入模型视野

一个工具要被 Agent 使用,通常要先变成模型可理解的描述:

{
  "name": "get_invoice",
  "description": "Fetch one invoice by invoice id. Use only when the user provided an invoice id.",
  "parameters": {
    "type": "object",
    "properties": {
      "invoice_id": { "type": "string" }
    },
    "required": ["invoice_id"]
  }
}

这段东西对程序员看起来像 API schema,对模型来说更像“行动菜单”。工具名和描述告诉模型什么时候选它,参数 schema 限制模型应该填什么,运行时再做真实校验。

OpenAI tools 文档提到,模型会根据 prompt 自动决定是否使用已配置工具,也可以用 tool_choice 控制或引导。工具太多时,问题会变复杂:所有工具都塞进上下文会浪费 token,也会增加选错工具的概率。Tool search就是为这个问题设计的:先只暴露 namespace 或 MCP server 的高层描述,等模型需要时再动态加载具体工具定义。

这说明工具设计不是普通后端 API 设计。普通 API 面向确定性代码,Agent 工具面向模型选择。模型不会像程序员一样读完整文档再写调用代码,它主要依赖当前上下文里的名称、描述、schema 和最近观察结果做判断。

所以,工具描述本身就是产品界面。写得含糊,模型就会误选;工具粒度混乱,模型就会在相似能力之间摇摆;返回结果啰嗦,下一轮上下文就会被噪声污染。

4. MCP:把外部能力接进来

MCP,全称 Model Context Protocol,解决的是“Agent 如何标准化连接外部系统”的问题。

按照 MCP 官方架构说明,MCP 是 client-server 架构:

MCP Host:    AI 应用本体,比如 IDE、桌面客户端、Agent 平台
MCP Client:  Host 内部为每个 server 维护的连接组件
MCP Server:  暴露工具、资源、prompt 的外部程序或服务

一个 Host 可以连接多个 MCP server。比如一个 coding agent 同时连接 GitHub、浏览器、Figma、内部知识库和数据库。每个 server 不需要知道模型是哪家,也不需要为每个 Agent 平台写一套私有插件,只要讲 MCP。

MCP 的协议层

MCP 2025-11-25 规范说明,MCP 基础消息遵循 JSON-RPC 2.0;所有实现必须支持 base protocol 和 lifecycle management。连接生命周期大致是:

initialize: 版本协商、能力协商、交换 client/server 信息
operation:  正常请求、响应、通知
shutdown:   关闭连接

传输层规范定义了两种标准 transport:stdio 和 Streamable HTTP。stdio 常见于本地工具,Host 启动一个子进程,用 stdin/stdout 传 JSON-RPC 消息;Streamable HTTP 则适合远程服务,可以服务多个 client,并支持基于 HTTP 的认证、流式响应和通知。

MCP server 暴露三类常见能力

第一类是 tools。MCP tools 规范说,tool 让模型能够与外部系统交互,例如查数据库、调 API 或做计算。工具是 model-controlled 的,也就是模型可以根据上下文自动发现和调用。典型流程是:

tools/list  -> client 发现 server 有哪些工具
tools/call  -> client 请求 server 执行某个工具
tool result -> server 返回文本、结构化内容、图片、资源链接等结果

第二类是 resources。MCP resources 规范把 resource 定义成给模型提供上下文的数据,比如文件、数据库 schema、应用内对象。resource 通常是 application-driven 的,由 Host 决定如何展示、搜索、选择或自动加入上下文。它们通过 URI 标识,可以支持模板、订阅和列表变更通知。

第三类是 prompts。MCP prompts 规范把 prompt 定义成 server 提供的结构化消息模板。prompt 通常是 user-controlled 的,更像 slash command 或工作流模板,需要用户显式选择。

这三类东西不要混在一起:

Tool:     让模型做动作。
Resource: 给模型补上下文。
Prompt:   给用户提供可复用任务模板。

MCP 在 Agent 调用链里的位置

当模型选择一个 MCP-backed tool 时,真实调用链通常是:

Model emits tool call
  -> Host validates policy
  -> MCP Client sends JSON-RPC tools/call
  -> MCP Server executes against external system
  -> MCP Server returns result
  -> Host converts result into model observation
  -> Model continues

模型不直接连 MCP server。它甚至不一定知道底层是 stdio、HTTP、OAuth 还是本地子进程。它只看到被 Host 翻译后的工具能力。

Codex MCP 文档也体现了这一点:Codex 可以连接 stdio 或 Streamable HTTP MCP server,并读取 server 初始化时返回的 instructions 字段作为 server-wide guidance;MCP 配置存放在 Codex 的配置文件中,CLI、IDE extension 和桌面 app 可以共享同一 Codex host 的 MCP 配置。

所以 MCP 不是“另一个 prompt 技巧”,而是 Agent runtime 和外部系统之间的协议层。

5. Skill:把做事方法装进来

Skill 解决的是另一个问题:Agent 不只是缺工具,也缺“怎么把工具和材料组织成可靠流程”的经验。

Agent Skills specification把 Skill 定义成一个目录,至少包含 SKILL.md,还可以包含 scripts/references/assets/ 等可选目录。SKILL.md 需要 YAML frontmatter,最少有 namedescription,正文则写具体工作流说明。

典型结构是:

my-skill/
  SKILL.md
  scripts/
  references/
  assets/

Skill 的核心机制叫 progressive disclosure,也就是渐进披露:

Discovery:  启动时只加载 name 和 description
Activation: 任务匹配时读取完整 SKILL.md
Execution:  根据需要再读取 references、assets,或运行 scripts

这和工具 schema 的思路类似:不要一开始把所有细节塞进上下文,而是先让模型知道“有哪些能力可能可用”,真正需要时再加载完整说明。

Codex Skills 文档也采用这个思路:Codex 初始上下文里会包含可用 skills 的名称、描述和文件路径;当它决定使用某个 skill 时,才读取完整 SKILL.md。Codex 可以通过两种方式激活 skill:用户显式点名,或者任务和 skill 的 description 匹配。

Skill 和 Tool 最大的区别是:

Tool 是一个可调用动作。
Skill 是一套如何完成任务的可复用工作流。

举例说,一个 web_search tool 能搜索网页;一个“写研究博客”的 Skill 会告诉 Agent 什么时候查证资料、怎么组织文章、如何避免泄露本机路径、写入哪个内容目录、跑哪些校验命令。Skill 可以指挥 Agent 调用工具,但 Skill 本身不是一个远程 RPC endpoint。

6. MCP 和 Skill 的根本差别

很多人会把 MCP、Skill、Plugin、AGENTS.md、function tool 混在一起。可以用这张表拆开:

机制本质解决什么问题谁来触发典型内容
Prompt当前任务约束这一次怎么做用户或应用临时要求、输出格式、边界
AGENTS.md项目持久规则这个仓库长期怎么做Runtime 启动时读取构建命令、代码风格、测试要求
Function tool单个动作接口让模型调用应用侧代码模型生成 tool call名称、描述、JSON schema、执行函数
MCP外部能力协议标准化连接工具、资源、promptHost 连接 server,模型调用 tooltools/list、tools/call、resources/read
Skill可复用工作流让 Agent 学会一类任务怎么做显式点名或 description 匹配SKILL.md、脚本、参考资料、模板
Plugin分发包把技能、连接器、工具等分发给团队用户或管理员安装skills、MCP 配置、资产、元数据
Subagent专家协作单元把复杂任务拆给专门 agentRuntime 或主 agent专门指令、工具、上下文、回传结果

更短地说:

AGENTS.md 规定这个项目的习惯。
Skill 教 Agent 怎么完成一类任务。
Tool 给 Agent 一个动作按钮。
MCP 让动作按钮可以来自外部系统。
Plugin 负责把这些东西打包分发。

Codex customization 文档也强调这些层是互补的:AGENTS.md 塑造行为,skills 打包可复用流程和专业知识,MCP 连接本地 workspace 之外的系统,subagents 用来委派专业工作。

7. 用“发布这篇博客”倒推一次真实调用

这篇文章本身就是一个很好的例子。用户说“讲清楚 Agent 内部工具调用机制,并发布博客”。一个支持 Skills 和 Tools 的 Agent 不应该只开始写文章,而是会先组装工作流。

理想调用轨迹大概是:

1. 在可用 skill 列表里发现 publish-to-blog 类 skill。
2. 因为任务包含“发布博客”,激活 skill,读取完整 SKILL.md。
3. Skill 要求先确认博客仓库、内容 schema、写作质量门槛、路径安全规则。
4. 任务涉及 Codex、MCP、Skill 的当前机制,再激活官方文档查询相关 skill 或工具。
5. Agent 用 web/docs/search 工具查公开资料,用 shell 读取本地博客 schema。
6. Agent 写源稿,再写发布稿。
7. Agent 用 shell 跑 validate 和 build。
8. Agent 把结果、文件路径和校验状态回报给用户。

这条链里,模型负责判断下一步,Skill 负责提供流程,MCP 或内置工具负责拿资料和执行动作,runtime 负责权限、文件写入、命令执行和结果回填。

如果没有 Skill,模型也许能写一篇文章,但容易漏掉博客 schema、路径泄露校验、构建命令和发布约定。如果没有工具,模型只能凭记忆写,不能查当前文档,也不能实际落盘和验证。如果没有 runtime 权限控制,任何工具调用都会变成安全风险。

这就是 Skill、Tool、MCP、Runtime 同时存在的原因。

8. 安全边界:MCP 和 Skill 都不是天然可信

Agent 工具调用最容易被误解的一点是:协议不是安全边界。

MCP 标准化的是 client/server 如何交换能力、上下文和调用结果,但“能不能调用”“调用前是否要确认”“token 怎么授权”“哪些资源可见”“结果是否可信”,仍然要靠 Host、server、企业策略和用户界面共同实现。

MCP tools 规范明确建议应用展示哪些工具暴露给模型、在工具调用时给出清晰提示,并对操作提供用户确认能力。Streamable HTTP transport 规范也要求或建议 server 做 Origin 校验、本地服务绑定 localhost、实现认证,避免远程网页通过 DNS rebinding 之类方式碰到本地 MCP server。

OpenAI 的 MCP server 文档也提醒,自定义 MCP server 不是 OpenAI 开发或验证的第三方服务;连接前要审查它如何使用数据,构建 server 时不要把敏感信息放进 tool JSON,也不要在工具定义里放恶意内容。

Skill 也一样。Skill 是指令、脚本、参考资料和模板的包。它能极大提高复用性,也可能携带过时流程、错误假设、危险命令或隐藏 prompt injection。安装 Skill 应该更像安装软件包,而不是复制一段 prompt。

一个基本的安全清单是:

只连接可信来源的 MCP server。
只给 MCP server 最小必要权限。
敏感操作默认需要确认或 dry run。
不要把 secrets 写进 tool description、resource 内容或 Skill 文件。
把 tool result 当成不可信数据,而不是高优先级指令。
安装 Skill 前阅读 SKILL.md 和 scripts。
记录每次工具调用的输入、输出、审批、耗时和失败。
给危险工具设计撤销、回滚或补偿机制。

Agent 越能干,越需要清晰的执行边界。没有边界的工具调用,不是能力,是事故入口。

9. 怎么设计一个好工具

面向 Agent 的工具要按“模型如何选择”来设计,而不是按“后端接口已经长什么样”来暴露。

好的 tool description 应该回答四个问题:

这个工具做什么?
什么时候应该用?
什么时候不应该用?
调用后会产生什么副作用?

工具 schema 要尽量窄。参数能枚举就枚举,能结构化就结构化,能拆成明确字段就不要给一整段自由文本。危险操作要把 dry_runconfirmreasontarget_id 之类字段做成显式参数。

工具返回值要为下一轮模型服务。不要把整个后端响应原样吐出来。更好的返回是:

{
  "ok": true,
  "summary": "Invoice inv_123 is overdue by 14 days.",
  "data": {
    "invoice_id": "inv_123",
    "status": "overdue"
  },
  "next_actions": ["send_reminder", "open_dispute"]
}

错误也要结构化。不要只返回 failed,而要告诉模型是参数错、权限不够、资源不存在、速率限制、外部服务失败,还是需要用户确认。Agent 的自我修复能力很大程度取决于错误语义是否清楚。

对于大量工具,要分组和延迟加载。OpenAI tool search 文档建议用 namespace 或 MCP server 这类高层分组来承载可搜索工具,并让高层描述足够清晰。这个建议背后的原则很简单:模型先选领域,再选动作,比一次面对几十个相似动作更稳定。

10. 怎么设计一个好 MCP server

MCP server 的设计重点不是“把所有 REST API 原样包出来”,而是“给模型暴露合适的任务语义”。

一个实用 MCP server 可以按这几个问题设计:

这个 server 代表哪个领域边界?
哪些能力应该是 tools,哪些应该是 resources,哪些应该是 prompts?
工具数量是否太多?
工具描述是否能让模型做出正确选择?
是否需要 server-wide instructions 说明跨工具约束?
认证、授权、租户隔离、审计日志在哪里实现?
长任务如何返回 task id、状态查询和取消能力?
工具列表或资源列表变化时是否通知 client?

常见错误是做一个万能工具:

run_api(method, path, body)
query_database(sql)
execute_shell(command)

这些工具对人类程序员很灵活,对 Agent 来说却危险且难选。它们把语义、权限、验证、审计全部推给模型。更好的做法是暴露任务级工具:

list_open_invoices(customer_id)
create_refund(order_id, amount, reason, dry_run)
search_design_files(query, project_id)
read_component_spec(component_id)

MCP server 还应该把 context 和 action 分开。数据库 schema、文件列表、设计稿元数据更适合 resources;真正会改变外部世界的动作才应该是 tools;常见工作流入口可以做 prompts。

11. 怎么设计一个好 Skill

Skill 的关键是触发准确、加载克制、执行可验证。

description 要写得像路由规则,不要写成广告语。它应该包含任务关键词、适用场景和边界。比如:

description: Publish a Markdown draft to Lei's Astro blog. Use when the user asks to publish, migrate, rewrite, or validate a blog post. Do not use for generic writing that is not intended for the blog.

SKILL.md 正文要写流程,而不是堆知识。好的 Skill 至少应该包含:

什么时候使用
输入从哪里来
先读哪些参考
具体步骤顺序
需要调用哪些工具或脚本
如何验证完成
常见失败和修复
不能做什么

长资料应该放进 references/,脚本放进 scripts/,模板放进 assets/。Agent Skills 规范建议利用渐进披露,主 SKILL.md 不要过长,详细材料按需加载。这不仅节省上下文,也让模型更容易抓住主流程。

Skill 还要有验证出口。一个没有验证步骤的 Skill,只是“建议”。一个有校验命令、输出检查、人工确认点和失败处理的 Skill,才是可复用工作流。

12. 最后用一句话收束

Agent 的内部工具调用机制,可以压缩成一个公式:

工具调用 = 上下文中可见的能力描述
        + 模型生成的结构化调用意图
        + 运行时控制的真实执行
        + 回填给模型的观察结果

MCP 把“外部能力如何接入”标准化。Skill 把“复杂任务如何被复用”标准化。AGENTS.md 把“项目长期习惯”固化下来。Tool schema 把“单个动作如何被模型选择和调用”表达清楚。

真正成熟的 Agent 系统,不是给模型开更多权限,而是把每一种能力放在正确的层:

一次性要求放 prompt。
项目习惯放 AGENTS.md。
可复用流程放 Skill。
外部系统接入走 MCP。
确定性动作做成 typed tool。
危险操作交给 runtime policy 和 human approval。

当你能分清这些层,Agent 就不再是一团“会自己调用工具的模型”,而是一套可以设计、审计、扩展和治理的软件系统。