[转][译] 从零开始构建 OpenClaw — 第二部分(技能插件系统)

6 阅读11分钟

[转][译] 从零开始构建 OpenClaw — 第一部分(智能体核心)

[转][译] 从零开始构建 OpenClaw — 第二部分(技能插件系统)

[转][译] 从零开始构建 OpenClaw — 第三部分(元技能)

[转][译] 从零开始构建 OpenClaw — 第四部分(工具循环检测)

[转][译] 从零开始构建 OpenClaw — 第五部分(对话压缩)

原文:Building Openclaw from Scratch — Part 2 (Skill Plugin System)

在这个部分,我们将为 openclaw-mini 添加一个可扩展的插件架构,大约需要 200 行 TypeScript 代码

第一部分,我们构建了 openclaw-mini — 一个轻量级、仅通过终端运行的 AI 编码智能体,由 PI SDK 驱动。它配备了 REPL、工具调用循环、网络搜索、文件编辑和上下文感知系统提示词。

在本篇帖子中,我们将添加一些内容,将其从一个功能强大的智能体转变为一个可扩展的平台:一个技能插件系统。技能允许任何人(你、你的团队或社区)仅使用 Markdown 文件来教会智能体新的工作流程。

无需代码。无需 API。无需插件运行时。只需在正确的文件夹中放置一个 .md 文件。

我们在第一部分构建了什么:基础

在深入技能之前,让我们回顾一下 openclaw-mini 已经实现了什么。如果你跟上了第一部分,你构建了一个终端智能体,包括:

一个工具调用式 REPL 循环——任何 AI 智能体的核心。用户输入一个提示词,LLM 用工具调用响应( read , write , edit , bash , web_search , web_fetch ),工具执行,结果反馈给模型,循环继续直到任务完成。

多提供者 LLM 支持——Anthropic、OpenAI、Google、Groq、XAI、Mistral、OpenRouter、Cerebras 和 Ollama。一个代码库,九个提供者。

自定义网络工具 — web_fetch (通过 Mozilla Readability 从 URL 中提取可读内容)和 web_search (Brave 搜索或 Perplexity 集成)叠加在 SDK 内置工具之上。

上下文感知系统提示词 — 自动加载项目文件如 CONTEXT.md 、 CLAUDE.md 、 SOUL.md 和 .github/copilot-instructions.md ,将其注入系统提示词中,以便智能体理解您项目的规范。

会话持久化 — 对话状态存储在 ~/.openclaw-mini/state/sessions/ 中,以便您可以继续未完成的对话。

斜杠命令 — /new 、 /think 、 /model 、 /status 、 /quit 用于从终端控制会话。

这是第一部分之后启动横幅的样子:

┌ openclaw-mini
│ model: anthropic/claude-sonnet-4-20250514
│ workspace: /Users/you/project
│ session: mini-1719432000000
│ context: CLAUDE.md
│ tools: read, bash, edit, write, web_fetch, web_search
└ /new /think /model /quit

这是一个可靠的智能体。但每个提示词都从零开始——模型有工具,但没有工作流。如果你希望它创建常规的提交记录、使用特定的清单审查代码,或以标准格式总结项目,你每次都必须解释工作流。

这就是技能发挥作用的地方。

技能是什么?

一个技能是一个用 Markdown 编写的可重用工作流定义。它告诉智能体如何一步步使用它已有的工具来执行特定任务。

可以这样理解:

  • 工具是智能体的手(读取文件、运行命令、搜索网络)
  • 技能是智能体的行动手册(如何将工具组合成工作流)

这里是一个完整的技能—— git-commit/SKILL.md :

---
name: git-commit
description: Create well-structured git commits with conventional commit messages
---

When asked to commit changes, follow this workflow:

1. Run `git status` to see modified, added, and deleted files
2. Run `git diff --staged` to review what's staged; if nothing is staged,
   ask the user what to stage or suggest staging relevant files
