🔧 什么是 Skill?
在 AI 代理的上下文中, skill (也称为 tool or function) 是一个打包的、可重用的能力,LLM 可以调用它与上下文窗口外的世界交互。Skills 弥合了语言理解与现实世界操作之间的差距。
每个 skill 有四个组成部分:
- Name — 模型用于引用该 skill 的唯一标识符(例如,
web_search) - Description — 用于解释该 skill 做什么以及何时使用的自然语言文本; LLM 读取此内容以决定何时调用该 skill
- 输入模式(Input schema) — 参数的结构化定义(JSON Schema);模型根据任务填写这些参数
- Implementation — 实际运行的代码:API 调用、数据库查询、shell 命令或任何其他操作
示例: A get_weather skill 的描述是 "Fetches current weather for a city", 接受 { "city": "string" } 作为输入,并调用天气 API。LLM 永远看不到 API 密钥或 HTTP 调用——它只接收作为文本返回的结果。
⚙️ 工具调用的工作原理
当代理可以访问 skills 时,LLM 并不直接调用它们——它会 requests 通过生成结构化输出发起一次调用。运行时(你的应用代码)执行实际调用并返回结果。以下是完整循环:
- 工具注册(Tool registration) — 你的代码将可用的 skills 列表及其描述和 schema 发送给模型
- 模型推理(Model reasoning) — LLM 读取任务和可用工具,决定调用哪个工具以及使用哪些参数
- 工具调用请求(Tool call request) — 模型输出一个结构化的 "tool call" 消息(例如,
{ "tool": "web_search", "args": { "query": "current Gemini models" } }) - Execution — 你的运行时代码调用实际的函数、API 或服务
- 结果注入(Result injection) — 结果被添加回对话上下文
- 持续推理(Continued reasoning) — 模型读取结果,要么调用另一个工具、要么提出后续问题、要么生成最终答案
此循环在一次交互中可能重复多次。一个复杂的自治任务可能会依次调用 10–20 个工具,然后才产生最终响应。
📊 Skills vs Plugins vs MCP Servers
术语迅速演变。以下是这些概念的历史关联:
| Era | Name | 它的工作方式 | Status |
|---|---|---|---|
| 2023 | ChatGPT Plugins | 具有 OpenAPI 规范的用户可安装扩展;ChatGPT 通过 HTTP 调用它们 | 已弃用(被 GPTs + tools 取代) |
| 2023 | OpenAI Function Calling | API 级别的 JSON Schema 定义;模型输出结构化的函数调用,你的代码执行它 | Active — 重命名为 "tool use" |
| 2023– | Tool Use / Skills | 与 function calling 相同;Anthropic 首创了 "tool use",其他人使用 "skills" 或 "functions" 术语 | Active — 当前标准 |
| 2024– | MCP Servers | 标准化协议(Anthropic,2024 年 12 月)用于打包和分发工具服务器;任何 MCP 客户端都可以连接到任何 MCP 服务器 | Active — 不断增长的生态系统 |
关键区别: 传统的 tool use 在你的应用代码中内联定义。 MCP 服务器是独立进程,通过标准化协议公开工具——使它们可在不同的 AI 客户端(Claude Desktop、Cursor、VS Code、自定义代理)之间重用。 阅读更多: 什么是 MCP.
🗂️ 工具类别与风险等级
并非所有工具承担的风险相同。按风险级别分组有助于为你的代理定义 行动空间(action space) 以及在何处添加人工介入(human-in-the-loop)检查点:
| Category | Examples | Risk | Recommendation |
|---|---|---|---|
| Read-only | web_search, read_file, get_weather, list_dir | Low | 允许自主;记录所有调用 |
| 写(本地) | write_file, create_dir, edit_code | Medium | 限制到沙箱化的工作目录 |
| 网络 / 外部(Network / External) | send_email, post_to_slack, call_api | 中–高(Medium–High) | 对不可逆的发送操作需要 HITL 批准 |
| OS / Shell | run_command, execute_script, install_package | High | 限制在容器化环境中;需要 HITL |
| Destructive | delete_file, drop_table, revoke_access | Critical | 始终要求明确的人工确认 |
🏗️ 良好 Skill 的构成
设计 skill 的最重要决策是其 description. LLM 读取描述以决定调用哪个工具——描述写得不好会导致错误的工具选择、漏掉调用或参数模糊。
有效描述的要素
- 明确说明何时使用它 — 比起 "Searches the web","当用户询问实时数据或时事时使用此工具" 更好
- 说明它不做什么 — "不返回 30 天以前的历史数据"
- 描述输出格式 — "返回包含 title、url 和 snippet 字段的搜索结果 JSON 数组"
- 保持在 200 字以内 — 超过 ~200 个标记的描述在注册许多工具时会压垮模型的注意力
模式(Schema)设计原则
- 必需 vs 可选 — 仅在真正必要时标记字段为必需;具有默认值的可选字段可以减少模型错误
- 对受限值使用枚举 —
"format": {"enum": ["json", "markdown", "text"]}可防止幻觉值 - 偏好幂等工具 — 可以安全重试的工具(读取、查找)比一次性操作(发送、删除)更安全
- 返回结构化数据 — 与非结构化文本块相比,JSON 响应更便于模型推理
💡 真实世界示例
| Skill 名称 | 它的功能(What it does) | 关键参数(Key arguments) | Risk |
|---|---|---|---|
web_search | 查询搜索引擎并返回顶级结果 | query: string, num_results?: number | Low |
read_file | 从工作区目录读取文件 | path: string, offset?: number, limit?: number | Low |
write_file | 创建或覆盖文件 | path: string, content: string | Medium |
run_tests | 执行项目测试并返回结果 | filter?: string | Medium |
browser_screenshot | 导航到 URL 并返回截图 | url: string | Medium |
send_email | 向一个或多个收件人发送电子邮件 | to: string[], subject: string, body: string | High — 始终需要 HITL |
run_shell | 执行任意 shell 命令 | command: string, cwd?: string | High — 需要沙箱 |
✅ 最佳实践
最小权限
仅为每个代理授予其完成特定任务所需的工具。一个回答常见问题的代理 不需要 write_file or send_email. 操作空间越小, 如果代理被操纵时的影响范围越小。参见: 过度自治.
审计所有工具调用
记录每次工具调用及其参数和结果。这使调试、成本归因和检测异常行为成为可能(例如,代理读取 ~/.ssh/ 而它本应 仅访问项目目录)。像 LangSmith 这样的 AgentOps 平台可以简化此过程。
对不可逆操作进行人工介入(HITL)
任何难以或无法撤销的操作——发送消息、删除数据、进行购买——应在执行前要求明确的人工确认。将 HITL 检查点构建到你的代理运行时,而不仅仅是提示中。提示可以被覆盖;代码不能被覆盖。
当心工具投毒(tool poisoning)
如果你允许代理动态安装或发现 MCP 服务器,恶意服务器可能会注册一个描述中包含隐藏指令的工具。将它们添加到代理注册表之前务必审查工具描述。参见: 工具投毒(Tool Poisoning).
编写清晰的错误返回
当工具失败时,返回一个结构化错误,提供足够的上下文使模型能够恢复或升级——而不是原始的异常堆栈跟踪。示例: { "error": "rate_limited", "retry_after": 5 }. 一个描述良好的错误可以让代理重试、使用备用工具或以友好的方式告知用户。