选择 Agent、Command 还是 Skill,取决于任务是否需要隔离上下文、如何被触发以及复用程度。核心区别在于:Agent 拥有独立进程和内存,用于复杂自主任务;Command 是用户显式触发的入口;Skill 是可被自动或手动调用的内联知识模块。本文将基于仓库中的实际文件与配置,全面解析这三种机制的差异与协作模式。

Agent vs Command vs Skill:Claude Code 三种扩展机制全面对比

Claude Code 提供了多种扩展能力,其中 Subagents(子代理)、Commands(命令)和 Skills(技能)是三种最核心的扩展机制。理解它们的区别、联系与适用场景,是从随意提示(Vibe Coding)转向结构化代理工程(Agentic Engineering)的关键一步。本文将基于项目仓库中的实际配置文件,对这三者进行全方位对比。

1. 三种机制的本质定义

在深入对比前,先明确它们各自是什么:

  • Agent (子代理): 定义在 .claude/agents/ 目录下的独立智能体。它拥有独立的上下文窗口,可以自主执行多步骤、需要探索和决策的复杂任务,支持工具限制、模型覆盖、持久化内存和技能预加载。
  • Command (命令): 定义在 .claude/commands/ 目录下的斜杠命令。它是用户在对话中通过 /command-name 显式触发的工作流入口,本身不运行在独立上下文中,非常适合用于编排和调度其他机制(如调用 Agent 或 Skill)。
  • Skill (技能): 定义在 .claude/skills/<name>/SKILL.md 文件中的可复用程序。它内联在主对话上下文中,既允许用户通过 /skill-name 调用,也允许 Claude 根据用户意图自动调用,是注入领域知识和标准化操作的理想载体。

2. 10 维核心对比表格

下表从 10 个关键维度总结了三种机制的核心差异,数据直接来源于项目的实际配置与文档。

维度 Agent (子代理) Command (命令) Skill (技能)
Location (存储位置) .claude/agents/<name>.md .claude/commands/<name>.md .claude/skills/<name>/SKILL.md
Context (执行上下文) 独立的子代理进程 内联(主对话) 内联(主对话)
User-invocable (用户可调用) 否 — 无 / 菜单,由 Claude 通过 Agent 工具调用 是 — /command-name 是 — /skill-name (除非 user-invocable: false)
Auto-invoked by Claude (自动调用) 是 — 通过 description 字段匹配意图 是 — 通过 description 字段匹配意图 (除非 disable-model-invocation: true)
Accepts arguments (接受参数) 通过 prompt 参数 $ARGUMENTS, $0, $1 $ARGUMENTS, $0, $1
Dynamic context injection (动态上下文注入) 是 — !`command` 是 — !`command`
Own context window (独立上下文窗口) 是 — 隔离 否 — 共享主上下文 否 — 共享主上下文 (除非设置 context: fork)
Model override (模型覆盖) model: frontmatter model: frontmatter model: frontmatter
Tool restrictions (工具限制) tools: / disallowedTools: allowed-tools: allowed-tools:
Memory (内存) memory: frontmatter (user/project/local)
Can preload skills (预加载技能) 是 — skills: frontmatter

3. 如何选择:应用场景与指南

理解定义和差异后,选择哪一种取决于你的具体需求。

选择 Agent 当:

  • 任务复杂、多步骤且需要自主决策,无需你逐步指导。
  • 需要上下文隔离,防止任务细节污染主对话。
  • 任务需要跨会话的记忆(如一个学习代码模式的代码审查代理)。
  • 希望为代理预加载特定领域知识(通过 skills: 字段),而不污染主上下文。
  • 任务适合在后台或 Git Worktree 中运行。
  • 需要施加特定的工具限制或权限模式(如 acceptEdits, plan)。

例如,仓库中的 time-agent 虽然简单,但演示了其独立进程、可指定模型(haiku)和工具限制(allowedTools)的特性。