3. Analyze the changes and determine the conventional commit type:
   - `feat`: New feature
   - `fix`: Bug fix
   - `docs`: Documentation changes
   - `refactor`: Code restructuring without behavior change
   - `test`: Adding or updating tests
   - `chore`: Maintenance tasks, dependency updates
   - `style`: Formatting, whitespace changes
4. Write a commit message:
   type(scope): short summary in imperative mood (<72 chars)
   Optional body explaining WHY the change was made.
5. Run `git commit -m "<message>"` to create the commit
6. Show `git log --oneline -1` to confirm

Keep the summary under 72 characters. Use imperative mood ("add feature" not "added feature").

就这样。没有 TypeScript。没有构建步骤。没有 API 注册。只需要 YAML 前置文本(名称 + 描述)和 Markdown 说明。

设计:渐进式披露

技能系统的关键架构决策是渐进式披露。以下是问题所在:如果你有 20 个技能,每个技能都有 30 多行指令,将它们全部注入系统提示词中会在每个请求上浪费数千个令牌——即使用户只是想问一个简单的问题。

我们的解决方案有两个层次:

  1. 系统提示词中的目录——仅注入技能名称和简短描述。每个技能大约消耗 10 个 token。模型知道有哪些技能存在,而无需承担它们的工作原理。
  2. 按需提供完整指令 — 当用户调用 /skill:git-commit 时,SDK 会读取完整的 SKILL.md 文件,并将其中所有指令注入到该特定提示词中。模型在需要时就能获得详细的逐步指导。

这意味着 50 项技能的成本大致相当于系统提示词中的 5 项。代币预算随使用量而扩展,而非注册。

实现

整个技能系统涉及两个文件,约 200 行更改。让我们逐一了解每个部分。

1. 技能发现

技能可以存在于三个地方,按检查优先级顺序排列:

这个层级意味着:

  • 用户技能会覆盖所有设置(您的个人偏好优先)
  • 项目技能覆盖捆绑技能(团队约定优于默认设置)
  • 打包技能提供开箱即用的合理默认值

发现功能很简单——遍历目录,加载技能,按名称去重(先匹配胜出):

function discoverSkills(workspaceDir: string): {
  skills: Skill[];
  diagnostics: string[];
} {
  const skillMap = new Map<string, Skill>();
  const diagnostics: string[] = [];
  const sources = [
    { dir: USER_SKILLS_DIR, source: "user" },
    { dir: path.join(workspaceDir, ".openclaw-mini", "skills"), source: "project" },
    { dir: BUNDLED_SKILLS_DIR, source: "bundled" },
  ];
  for (const { dir, source } of sources) {
    try {
      const result = loadSkillsFromDir({ dir, source });
      for (const diag of result.diagnostics) {
        diagnostics.push(`[${source}] ${diag.message}`);
      }
      for (const skill of result.skills) {
        if (!skillMap.has(skill.name)) {
          skillMap.set(skill.name, skill);
        }
      }
    } catch {
      // Directory doesn't exist - skip silently
    }
  }
  return { skills: Array.from(skillMap.values()), diagnostics };
}

loadSkillsFromDir 是一个 PI SDK 工具,用于读取目录,查找包含 SKILL.md 文件的子目录,解析 YAML 前置内容,并返回类型的 Skill 对象。SDK 还处理格式错误的技能的诊断(缺少名称、YAML 错误等)。

2. 系统提示词集成

接下来,我们将技能目录注入到系统提示词中。SDK 的 formatSkillsForPrompt() 将技能数组转换为 XML 目录:

<skills>
  <skill name="git-commit"
         description="Create well-structured git commits with conventional commit messages" />
  <skill name="code-review"
         description="Review code changes for bugs, style issues, and improvement opportunities" />
  <skill name="summarize"
         description="Summarize files, directories, or project structure into concise overviews" />
  <skill name="weather"
         description="Fetch current weather and forecasts for any location" />
</skills>

这只需三行代码即可添加到系统提示词构建器中:

