本文中的 ToolFunction 泛指"注册给 AI 应用使用的函数工具",不是某个框架的固定类名;Skill 指采用 SKILL.md 组织的 Agent Skills。
本文中的订单、日志、函数和编号均为虚构的通用示例,不涉及任何公司的真实代码、业务规则、内部接口或数据,也不代表任何实际系统的实现。
1. 先记住一句话
ToolFunction 提供动作,MCP 统一接入,Skill 提供方法;Harness 组织执行,模型提出下一步。
| 概念 | 主要解决什么问题 | 排查下单超时的例子 |
|---|---|---|
| 模型 LLM | 理解问题、分析证据、生成回答或工具调用请求 | 判断还需要查哪段代码、哪份日志 |
| Harness | 组织上下文、调用模型、调度工具、落实运行限制 | 管理整段排查过程与工具调用循环 |
| ToolFunction | 把一项程序能力注册成可调用工具 | 搜索代码、读取文件、查询日志 |
| MCP | 约定应用如何发现和调用服务端能力 | 接入独立部署的日志查询服务 |
| Skill | 打包专业知识、操作步骤及配套资源 | 按"核对现象 → 查代码 → 查日志 → 给结论"排查 |
它们不是互相替代的三代技术。同一个函数可以通过 MCP 暴露,也可以被某个 Skill 指导使用。这里对 Harness 的描述是架构层面的概括,不表示所有产品都采用同一套内部实现。
2. 最核心的关系图
下面展示"应用侧负责工具执行"的常见结构。箭头旁标注的是传递的内容或执行路径。

