Claw Code 源码解析(三):Tool、Skill、Slash Command 与扩展系统

Published on
17 mins read

这是这组 Claw Code 源码解析的第三篇。

如果只看表面,这个仓库里有三套很容易让人混淆的机制:

  • slash command
  • tool
  • skill

很多项目会把这三件事揉成一团,最后结果通常是:

  • 用户命令和模型能力混在一起
  • 权限边界不清
  • 系统越来越难扩展

Claw Code 最值得学的地方之一,就是它没有偷懒,而是把这三者拆成了不同层级。更有意思的是,这三层并没有停在“命令和工具的整洁分工”上,而是继续长出了 Plugin、MCP、Worker、Task、Cron、Telemetry 这些更平台化的能力。

所以这篇我会一起回答两个问题:

  1. 为什么 Tool、Skill、Slash Command 要分三层?
  2. 为什么这三层之上会自然长出一套扩展系统?

先给结论:这三者不是同类东西

我会这样理解它们:

  • Slash Command:面向人类输入的控制面
  • Tool:面向模型调用的 schema 化能力
  • Skill:面向本地复用的提示词包

只要把这三句话记住,后面的很多实现都会突然变得合理。


Slash Command:给人类的控制面

slash command 最像传统 CLI 思维在对话环境里的延伸。它解决的是:

  • 人类想直接控制某个行为
  • 不想把这个行为交给模型自由判断
  • 需要明确、短路径、可记忆的操作方式

这类能力通常包括:

  • 查看或切换状态
  • 触发特定管理动作
  • 查询技能、配置、上下文信息
  • 显式执行某个操作,而不是让模型自己决定

slash command 的关键词是:

  • 明确
  • 可预测
  • 面向人类

它不是为了让模型调用,所以不需要把自己包装成一套模型可消费的 schema。


Tool:给模型的能力面

tool 就完全不同了。

模型调用工具,核心不是“像人类那样记住命令”,而是:

  • 看到结构化工具定义
  • 理解工具名称和用途
  • 生成符合 schema 的输入
  • 在多轮 loop 中使用结果继续推理

所以 tool 的设计重点会落在:

  • 名称
  • 描述
  • 输入 schema
  • 输出处理
  • 执行桥接

这也是为什么 tool 会被做成正式注册的能力面,而不是简单的“把内部函数暴露给模型”。


Skill:给本地复用的提示词包

skill 又是第三种东西。

它不是命令,也不是工具。它更像一包可复用的本地能力说明,通常包含:

  • 一组专门化指令
  • 推荐工作流
  • 可能关联某些工具或外部技能
  • 某个任务域的最佳实践

skill 的关键词是:

  • 可复用
  • 本地化
  • 偏提示词与工作流知识

这让它非常适合表达那些“不是代码能力,但又需要被系统稳定引用”的东西。

例如:

  • 某类测试流程
  • 某种写作规范
  • 特定工程任务的操作指南

为什么把三者混在一起会出问题

如果一个系统不做这层拆分,最常见的后果是:

  • 用户命令开始长得像工具
  • 工具开始承担提示词职责
  • 提示词包又偷偷在模拟命令

最后你会得到一个很难解释的系统:

  • 用户不知道应该输入命令、描述任务,还是触发技能
  • 模型不知道哪些东西它能调、哪些只是人类入口
  • 权限和可观测性也会变模糊

Claw Code 的做法,本质上是在避免这类塌陷。


GlobalToolRegistry:统一工具注册中心

工具机制里最关键的一层,是统一注册中心。

这意味着工具不是零散挂在各个模块里,而是会被收束到同一个 registry 里。这样带来的收益很实际:

  • 模型看到的是统一工具面
  • 内置工具、插件工具、运行时工具能走同一条注入路径
  • 权限、白名单、可观测性都能在同一层生效

如果没有 registry,工具系统很快就会变成“哪里需要哪里接一个函数”。短期快,长期一定乱。


--allowedTools:工具面裁剪比“有没有工具”更重要

工具系统真正成熟的标志,不是工具数量多,而是你能不能控制 这一轮究竟给模型什么工具面

