一篇面向工程师的技术深度文章,带你从底层机制到上层抽象,彻底搞懂 AI Agent 的工具调用体系。
目录
- 为什么需要"工具调用"?
- [Function Calling:大模型的手与脚](#Function Calling:大模型的手与脚)
- [MCP(Model Context Protocol):标准化的工具协议层](#MCP(Model Context Protocol):标准化的工具协议层)
- Skills:可复用的能力包
- [Plugins:Skills 的发布与分发](#Plugins:Skills 的发布与分发)
- 三层架构全景图
- [实战:从零构建一个 Skill](#实战:从零构建一个 Skill)
- 总结
1. 为什么需要"工具调用"?
大语言模型(LLM)本质上是一个文本预测器------给它一段上文,它预测下文。这让它在对话、写作、推理等方面表现出色,但也带来了三个根本性的局限:
| 局限 | 表现 |
|---|---|
| 知识截止 | 训练数据有固定的时间窗口,无法感知训练后发生的事件 |
| 无状态 | 每次推理都是"无记忆"的,无法访问实时数据或外部系统 |
| 纯文本 | 只能产出文本,无法真正执行操作:读文件、调 API、操作浏览器 |
解决这些问题的思路非常直接:让模型能够"调用工具"。
这就是 Function Calling、MCP、Skills 三条技术线共同指向的目标。它们分别在不同的抽象层级上解决同一个问题------如何让 LLM 安全、高效、可扩展地与外部世界交互。
2. Function Calling:大模型的手与脚
2.1 核心机制
Function Calling 是 LLM 与外部工具交互的底层原语。它的工作方式出奇的简单:
用户输入 + 工具定义列表 → LLM → "我要调用这个工具,参数是......" → 执行工具 → 结果返回 LLM → 最终回答
具体来说,整个流程分为以下步骤:
- 定义工具 Schema:用 JSON Schema 描述每个函数的名称、用途、参数类型
- 注入上下文:将工具定义连同用户消息一起发送给模型
- 模型决策:模型返回的不是工具执行结果,而是一个"调用意图"------包含函数名和参数
- 宿主执行:由宿主程序(而非模型)真正执行这个函数
- 结果回传:将执行结果作为新的上下文再次发送给模型
- 模型生成:模型综合所有信息,生成最终回复
2.2 一个最小示例
以 OpenAI 的 API 为例,定义一个天气查询工具:
json
{
"name": "get_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如 'Beijing'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
当用户问"北京现在多少度?",模型会返回:
json
{
"role": "assistant",
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Beijing\", \"unit\": \"celsius\"}"
}
}]
}
注意关键点:模型不执行函数,它只是表达了"我想调用这个函数"的意图。真正调用 API 获取天气数据的是你的代码。
2.3 Function Calling 的设计哲学
理解这几个设计原则,你就能理解 Function Calling 的设计哲学:
- 模型只管决策,不管执行------这保证了安全性:模型不可能意外删除你的文件
- Schema 即契约------工具定义是模型与宿主之间的接口契约,双方通过 JSON Schema 对齐预期
- 多轮对话原生支持------每次工具调用都是一轮独立的对话,结果回传后模型可以决定继续调用或给出最终答案
- 并行调用------模型可以一次性返回多个 tool_calls,宿主可以并行执行后统一回传
2.4 主流模型的 Function Calling 实现
| 厂商 | 机制名称 | 特点 |
|---|---|---|
| OpenAI | Tools / Function Calling | 最早的标准化实现,支持并行调用、strict mode |
| Anthropic | Tool Use | 类似的 JSON Schema 定义方式,强调安全边界 |
| Google Gemini | Function Calling | 与 OpenAI 接口高度兼容 |
| 开源模型 | 通过 system prompt + 输出解析实现 | 依赖 prompt engineering,稳定性不如商业方案 |
3. MCP(Model Context Protocol):标准化的工具协议层
3.1 Function Calling 的痛点
Function Calling 解决了"能调用"的问题,但没有解决以下问题:
- 每个 AI 应用都要重复定义工具------如果你有三个不同的 AI 客户端,同一个"查天气"工具可能要写三遍
- 工具实现与 AI 应用强耦合------工具的代码和 AI 应用的代码混在一起,难以独立迭代
- 没有标准化的发现机制------模型不知道有哪些工具可用,需要开发者手动注入
- 没有统一的传输层------工具通过 stdin/stdout、HTTP、WebSocket 还是其他方式通信?各自为政
MCP(Model Context Protocol)正是为了解决这些问题而生的。
3.2 MCP 是什么
MCP 是由 Anthropic 提出的开放协议 ,定义了 AI 模型与外部工具/数据源之间的标准化通信方式。它的核心思想可以概括为:用统一的协议,让任何 AI 客户端都能连接到任何工具服务器。
MCP 架构包含三个角色:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ MCP Host │ ←→ │ MCP Client │ ←→ │ MCP Server │
│ (AI 应用) │ │ (协议客户端) │ │ (工具服务器) │
└──────────────┘ └──────────────┘ └──────────────┘
- MCP Host:AI 应用本身(如 Codex、Claude Desktop)
- MCP Client:与 Server 建立一对一连接的协议客户端
- MCP Server:暴露特定能力的轻量服务,提供 Tools、Resources、Prompts 三类能力
3.3 MCP Server 的三种能力
每个 MCP Server 可以暴露三类能力:
Tools(工具)
模型可控的、可执行的操作。类似于 Function Calling 中的函数定义,但通过标准化协议暴露:
json
{
"name": "read_file",
"description": "读取文件内容",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string" }
}
}
}
Resources(资源)
模型可读取的上下文数据------数据库 Schema、API 文档、系统配置等。Resources 允许 Server 向模型主动暴露信息,而不需要模型先猜测有什么可用:
Resource URI: mcp://my-server/docs/api-reference
Resource URI: mcp://my-server/schemas/user-table
Prompts(提示模板)
预定义的、可复用的 prompt 模板,帮助用户快速开始特定任务。
3.4 MCP 的传输层
MCP 支持两种传输方式:
| 传输方式 | 描述 | 适用场景 |
|---|---|---|
| stdio | 通过标准输入输出通信,Server 作为子进程运行 | 本地工具、CLI 工具 |
| HTTP + SSE | 通过 HTTP 请求和 Server-Sent Events 通信 | 远程服务、云端工具 |
一个典型的 MCP Server 配置示例(.mcp.json):
json
{
"mcpServers": {
"cloudflare-api": {
"type": "http",
"url": "https://mcp.cloudflare.com/mcp"
}
}
}
3.5 MCP 与 Function Calling 的关系
| 维度 | Function Calling | MCP |
|---|---|---|
| 定位 | LLM 调用工具的机制 | 工具服务的标准化协议 |
| 抽象层级 | API 级别 | 架构级别 |
| 解决什么问题 | "模型如何表达调用意图" | "工具如何被发现、连接和管理" |
| 关系 | MCP 暴露的工具最终通过 Function Calling 被模型调用 | Function Calling 是 MCP 工具被调用的底层机制 |
简单来说:MCP 定义了工具"是什么"和"怎么连",Function Calling 定义了模型"怎么用"这些工具。
4. Skills:可复用的能力包
4.1 从工具到技能------为什么需要 Skills?
MCP 解决了工具的标准化连接问题,但仍然留下了一个空白:领域知识和复杂工作流如何被复用?
举个例子:你需要让 AI 帮你处理一个复杂的 Word 文档------包含追踪修订、批注、格式保留。这不仅仅是一个"调用 docx 库"的工具调用,而是一个包含多个步骤、多种判断、大量领域知识的工作流。
这就是 Skills 解决的问题:将领域知识、工作流指引、可执行脚本和资源打包成一个可复用的能力单元。
4.2 Skill 的解剖结构
一个 Skill 是一个包含 SKILL.md 文件的自包含目录:
imagegen/ # Skill 名称
├── SKILL.md # 核心:触发条件 + 工作流指引
│ ├── YAML frontmatter # name + description(触发机制)
│ └── Markdown body # 使用指引(触发后才加载)
├── agents/ # UI 元数据(推荐)
│ └── openai.yaml # 展示名称、描述、默认 prompt
├── scripts/ # 可执行脚本(可选)
│ └── generate_image.py
├── references/ # 参考文档(可选)
│ └── api_docs.md
└── assets/ # 模板、图标等输出资源(可选)
└── logo.png
4.3 渐进式加载:Skills 的上下文管理哲学
Skills 最精妙的设计在于三级渐进式加载机制:
第 1 级:元数据(name + description)
↓ 始终在上下文,约 100 词,决定是否触发
第 2 级:SKILL.md 正文
↓ 仅在 Skill 触发后加载,< 5000 词
第 3 级:捆綁资源(scripts/ references/ assets/)
↓ 按需加载,无上限
这种设计解决了 AI Agent 面临的一个核心矛盾:上下文窗口是公共资源,但我们又需要给模型足够的领域知识。通过渐进式加载,Skills 保持了下文窗口的"轻盈",同时在需要时提供深度支持。
4.4 SKILL.md 的触发机制
一个 Skill 的 SKILL.md 包含两部分的 YAML frontmatter:
yaml
---
name: imagegen
description: >
Generate or edit raster images when the task benefits from AI-created
bitmap visuals such as photos, illustrations, textures, sprites, mockups,
or transparent-background cutouts. Use when Codex should create a brand-new
image, transform an existing image, or derive visual variants from references.
---
- name:Skill 的唯一标识
- description:触发条件------这是 Codex 判断"什么时候该用这个 Skill"的核心依据
关键洞察:description 写得好不好,直接决定了 Skill 是否能在恰当的时机被触发。它需要同时包含"这个 Skill 做什么"和"什么时候用它"。
4.5 Skill vs Function Calling vs MCP
| 维度 | Function Calling | MCP | Skills |
|---|---|---|---|
| 抽象层级 | API 原语 | 通信协议 | 能力包 |
| 核心价值 | 让模型调用外部函数 | 标准化工具连接 | 复用领域知识和工作流 |
| 包含什么 | JSON Schema 定义 | 工具、资源、提示模板 | 指引文档、脚本、资源、资产 |
| 如何触发 | 模型自主决策 | Host 发现并连接 | 元数据匹配触发 |
| 可分发 | 需随代码打包 | Server 独立部署 | 目录即包,可 Git 分发 |
5. Plugins:Skills 的发布与分发
5.1 Plugin 是什么
Plugin 是 Skills 的容器和分发单元。如果说 Skill 是"功能模块",Plugin 就是"安装包"。一个 Plugin 可以包含:
my-plugin/
├── .codex-plugin/
│ └── plugin.json # 插件元数据
├── skills/ # 包含的 Skills
│ └── my-skill/
│ └── SKILL.md
├── .mcp.json # 包含的 MCP Server 配置
├── .app.json # 包含的应用配置
├── scripts/ # 插件级脚本
├── hooks/ # 生命周期钩子
└── assets/ # 插件级资源
5.2 Plugin 的 marketplace 机制
Plugin 通过 marketplace 文件进行分发管理:
json
{
"name": "personal",
"interface": { "displayName": "Personal" },
"plugins": [
{
"name": "my-plugin",
"source": {
"source": "local",
"path": "./plugins/my-plugin"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}
installation: "AVAILABLE"--- 用户可选安装installation: "INSTALLED_BY_DEFAULT"--- 默认预装authentication: "ON_INSTALL"--- 安装时授权authentication: "ON_USE"--- 使用时授权
6. 三层架构全景图
将 Function Calling、MCP 和 Skills 放在一起看,它们构成了 AI Agent 工具调用体系的三个层次:
┌─────────────────────────────────────────────────────┐
│ Skills / Plugins │ ← 能力包层
│ (可复用的领域知识、工作流、资源打包) │ "做什么"
├─────────────────────────────────────────────────────┤
│ MCP(Model Context Protocol) │ ← 协议层
│ (标准化的工具发现、连接、通信协议) │ "怎么连"
├─────────────────────────────────────────────────────┤
│ Function Calling / Tool Use │ ← 机制层
│ (LLM 调用外部工具的底层原语) │ "怎么调"
└─────────────────────────────────────────────────────┘
一个完整的调用链路长这样:
用户请求
→
Skill 触发(元数据匹配)→ 加载 SKILL.md 指引
→
MCP Server 发现工具 → 建立连接
→
Function Calling 生成调用意图 → 宿主执行 → 结果回传
→
模型综合 Skill 指引 + 工具结果 → 生成最终回复
7. 实战:从零构建一个 Skill
让我们通过一个完整示例,理解如何创建一个 Skill。
7.1 场景定义
假设我们要创建一个 code-reviewer Skill:当用户请求代码审查时,它提供结构化的审查流程和 Checklist 指引。
7.2 初始化 Skill
使用 skill-creator 的初始化脚本:
bash
python3 scripts/init_skill.py code-reviewer \
--path ~/.codex/skills \
--resources scripts,references
这会生成:
~/.codex/skills/code-reviewer/
├── SKILL.md
├── agents/
│ └── openai.yaml
├── scripts/
└── references/
7.3 编写 SKILL.md
markdown
---
name: code-reviewer
description: >
Structured code review workflow for pull requests and code changes.
Use when the user asks for a code review, PR review, or requests
feedback on code changes with phrases like "review this", "code
review", "check my code", or "look over this PR".
---
# Code Reviewer
## Review Process
Follow this structured review process for every code review:
1. **Understand the change** --- Read the diff or changed files completely
before forming opinions. Identify the primary goal of the change.
2. **Architecture & Design** --- Evaluate whether the change fits the
existing architecture. Flag unnecessary abstractions, tight coupling,
or design decisions that work against the codebase's patterns.
3. **Correctness** --- Look for off-by-one errors, null/undefined handling,
race conditions, and edge cases.
4. **Security** --- Check for injection risks, exposed secrets, missing
auth checks, and unsafe deserialization.
5. **Performance** --- Flag N+1 queries, unnecessary allocations, blocking
operations in async contexts, and large memory footprints.
6. **Test Coverage** --- Assess whether tests cover the changed behavior,
edge cases, and regression risks.
7. **Maintainability** --- Evaluate naming, comments, function length,
and whether the code is self-explanatory.
## Output Format
Present findings ordered by severity (P0 → P3):
- **P0 (Blocker)**: Must fix before merge --- security, data loss, crashes
- **P1 (High)**: Should fix --- bugs, incorrect behavior
- **P2 (Medium)**: Nice to fix --- code quality, minor performance
- **P3 (Low)**: Optional --- style nits, alternative approaches
Use inline code comments (::code-comment{...}) when referencing specific
lines. End with a summary and risk assessment.
7.4 验证 Skill
bash
python3 scripts/quick_validate.py ~/.codex/skills/code-reviewer
7.5 触发与使用
创建完成后,当用户说"review this PR"或"帮我审查这段代码"时,Codex 会:
- 在 Skill 列表中匹配
code-reviewer的 description - 加载
SKILL.md正文获取审查流程 - 按照 Skill 指引执行结构化的代码审查
7.6 进阶:Skill 的复杂场景
多领域组织
当 Skill 涉及多个领域时,使用 references 按领域拆分:
bigquery-skill/
├── SKILL.md # 概览和各领域导航
└── references/
├── finance.md # 财务相关表结构和查询
├── sales.md # 销售管道和机会数据
└── product.md # 产品使用和功能数据
SKILL.md 中只需要写:
markdown
## Domain-specific schemas
- **Finance metrics**: See [finance.md](references/finance.md)
- **Sales pipeline**: See [sales.md](references/sales.md)
- **Product analytics**: See [product.md](references/product.md)
这样,当用户问财务问题时,只有 finance.md 被加载到上下文。
可执行脚本
当 Skill 需要重复执行确定性的操作时,放入 scripts/:
python
# scripts/analyze_complexity.py
"""分析代码复杂度并生成报告"""
import ast
import sys
from pathlib import Path
def analyze_file(filepath: str) -> dict:
with open(filepath) as f:
tree = ast.parse(f.read())
# ... 复杂度分析逻辑 ...
return report
脚本可以被直接执行(无需加载到上下文),极大节省 token。
8. 总结
| 概念 | 一句话概括 | 解决的核心问题 |
|---|---|---|
| Function Calling | LLM 调用外部工具的 API 原语 | 模型如何表达"我要用工具" |
| MCP | 工具服务的标准化通信协议 | 工具如何被发现、连接和复用 |
| Skills | 领域知识+工作流的可复用能力包 | 复杂任务的知识和流程如何传承 |
| Plugins | Skills 的容器和分发单元 | 能力如何打包、发布和安装 |
三者的关系不是替代,而是逐层递进:Function Calling 提供了"调用的能力";MCP 在这个基础上添加了"标准化的连接";Skills 再往上封装了"可复用的知识"。理解这一体系,你就能更有针对性地设计 AI Agent 的工具策略:遇到新任务时,先问自己------这个问题更适合用 Function Calling 硬编码、用 MCP 暴露服务、还是封装成一个可复用的 Skill?