模型产生工具调用请求,实际程序由应用或服务端执行,再把结果交回模型。这是 Function Calling 的基本交互方式。OpenAI Function Calling
3. ToolFunction:把程序能力交给模型选择
一个普通 Java 方法,并不会因为写好了就自动成为模型能用的工具。应用还需要完成注册和参数映射,让模型知道工具的名称、用途和参数格式。
例如下面的示意方法。logService 是虚构的服务依赖,代码省略了它的定义及框架注册部分,不来自实际项目:
java
public String searchOrderLogs(String orderId) {
// 校验模型提供的订单号,避免无条件执行无效查询。
if (orderId == null || orderId.isBlank()) {
// 将输入问题反馈给调用方,而不是继续访问日志服务。
throw new IllegalArgumentException("orderId 不能为空");
}
// 使用应用已有的日志服务查询;真正执行 Java 的是运行环境。
return logService.searchByOrderId(orderId);
}
这里需要区分三件事:
- 工具实现:上面的 Java 代码,负责真正查询。
- 工具定义:名称、描述、参数 Schema,帮助模型理解怎么使用。
- 工具调用:模型生成的工具名和参数,由运行环境接收并处理。
模型通常不需要看到函数源码。名称也不必与 Java 方法完全一致,可以通过注册关系把 search_order_logs 映射到 searchOrderLogs。函数工具定义与调用
"直接注册的函数"不等于"只能操作本地数据":函数内部完全可以访问远程日志 API。
4. MCP:给能力提供统一的接入方式
MCP 全称 Model Context Protocol。它定义应用与服务端交换能力和上下文的协议,而不是某个模型,也不是特指第三方服务。
它的三个参与者是:
- Host:使用 MCP 的 AI 应用,组织一个或多个 Client。
- Client:应用中负责与 MCP Server 通信的组件。
- Server:提供工具、资源或提示模板的程序。
Server 可以是自己开发的,也可以来自第三方;可以运行在本机,也可以运行在远程。常见传输方式是本机进程间的 stdio 和基于 HTTP 的 Streamable HTTP。因此,MCP 与 HTTP 不是同一层面的替代关系。MCP 架构
对于工具,两个关键操作是:
| MCP 方法 | 用途 | 核心内容 |
|---|---|---|
tools/list |
发现服务端有哪些工具 | 名称、描述、inputSchema 等 |
tools/call |
执行指定工具 | 工具名、arguments |
一个调用可以概括为下面的协议片段。为便于阅读,省略了协议版本相关的必需元数据;这不是可直接发送的完整请求。
json
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "search_order_logs",
"arguments": {
"order_id": "ORDER-001"
}
}
}
tools/call 是应用侧 MCP Client 与 Server 的通信,不是必须交给模型阅读的提示词。模型看到的通常是应用为其整理的工具说明和执行结果。MCP Tools 规范
即使 Server 在本机,只要采用 MCP 接入,仍然要经过这一层协议;使用 stdio 时不必经过网络。协议交换本身不需要调用模型。
5. Skill:不仅是工具清单,更是操作说明
一个 Skill 可以包含专业知识、执行步骤、判断标准,也可以附带脚本和模板。Agent Skills 概述
例如下面的目录,仅用于展示组织方式:
text
order-troubleshooting/
├── SKILL.md # 何时使用、排查步骤、输出要求
├── scripts/
│ └── summarize_logs.py # 可选:整理日志
├── references/
│ └── order-flow.md # 可选:下单业务说明
└── assets/
└── report-template.md # 可选:报告模板
其中 SKILL.md 的操作说明可以是:
先确认订单号、环境和时间范围;再定位下单代码与日志;区分已确认事实和待验证假设;只做只读诊断,不自行修改配置或重启服务。
Skill 的常见加载方式是:先提供名称和简介,选中后加载完整说明,再按需要读取参考资料或执行脚本。具体触发方式由宿主实现。Agent Skills 规范
需要注意:
- 脚本放在 Skill 目录里,不会自动变成一个 Function Calling 工具。
- 运行脚本需要已有的执行能力,例如经过授权的终端工具或专用执行器。
- 脚本不一定只能由这个 Skill 使用,也可以复用公共代码;可移植性取决于依赖是否齐全。
- 如果脚本内部主动调用模型 API,就会产生额外的模型调用;不是因为它叫 Skill 就自动发生。
因此,Toolkit 通常指一组可调用工具,并不等于一组 Skill;一个 Skill 可以指导使用多个 Toolkit 中的工具。
6. 最后到底给模型什么?
以 OpenAI Responses API 为例,最简单的请求可以这样组织。model 使用占位符,工具及订单号均为示例:
json
{
"model": "你的模型ID",
"instructions": "你是只读排查助手。先核对证据,不修改数据。",
"input": "请查一下 ORDER-001 的下单日志,解释超时原因。",
"tools": [
{
"type": "function",
"name": "search_order_logs",
"description": "按订单号查询调用者有权访问的下单日志。",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "待查询的订单号"
}
},
"required": ["order_id"],
"additionalProperties": false
},
"strict": true
}
],
"tool_choice": "auto"
}
这些字段分别表示:
| 内容 | 本例的承载位置 |
|---|---|
| 系统/开发者行为指令 | instructions |
| 用户问题 | 简单字符串形式的 input |
| 工具名称、说明和参数格式 | tools |
| 是否允许模型自行选择工具 | tool_choice |
| 已加载的 Skill 内容 | 由 Harness 放进合适的指令或上下文,不是通用的独立 skill 参数 |
input 也可以使用带 role 的消息和其他输入项来承载历史对话、工具结果等。Chat Completions 则主要使用 messages 中的 system/developer/user 消息;系统提示词和用户提示词没有消失,只是接口组织方式不同。消息角色与指令
如果日志工具来自 MCP,Harness 可以把 tools/list 中的 inputSchema 适配为上例的 parameters,并记录调用时应该路由到哪个 Server。这是一种接入设计,不是 MCP 强制要求的模型 API 格式。
也有平台原生支持 MCP。例如 Responses 支持 type: "mcp",由平台处理相应的服务接入;不能把"所有 MCP 都先转成 function"当成通则。OpenAI MCP 接入
是不是所有工具和 Skill 都一次性给模型?
不一定。要区分三个范围:
- 系统安装或配置了哪些能力。
- 当前 Agent 被允许使用哪些能力。
- 当前请求实际提供了哪些定义和上下文。
应用可以预先提供工具,也可以通过工具搜索等机制按需加载;Skill 也通常采用渐进加载。具体策略由 Harness、配置和所用平台能力共同决定,不是所有客户端都一样。工具搜索与延迟加载
API 层面,文本消息和工具定义可以分字段传递;它们都会影响模型生成,但这不等于把所有内容手工拼成一段用户提示词。
7. 一个完整案例:排查下单超时
假设用户说:"帮我排查 ORDER-001 下单超时,先不要修改代码。"下面是人为简化的串行执行例子,不是任何产品固定的调用次数。
| 步骤 | 谁在处理 | 发生什么 | 对应概念 |
|---|---|---|---|
| 1 | Harness | 按已有配置选定排查 Skill,准备相关说明与工具 | Skill、上下文装配 |
| 2 | 模型,第 1 次 | 请求搜索下单超时相关代码 | 选择 ToolFunction |
| 3 | 工具执行器 | 校验后执行代码搜索,将代码片段返回 | ToolFunction 执行 |
| 4 | 模型,第 2 次 | 结合代码,请求查询订单日志 | 选择 MCP 暴露的工具 |
| 5 | MCP Client/Server | 查询日志,返回"等待数据库连接超时"的记录 | MCP 调用及服务端执行 |
| 6 | 模型,第 3 次 | 汇总证据,给出结论和仍需验证的部分 | 分析与最终回答 |
这条示例链路调用了 3 次模型、执行了 2 次业务工具。不能据此认为"一个工具固定对应一次模型调用":一次模型响应可能请求多个工具,工具也可能内部调用模型。
同样,应用发出了几次 API 请求,也不一定等于平台内部推理了几轮;托管工具流程可能在一次外部请求中完成多步处理。
本例应输出"日志显示等待数据库连接超时",而不能只凭这一条日志断言"数据库已经宕机"。工具提供证据,模型负责分析,最终结论仍需要足够证据支撑。
8. 工具失败了,谁决定下一步?
可以分成两层:
- 预设策略:Harness 或工具执行器按代码规则处理,例如对幂等的只读查询有限重试、到达超时后返回错误。
- 模型决策:错误进入上下文后,模型可能提出更换查询条件、使用其他已获准工具,或说明无法继续。
更换工具不是必然发生,也不能因为工具名称相似就认定能力等价。MCP 负责传递调用和结果,不会自动提供完整的排查与恢复策略。
尤其是写操作,失败响应不一定意味着操作没有生效。重试前需要检查执行结果和幂等条件,不能无限重试。
9. 权限控制不能只靠提示词
"只读排查,不允许修改"可以写进系统指令和 Skill,但工程上还应在执行层落实:
- 只暴露任务所需的工具,并使用最小权限凭证。
- 校验工具名、参数、目标资源和当前用户权限。
- 对写入、删除、发布等动作设置明确的授权规则。
- 限制执行时间、重试次数与总调用预算。
- 记录工具调用与结果摘要,但不要记录凭证或无关敏感数据。
- 把外部日志、文档和工具返回中的指令性文字当作待判断的数据,不能让它们自行扩大权限。
这些是执行层的设计要求,不能由"工具描述说它只读"或"模型承诺不修改"代替。MCP 规范同样强调输入校验、访问控制及对工具元数据的信任边界。MCP 安全要求
10. 怎么选?
作为工程选型的简化判断:
- 只在一个应用里使用某项能力:直接注册 ToolFunction 往往最简单。
- 希望多个兼容客户端复用服务:可以考虑通过 MCP 暴露能力。
- 希望沉淀团队的专业做法:使用 Skill 打包流程、知识和配套资源。
- 希望管理多轮执行、上下文与权限:由已有或自建的 Harness 承担。
它们可以组合,也可以只采用其中一部分。有工具,不代表有操作方法;有操作方法,不代表有执行权限;有协议,不代表有完整的 Agent 运行循环。