--allowedTools 这类能力的意义就在这里。它说明工具不是“固定全开”,而是:

  • 可按场景裁剪
  • 可按权限限制
  • 可按任务最小化暴露

这对 agent 系统非常关键,因为真正的问题通常不是“模型不会用工具”,而是“给它太多工具后行为变得不可控”。

所以工具白名单和别名规范化,不是小细节,而是 agent 安全边界的一部分。


Skill 的查找路径,说明它是本地知识包而不是远程能力

从实现意图看,Skill 明显被视为一种 本地可发现、可复用的提示词资产

这类机制通常会去几个地方找:

  • 项目本地技能目录
  • 用户级技能目录
  • 系统级技能目录

这个查找策略很有意思,因为它本质上是在做一套“本地知识覆盖链”:

  • 项目可以有自己的技能
  • 用户可以有全局技能
  • 系统可以带默认技能

这和 tool 完全不一样。tool 解决的是执行能力,skill 解决的是任务知识与操作范式。


/skills 命令和 Skill 工具为什么要同时存在

这又是一个很容易被误解的点。

表面看,它们都和 skill 有关,但服务对象不同:

/skills

这是给人看的。用户想知道:

  • 当前有哪些 skill
  • 某个 skill 叫什么
  • 它大概做什么

Skill 工具

这是给模型用的。模型需要:

  • 在合适的时候发现某个 skill
  • 读取 skill 内容
  • 把 skill 作为当前任务的辅助知识

所以同样是“技能”,对人和对模型暴露的接口完全可以不一样。这一点处理得越清楚,系统越稳。


classify_skills_slash_command() 这种设计为什么聪明

从命名就能看出来,这类逻辑不是简单地“有个 /skills 命令”,而是在判断:

  • 当前输入是不是技能相关的人类指令
  • 应该走 slash command 还是走普通对话路径

这说明 slash command 并不是随便插在 REPL 上的糖,而是被认真纳入输入分类体系里。

这类设计的收益在于:

  • 控制面更稳定
  • 人类入口不依赖模型猜测
  • 系统可以对某些高确定性命令做短路径处理

ToolSearch:模型也需要“找工具的工具”

这是整个设计里一个很有意思的信号。

当系统工具面足够大时,问题已经不再是“有没有工具”,而是:

  • 模型知不知道该用哪个
  • 它能不能在当前任务里快速发现相关工具

ToolSearch 这种机制本质上是在承认一件事:工具面过大之后,发现成本本身也成了问题。

这说明 Claw Code 并不是把工具系统当成静态列表,而是在考虑更复杂的 agent 使用场景。


为什么三层机制往上,会自然长出 Plugin 和 MCP

当一个系统已经把:

  • 人类控制面
  • 模型能力面
  • 本地知识资产

这三层切开之后,下一步自然会遇到一个问题:外部能力要接到哪里去?

这时就会很自然地长出两种机制:

  • Plugin:显式建模的本地或半本地扩展包
  • MCP:把外部服务能力接进统一工具面

这两者本质上都不是“另起炉灶”,而是在往同一个 tool plane 上继续加能力。


Plugin:它不是脚本目录,而是正式 manifest

Claw Code 里的插件系统最值得注意的,不是“支持插件”这件事本身,而是它支持的方式非常正式。

manifest 里不仅会声明:

  • 名称
  • 版本
  • 描述

还会声明:

  • 权限
  • default enabled 状态
  • hooks
  • lifecycle
  • tools
  • commands

这意味着插件可以同时扩展:

  1. 运行时行为
  2. 工具能力面
  3. 命令面

换句话说,插件并不是边缘脚本,而是第一层扩展子系统。


Hooks 与 Lifecycle:插件可以参与运行时事件

更进一步,插件不只是“给用户加个新命令”。

当系统允许插件接入:

  • 初始化
  • 关闭
  • tool 使用前
  • tool 使用后
  • tool 失败后

这些事件时,插件就已经开始参与运行时生命周期了。

这带来的价值非常实际:

  • 审计
  • 监控
  • 前置检查
  • 外部通知
  • 资源清理