// In buildSystemPrompt()
if (skillsPrompt) {
  lines.push("## Skills", skillsPrompt, "");
}

函数签名增加了一个新的可选参数:

export function buildSystemPrompt(params: {
  workspaceDir: string;
  runtime: RuntimeInfo;
  toolNames: string[];
  contextFiles: ContextFile[];
  thinkingLevel?: string;
  skillsPrompt?: string;    // ← new
}): string {

这就是整个系统提示词的变更——包括参数添加在内,共 8 行代码。

3. 资源加载器用于按需扩展

目录告诉模型存在哪些技能。但当用户输入 /skill:git-commit 时,SDK 需要知道在哪里找到完整的 SKILL.md 文件。这由 DefaultResourceLoader 处理:

const resourceLoader = new DefaultResourceLoader({
  cwd: workspaceDir,
  agentDir: AGENT_DIR,
  settingsManager,
  additionalSkillPaths: [
    USER_SKILLS_DIR,
    path.join(workspaceDir, ".openclaw-mini", "skills"),
    BUNDLED_SKILLS_DIR,
  ],
  noExtensions: true,
  noPromptTemplates: true,
  noThemes: true,
});
await resourceLoader.reload();

资源加载器被传递给 createAgentSession() ,它连接了 SDK 内部的 _expandSkillCommand() 机制。当模型在提示词中看到 /skill:weather 时,SDK:

  1. 在 additionalSkillPaths 中搜索 weather/SKILL.md
  2. 读取文件内容
  3. 将它们包裹在 XML 标签中
  4. 将完整的指令注入到该特定提示词中

模型随后看到完整的工作流程,并使用其可用工具逐步执行它。

4. /skills 命令

最后,我们添加一个斜杠命令,以便用户可以查看已加载的内容:

if (trimmed === "/skills") {
  if (skills.length === 0) {
    console.log("No skills loaded.");
  } else {
    console.log(`Loaded skills (${skills.length}):`);
    for (const skill of skills) {
      const src = `[${skill.source}]`.padEnd(10);
      console.log(`  ${src} ${skill.name}${skill.description}`);
    }
  }
  continue;
}

输出:

Loaded skills (4):
  [bundled]  git-commitCreate well-structured git commits with conventional commit messages
  [bundled]  code-review — Review code changes for bugs, style issues, and improvement opportunities
  [bundled]  summarize — Summarize files, directories, or project structure into concise overviews
  [bundled]  weather — Fetch current weather and forecasts for any location

启动横幅也会更新:

┌ openclaw-mini
│ model: anthropic/claude-sonnet-4-20250514
│ workspace: /Users/you/project
│ session: mini-1719432000000
│ context: CLAUDE.md
│ tools: read, bash, edit, write, web_fetch, web_search
│ skills: git-commit, code-review, summarize, weather        ← new
└ /new /think /model /skills /quit                           ← /skills added

捆绑的四种技能

openclaw-mini 随附四种技能,展示了不同的模式:

git-commit — 工具编排

仅使用 bash 。链式 git status → git diff --staged → 分析 → git commit → git log 。展示技能如何将多个顺序工具调用编排成一个连贯的工作流程。

code-review — 结构化输出

使用 bash 和 read 。定义特定的审查清单(正确性、安全性、性能、可读性、错误处理)和输出格式(关键问题 / 建议 / 细节问题 / 积极反馈)。展示技能如何强制执行一致的输出结构。

summarize — 多模式行为

使用 bash 和 read 。根据输入(单个文件与目录与完整项目)表现不同。展示技能如何定义基于不同上下文的条件工作流。

weather — 自定义工具集成

专用于 web_search 。展示了技能与自定义工具无缝协作,而不仅限于 SDK 内置工具。还展示了非编码用例——技能不仅限于工程工作流程。

创建自己的技能

创建一个技能只需 60 秒:

# 1. Create the skill directory
mkdir -p ~/.openclaw-mini/agents/main/agent/skills/my-skill

# 2. Write the SKILL.md
cat > ~/.openclaw-mini/agents/main/agent/skills/my-skill/SKILL.md << 'EOF'
---
name: my-skill
description: One-line description of what this skill does
---
Step-by-step instructions for the agent...
1. First, do X using `tool_name`
2. Then, do Y
3. Finally, present the result in this format:

Output template here

EOF

# 3. Restart openclaw-mini — your skill is automatically discovered

可以尝试的技能想法:

  • pr-review — 获取一个 GitHub PR 并使用团队检查清单进行评审
  • test-writer — 读取源文件并生成单元测试
  • explain — 生成复杂函数的逐步解释
  • migrate — 指导数据库迁移工作流
  • deploy-check — 部署前验证清单

如何协同工作

以下是从启动到技能执行的完整流程:

Startup
  │
  ├─ discoverSkills()
  │    ├─ Scan user skills dir     (~/.openclaw-mini/.../skills/)
  │    ├─ Scan project skills dir  ({cwd}/.openclaw-mini/skills/)
  │    └─ Scan bundled skills dir  ({repo}/skills/)
  │         └─ Deduplicate by name (first-match-wins)
  │
  ├─ formatSkillsForPrompt(skills)
  │    └─ Generate XML catalog (names + descriptions only)
  │
  ├─ buildSystemPrompt({ ..., skillsPrompt })
  │    └─ Inject "## Skills" section with catalog
  │
  └─ Print startup banner with skill names
  
User prompt: "/skill:git-commit stage and commit my changes"
  │
  ├─ SDK's _expandSkillCommand("git-commit")
  │    ├─ Search additionalSkillPaths for git-commit/SKILL.md
  │    ├─ Read file contents
  │    └─ Inject full instructions into prompt
  │
  ├─ Model sees: system prompt + skill catalog + full git-commit instructions + user message
  │
  └─ Model executes:
       ├─ bash("git status")
       ├─ bash("git diff --staged")
       ├─ [analyzes changes, picks commit type]
       ├─ bash("git commit -m 'feat(auth): add OAuth2 login flow'")
       └─ bash("git log --oneline -1")

变更内容:差异对比

整个技能系统都包含在这些更改中:

没有新的依赖项。没有新的构建步骤。未使用的技能没有运行时开销。

为什么这种架构有效

  1. 零成本抽象 — 技能是 Markdown 文件。没有插件运行时,没有沙箱,没有 IPC。所谓的“执行引擎”就是 LLM 本身,它遵循指令并调用自己已有的工具。
  2. 渐进式披露 — 系统提示词始终保持简洁,无论安装了多少技能。完整说明仅在调用时加载。
  3. 覆盖层级 — 用户 > 项目 > 打包。团队可以通过项目技能标准化工作流程。个人可以自定义而不需要分叉。
  4. 模型原生 — 技能不会与模型冲突;它们会引导模型。一个技能本质上是一个精心设计的提示词,在正确的时间注入。模型的推理和工具调用能力来完成剩余工作。
  5. 可组合 — 技能使用与自由形式提示词相同的工具。模型可以自然地组合技能工作流与临时指令。

下一步是什么

技能系统开辟了几个方向:

  • 技能参数 — 向技能传递参数(例如, /skill:review --scope=staged )
  • 技能链式调用 — 一个技能调用另一个技能
  • 社区技能注册表 — 通过 npm 或中央仓库共享技能
  • 条件工具需求 — 技能声明所需的工具
  • 技能模板 — 使用 openclaw-mini skill:create 构建新技能

但是基础已经在这里:一个 200 行的插件系统,可以将 Markdown 文件转换为可重用的智能体工作流。无需编写代码。

openclaw-mini 的完整源代码(包括技能系统)可在 GitHub 上获取。第一部分涵盖了构建基础智能体;本文则介绍了如何添加技能插件系统。

如果你构建了一个酷炫的技能,我很乐意听到相关信息。