选择 Command 当:

  • 需要为用户提供一个明确的、用户触发的入口来启动一个工作流。
  • 该工作流涉及编排其他代理或技能。
  • 希望保持主上下文精简,直到用户主动触发时才注入命令内容。

例如,你可以创建一个 /deploy 命令,它内部依次调用测试 Agent、构建 Skill 和通知 Command。

选择 Skill 当:

  • 希望 Claude 能根据用户意图自动匹配并调用,技能描述被注入上下文用于语义匹配。
  • 该任务是可复用的过程,可以从多个地方(命令、其他代理或 Claude 自身)调用。
  • 需要将领域知识“烘焙”到特定代理中(通过 Agent 的 skills: 字段)。

例如,time-skilldescription 字段使其能被 Claude 自动调用,同时用户也可以输入 /time-skill 手动触发。

4. 实战示例:“现在几点了?”的三种实现

仓库中为同一个任务“显示当前巴基斯坦标准时间”提供了三种机制的实现文件。这完美展示了它们的差异与调用逻辑。

  • .claude/commands/time-command.md: 定义了命令。用户必须输入 /time-command 才能执行。它永远不会被自动调用。
  • .claude/agents/time-agent.md: 定义了子代理。其 description (“Use this agent to display the current time in Pakistan Standard Time”) 使 Claude 可能自动匹配并生成它,但它会启动一个独立的上下文进程。
  • .claude/skills/time-skill/SKILL.md: 定义了技能。其 description (“Display the current time in Pakistan Standard Time…”) 同样使其能被自动调用,且因为它运行在主上下文中,是执行简单 bash 命令的最轻量方式。

当用户直接输入“现在几点了?”时,Claude 的自动调用优先级如下:

1. Skill (内联,无上下文开销)     ← 最优先
2. Agent (独立上下文,自主)       ← 如果技能不可用或任务复杂,则使用
3. Command (从不 — 需要显式 /)   ← 只有用户输入 /time-command 才会执行

这个顺序体现了“最轻量满足需求”的原则。如果给 time-skill 设置 disable-model-invocation: true,那么 Claude 就无法自动调用它,可能会转而使用 time-agent。如果两者都被禁用,Claude 只能直接运行 bash 命令。

5. 编排工作流:Command → Agent → Skill 架构

这三种机制可以组合成强大的编排工作流,形成分层架构:

用户触发 /command

Command 编排工作流

Command 调用 Agent (独立上下文,自主执行)

Agent 使用预加载的 Skill (领域知识)

Command 调用 Skill (内联,用于输出生成)

这种模式实现了关注点分离:Command 作为协调者,Agent 作为自主执行者,Skill 作为可复用的知识单元。

FAQ

Q: 为什么 Agent 不会出现在 / 菜单里? A: 根据设计,Agent 主要供 Claude 自动调用,或通过其他机制(如 Command 或另一个 Agent)以编程方式调用,而不是直接供用户从菜单选择。这是为了保持用户界面的简洁。

Q: 我可以让一个 Skill 在独立的上下文中运行吗? A: 可以。在 Skill 的 Frontmatter 中设置 context: fork 字段,该技能就会在一个 fork 出的独立上下文中执行,执行完毕后再将结果合并回主对话。

Q: 如何防止 Claude 自动调用某个我定义的机制? A: 对于 Agent,可以修改其 description 字段,使其不包含鼓励自动调用的词语(如“PROACTIVELY”)。对于 Skill,在 Frontmatter 中设置 disable-model-invocation: true。Command 本身就不能被自动调用。

Q: Command 和 Skill 都能接受 $ARGUMENTS 参数,使用上有何区别? A: 参数语法相同,关键区别在于触发方式和上下文。Command 必须由用户显式以 /command arg 形式触发。Skill 则既可由用户显式触发,也可能被 Claude 自动调用并自动传递推断出的参数。