一旦一个 CLI 开始认真对待这些事件,它就已经在往平台走,而不再只是本地终端工具。


MCP:把外部服务能力收束回统一工具面

MCP 在这套结构里非常合理,因为它正好补上了“外部服务能力如何并入系统”的问题。

它的重要性不只是“多支持一种协议”,而是让外部服务继续表现为:

  • 模型可理解的工具
  • 系统可管理的能力对象

这样一来,本地工具、插件工具、外部服务工具就能在同一个平面里被发现、注册、裁剪和执行。

这比为每种外部集成都单独开旁路成熟很多。


Worker、Task、Team、Cron:系统开始考虑无人值守执行

继续顺着这条逻辑往前走,就会自然长出更重的 orchestration 能力。

Task

task 的意义是:系统开始显式建模“任务”本身,而不只是当前对话回合。

Worker

worker 表明系统开始关心某些工作能不能在不同执行环境、甚至脱离当前交互界面完成。

Team

team 的出现说明系统已经在往多 agent 协调视角靠近,而不是只处理单一 agent turn。

Cron

cron 则意味着系统已经开始考虑周期任务和调度,而不只是一次性对话。

这些能力放在一起时,你会发现方向已经非常明确:它正在从一个会话工具,长成一个 agent orchestration substrate。


为什么这些能力不是外挂“高级功能包”

一个常见误区是觉得 Worker、Task、Cron 这种能力应该放在系统最外层,当成高级包。

但如果你真的往下想,会发现它们最终都要依赖同一套基础设施:

  • session
  • tools
  • permissions
  • config
  • provider 通信

既然如此,把它们纳入 runtime / tools 的正式边界,反而是更干净的做法。

Claw Code 的选择说明,它把这些能力视为 agent 系统的自然延长线,而不是额外的 fancy feature。


api crate、Streaming、Prompt Cache、Telemetry 为什么也必须一起看

只看工具和扩展,还不足以解释整套系统为什么像平台。真正把它补完整的,是协议层和可观测性层。

api crate 的存在说明,provider 接入在这里不是“发个 HTTP 请求”这么简单。它至少要处理:

  • provider abstraction
  • structured messages
  • streaming
  • auth / OAuth
  • prompt cache
  • usage event

其中最关键的是 streaming。

在 agent runtime 里,streaming 的价值远不只是让文字一点点出现。它还意味着系统可以逐步接收:

  • text delta
  • tool use 片段
  • usage 更新
  • 其他结构化事件

这对工具调用 loop 非常关键,因为工具输入往往不是一次性生成完的。

Prompt Cache 则补上了长会话成本和上下文复用的问题;Telemetry 则把运行状态从字符串日志升级成结构化事件,方便监控、回放和诊断。

所以平台化不是只靠 Plugin 和 MCP 撑起来的,而是 工具面 + 扩展面 + 协议层 + 可观测性层 一起长出来的。


为什么这种三层拆分加扩展系统值得借鉴

如果只站在“功能能跑就行”的角度,这种拆分看起来很重。但一旦系统开始变复杂,你会发现它其实是在帮你保住三个边界:

  1. 人类控制面 不被模型能力污染
  2. 模型能力面 不被提示词资产污染
  3. 提示词资产 不被命令系统污染

这三个边界一旦还在,后续无论加:

  • 新工具
  • 新技能
  • 新插件
  • 新命令
  • 新外部服务接入
  • 新任务编排能力

系统都还能继续长。


小结

  • Slash Command 是人类控制面,Tool 是模型能力面,Skill 是本地知识资产
  • GlobalToolRegistry--allowedToolsToolSearch 说明工具面已经被正式建模
  • Plugin 和 MCP 把扩展能力继续收束进统一能力面
  • Worker、Task、Team、Cron 说明系统开始考虑无人值守和编排
  • Streaming、Prompt Cache、Telemetry 让这套系统更像平台而不是命令行糖衣

下一篇单独看这套系统里最有启发性的一部分:上下文预算、prompt cache 和历史压缩。

Claw Code 源码解析(四):Agent 如何管理上下文与压缩历史