给模型写一段更完整的指令、安装一个 Skill、注册一个 Tool,经常被笼统地描述成“给 Agent 增加能力”。但这三类变化发生在不同层次。
如果不区分它们,就会出现很多似是而非的设计:把一段角色 Prompt 当成 Agent,把 API 文档当成 Skill,或者以为安装了一个 Skill 就自动获得了访问邮箱和数据库的权限。
这一章先建立三个清晰的定义:
Skill 提供做事的方法,Tool 提供可以执行的动作,Environment 提供动作发生的外部世界。
Skill 是可复用的任务方法
本书把 Skill 定义为:
Skill 是一组可复用的任务知识、操作步骤和配套资源,用来指导 Agent 完成某一类任务。
例如,一个 Pull Request 审查 Skill 可能包含:
代码块说明|层级示意: 这段文本图按缩进和树枝符号阅读,用来展开“Skill 是可复用的任务方法”中的父子关系与归属边界。它描述的是逻辑结构,不要求数据库或服务按同样层级拆分。
PR Review Skill
├── 什么时候使用
├── 需要哪些输入
├── 按什么顺序检查
├── 哪些风险必须报告
├── 报告的固定结构
├── 示例与反例
├── 检查清单
└── 辅助脚本或模板
Skill 的核心价值不是让 Prompt 变长,而是把经过验证的领域方法从临时对话中提取出来,形成可以反复加载、维护和演进的操作知识。
一个 Skill 通常需要回答四个问题:
- 触发条件:什么任务应该使用它,什么任务不应该使用?
- 输入要求:开始执行前必须获得哪些事实、文件或授权?
- 操作方法:任务应该按什么步骤完成,哪些判断不能省略?
- 完成标准:最终要产出什么,怎样检查是否合格?
如果一份 Skill 只有“你是一名资深安全专家,请认真检查代码”,它仍然只是角色 Prompt。真正的 Skill 应该把领域经验落实为可执行的方法和检查标准。
Skill 不等于 Agent
Skill 通常不会独立接收目标、保存任务状态或组织循环。它需要被某个 Runtime 加载到 Agent 的 Context 中,再由 Agent 根据当前任务执行。
可以把两者理解为:
代码块说明|结构示意: 这段文本把“Skill 不等于 Agent”中的关键对象并列出来,帮助读者识别各自责任与边界。它用于概念建模,不代表唯一的产品命名或实现结构。
Agent:当前正在工作的执行者
Skill:执行者在特定任务上采用的方法
同一个 Agent 可以根据任务加载不同 Skill:
代码块说明|层级示意: 这段文本图按缩进和树枝符号阅读,用来展开“Skill 不等于 Agent”中的父子关系与归属边界。它描述的是逻辑结构,不要求数据库或服务按同样层级拆分。
Agent
├── 处理 PR → 加载代码审查 Skill
├── 处理故障 → 加载事故分析 Skill
└── 处理邮件 → 加载邮件沟通 Skill
同一个 Skill 也可以被不同 Agent 使用。Skill 是可复用的知识和流程资产,不必绑定某一个人格、模型或会话。
Skill 是否必须包含 Tool
不一定。判断标准是任务是否需要读取或改变模型之外的环境。
纯认知型 Skill 可以不依赖 Tool。例如用户已经把一段文字完整放进 Context,要求 Agent 按某种框架分析,Skill 只需提供分析方法和输出格式。
一旦任务需要获得外部信息或产生真实副作用,就需要 Tool:
| 任务 | Skill 提供什么 | 需要的 Tool |
|---|---|---|
| 改写用户提供的文案 | 文风规则、检查表 | 可以不需要 |
| 审查代码仓库 | 审查步骤、风险标准 | 读取文件、查看 diff、运行测试 |
| 起草邮件 | 写作规范、邮件模板 | 可以不需要 |
| 发送邮件 | 写作与发送流程 | 查询联系人、发送邮件 |
| 分析数据库性能 | 分析方法、诊断顺序 | 查询 schema、执行只读 SQL |
因此,更准确的关系不是“Skill 里面必须有 Tool”,而是:
Skill 应声明完成任务所依赖的 Tool 和环境前提,Runtime 决定这些 Tool 是否真实存在、当前是否可用以及是否获得授权。
Skill 可以附带脚本,但脚本仍然需要由 Runtime 或某个 Tool 执行。写在 Skill 文档中的 send_email() 示例,不会自动产生邮箱连接和发送权限。
Tool 是结构化的动作接口
Tool 是 Agent 与外部环境之间的动作接口。例如:
代码块说明|结构示意: 这段文本把“Tool 是结构化的动作接口”中的关键对象并列出来,帮助读者识别各自责任与边界。它用于概念建模,不代表唯一的产品命名或实现结构。
read_file(path)
run_test(target)
search_web(query)
query_database(sql)
send_email(to, subject, body)
Tool 不应该只被理解成“一个函数”。对 Agent 来说,Tool 的名称、描述、参数和返回结果共同定义了它的行动空间。
一个完整的 Tool 契约至少包括:
代码块说明|Playbook 参考 Schema: 这段 YAML 把“Tool 是结构化的动作接口”落成可检查的结构化记录,主要字段包括 name、description、input、output、errors。除非正文另有说明,字段名和示例值用于解释设计语义,不是某个外部协议的标准格式。
name: query_database
description: 执行只读 SQL 并返回结构化结果
input:
sql: string
output:
columns: string[]
rows: object[]
row_count: integer
errors:
- permission_denied
- invalid_sql
- timeout
side_effect: none
好的 Tool 设计应该让 Agent 容易判断三件事:
- 什么时候应该调用。
- 调用时必须提供什么。
- 返回结果意味着什么。
如果 Tool 只返回“执行失败,请重试”这样的自然语言,Agent 无法稳定区分权限不足、参数错误和临时网络故障,也就无法选择正确的下一步。
API、CLI、MCP 和 Tool 的关系
Tool 是 Agent 看到的逻辑动作;API、CLI、MCP 或本地函数是实现这个动作的技术方式。
代码块说明|层级示意: 这段文本图按缩进和树枝符号阅读,用来展开“API、CLI、MCP 和 Tool 的关系”中的父子关系与归属边界。它描述的是逻辑结构,不要求数据库或服务按同样层级拆分。
Agent 看到:search_issues(query)
↓
Runtime 可以通过以下方式实现
├── 调用 HTTP API
├── 执行 CLI 命令
├── 调用 MCP Server
└── 调用进程内函数
这一区分让 Agent 的决策与底层集成解耦。Agent 不必知道鉴权 Header 怎样构造,也不必解析一整页 CLI 帮助信息。Runtime 把底层接口包装成语义清晰、返回稳定的 Tool。
MCP 的作用之一,就是提供一套标准协议,让外部系统能够向 Agent Runtime 暴露 Tool、Resource 等能力。但“接入 MCP”仍然不等于“完成 Agent 设计”:它只是扩展了 Agent 可以使用的外部接口。
Environment 是模型之外的真实状态
Environment 是 Tool 读取或改变的对象集合,包括:
- 文件系统和代码仓库
- 浏览器页面
- 数据库与知识库
- 邮箱、日历和任务系统
- 云基础设施
- 机器人所处的物理空间
- 用户和其他 Agent
环境和 Context 不是一回事。数据库中可能有一百万条记录,但只有 Tool 查询返回的几十行进入当前 Context。文件已经被其他进程修改,也只有 Agent 再次读取时才能观察到变化。
因此必须区分:
代码块说明|结构示意: 这段文本把“Environment 是模型之外的真实状态”中的关键对象并列出来,帮助读者识别各自责任与边界。它用于概念建模,不代表唯一的产品命名或实现结构。
Environment:外部世界当前真实是什么样
Observation:Agent 通过 Tool 获得的局部证据
Context:这一轮提供给模型的全部可见信息
Agent 从来不是直接“看见整个环境”。它只能通过有限的观察接口获得一个局部视图。这种局部性是误判、信息过期和并发冲突的重要来源。
权限不属于 Skill
Skill 可以说明“下一步需要发送邮件”,但不能自行授予发送权限。Tool 可以提供 send_email 接口,但不代表任何任务都应该直接调用。
权限更适合由 Runtime 根据下面的信息共同决定:
代码块说明|关系式: 这段表达式把“权限不属于 Skill”压缩成便于比较的组成关系。它是分析模型,不是可执行代码,也不是要求实现者采用的行业标准公式。
是否允许执行
= Agent 身份
+ 当前任务范围
+ Tool 风险等级
+ Environment 策略
+ 用户授权状态
例如读取公开网页可以自动执行;向个人发送草稿可能需要确认;向客户群发邮件则可能要求更高等级的审批。即使三个动作都属于同一个“邮件 Skill”,权限也不应该相同。
这带来一个重要原则:
方法可以被加载,能力必须被注册,权限需要在执行时检查。
组合示例:一个代码审查 Agent
假设目标是“审查 Pull Request #128,并给出是否可以合并的建议”。
系统可能这样组合:
代码块说明|层级示意: 这段文本图按缩进和树枝符号阅读,用来展开“组合示例:一个代码审查 Agent”中的父子关系与归属边界。它描述的是逻辑结构,不要求数据库或服务按同样层级拆分。
Goal
└── 审查 PR #128
Skill
├── 检查变更意图
├── 检查兼容性风险
├── 检查测试覆盖
└── 按严重程度输出发现
Tools
├── get_pull_request
├── read_diff
├── read_file
├── get_ci_status
└── run_test
Environment
├── Git 仓库
├── CI 系统
└── 测试运行环境
Skill 告诉 Agent 审查什么;Tool 让 Agent 能够读取变更和运行测试;Environment 保存 PR、代码和 CI 的真实状态。Runtime 把它们组织起来,并决定哪些动作允许自动执行。
缺少其中任何一层,结果都不同:
- 没有 Skill:Agent 可以读代码,但审查过程可能随意且不完整。
- 没有 Tool:Agent 知道审查方法,却拿不到真实变更。
- 没有 Environment:Tool 只能返回模拟结果,无法形成真实证据。
- 没有 Runtime:方法和接口都存在,却没有系统组织它们完成任务。
本章检查
- Skill 是否描述了可复用的方法,而不只是一个角色设定。
- Skill 是否声明了必要输入、Tool 依赖和完成标准。
- Tool 是否使用领域动作命名,并提供结构化成功与失败结果。
- 是否区分了 Tool 的逻辑契约与 API、CLI、MCP 等实现方式。
- Environment 的变化是否需要重新观察,而不是依赖过期 Context。
- Tool 是否在执行时进行权限和风险检查。