iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0

開場故事

如果 skill 像工作手冊,那 plugin 就比較像「系統的插槽和擴充卡」。

它不是單純給 agent 看的內容,而是會真的改變 OpenClaw 如何載入能力、如何記錄 install、如何啟用功能,甚至如何對待來源與信任。

第 28 天我想談的是:

plugin 在 ClawHub 裡到底扮演什麼角色?它跟 skill 為什麼都走 ClawHub,但又不是同一種東西?

這題很重要。
因為一旦你把 skill 和 plugin 混成同一種「安裝項目」,你就會很難理解為什麼有些東西裝進來只是讓 agent 多一本手冊,有些東西卻會讓整個系統多一個能力。

今天要解的問題

  • plugin 在 OpenClaw 的位置是什麼?
  • 為什麼 plugin install 要記錄那麼多來源與 trust 資訊?
  • ClawHub plugin 的 install / enable / record 流程長什麼樣子?
  • 為什麼 onboarding plugin install 會有 ClawHub、npm、local、override 四條路?
  • plugin 這個角色,跟 skill 最大的差異在哪裡?

架構總覽

我會把 plugin 看成三層:

  1. 來源層:從 ClawHub、npm、local path、override 來
  2. 安裝層:下載、解壓、驗證、放進 extensions
  3. 記錄層:把 install record 和 config 狀態寫回去

這三層組合起來,plugin 才算真的進到 OpenClaw。

skill 比較像「內容」;plugin 比較像「可執行的系統模組」。

所以 plugin 的關鍵問題不是「內容是不是對」,而是:

這個模組會不會影響 runtime 行為?它是不是能被正確啟用?它的來源可不可以被追蹤?

原始碼節錄

先看 onboarding plugin install 的主入口說明。

📄 原始碼:src/commands/onboarding-plugin-install.ts(本機版本行號已變動,僅標到檔案)

/**
 * Onboarding plugin installation flow.
 *
 * It selects local, ClawHub, npm, or override install sources; records durable
 * install metadata; and enables plugins requested by setup workflows.
 */

這一小段註解其實已經把 plugin 角色講完一半了。

再看它怎麼決定來源。

📄 原始碼:src/commands/onboarding-plugin-install.ts:1154-1184

type InstallChoice = "clawhub" | "npm" | "local" | "skip";

const clawhubSpec = resolveClawHubSpecForOnboarding(entry.install);
const npmSpec = resolveNpmSpecForOnboarding(entry.install);
const defaultChoice = resolveInstallDefaultChoice({
  cfg: next,
  entry,
  localPath,
  bundledLocalPath,
  hasClawHubSpec: Boolean(clawhubSpec),
  hasNpmSpec: Boolean(npmSpec),
});

這裡可以看到 plugin 的世界不是單一管道。

它可能來自:

  • ClawHub
  • npm
  • local checkout
  • override

這很合理,因為 plugin 有時候是官方發佈,有時候是開發中的本地版本,有時候甚至是操作員明確指定的 override。

再看 ClawHub 安裝時怎麼把結果轉成 install record。

📄 原始碼:src/commands/onboarding-plugin-install.ts:951-977

if (result.ok) {
  const enableResult = await applyPluginEnablement({
    cfg: next,
    pluginId: result.pluginId,
    label: entry.label,
    prompter,
    runtime,
  });
  if (!enableResult.enabled) {
    return {
      cfg: enableResult.config,
      installed: false,
      pluginId: result.pluginId,
      status: "failed",
    };
  }
  next = enableResult.config;
  next = recordPluginInstall(next, {
    pluginId: result.pluginId,
    ...buildClawHubPluginInstallRecordFields(result.clawhub),
    spec: clawhubSpecs?.recordSpec ?? clawhubInstallSpec,
    installPath: result.targetDir,
  });
}

這段很關鍵。

plugin 安裝不是只要 result.ok 就結束,還要:

  • 啟用成功
  • 寫入 install record
  • 保留 ClawHub 的 trust / version / artifact / provenance

這表示 plugin 在 OpenClaw 裡是「安裝後還要被治理」的東西。

再看 install record 本身存什麼。

📄 原始碼:src/plugins/clawhub-install-records.ts:6-25

export type ClawHubPluginInstallRecordFields = {
  source: "clawhub";
  clawhubUrl: string;
  clawhubPackage: string;
  clawhubFamily: Exclude<ClawHubPackageFamily, "skill">;
  clawhubChannel?: ClawHubPackageChannel;
  clawhubTrustDisposition?: "clean" | "review-recommended" | "review-required" | "blocked";
  clawhubTrustScanStatus?: string;
  clawhubTrustModerationState?: string;
  clawhubTrustReasons?: string[];
  clawhubTrustPending?: boolean;
  clawhubTrustStale?: boolean;
  clawhubTrustCheckedAt?: string;
  clawhubTrustAcknowledgedAt?: string;
  version?: string;
  integrity?: string;
  resolvedAt?: string;
  installedAt?: string;
  artifactKind?: "legacy-zip" | "npm-pack";
  artifactFormat?: "zip" | "tgz";
};

這裡已經不是一般「已安裝清單」的規模了。
它是一份 install provenance 的摘要。

OpenClaw 在意的不只是插件存在,而是:

  • 它從哪裡來
  • 它是什麼 family
  • 它的 trust disposition 是什麼
  • 它是 zip 還是 tgz
  • 它是怎麼被解析與安裝的

再看 install record 的寫回。

📄 原始碼:src/plugins/installs.ts:49-67

export function recordPluginInstall(
  cfg: OpenClawConfig,
  update: PluginInstallUpdate,
): OpenClawConfig {
  const { pluginId, ...record } = update;
  const previous = clearStaleInstallRecordFields(cfg.plugins?.installs?.[pluginId]);
  const nextRecord = {
    ...previous,
    ...record,
    installedAt: record.installedAt ?? new Date().toISOString(),
  };

  return {
    ...cfg,
    plugins: {
      ...cfg.plugins,
      installs: {
        ...cfg.plugins?.installs,
        [pluginId]: nextRecord,
      },
    },
  };
}

這代表 plugin install 是配置的一部分。
也就是說,plugin 不是臨時下載到某個資料夾就算結束,而是會進到 config 的世界,成為系統的一部分記錄。

最後再看 manifest 層怎麼定義 plugin 的身份。

📄 原始碼:src/plugins/manifest.ts:160-183

export type PluginManifestActivation = {
  onStartup?: boolean;
  onProviders?: string[];
  onAgentHarnesses?: string[];
  onCommands?: string[];
  onChannels?: string[];
  onRoutes?: string[];
  onConfigPaths?: string[];
  onCapabilities?: PluginManifestActivationCapability[];
};

這個欄位很能說明 plugin 的角色。

plugin 不只是「能裝」,而是要告訴 core:

  • 我在什麼情況下要被載入
  • 我跟哪個 provider 有關
  • 我是不是某個 channel 的實作
  • 我是不是某個 command 的擴充

這就是 plugin 跟 skill 最本質的差異之一。

白話拆解

如果 skill 是一本工作手冊,那 plugin 是一塊真的插進主機板的模組。

因此 plugin 在 ClawHub 裡扮演的角色是:

  1. 提供可治理的來源
  2. 提供可安裝的分支
  3. 提供可啟用的系統能力
  4. 提供可追蹤的安裝痕跡

你可以想像成:

  • skill 裝進去後,agent 會多知道怎麼做事
  • plugin 裝進去後,OpenClaw 本身會多知道怎麼運作

這就是為什麼 plugin install 會比 skill install 更在意 config、record、activation、trust。

它不是一份內容包,而是一個會改變系統行為的組件。

設計取捨

  • 好處是 plugin 來源非常彈性,本地、npm、ClawHub、override 都能接
  • 好處是 install record 很完整,方便後續排錯與稽核
  • 好處是 activation metadata 能讓 core 在需要時才載入 plugin
  • 代價是 plugin lifecycle 較複雜,不只是下載和解壓
  • 代價是安裝失敗時要同時看來源、trust、enablement、record 四個面向
  • 代價是 plugin 的概念對新讀者會比 skill 更抽象

但這樣才合理。
因為 plugin 真的就是系統層的能力,不可能像文章或手冊那樣單純。

今天的結論

  • plugin 在 ClawHub 裡是「系統模組」而不是單純內容包
  • plugin install 會走來源選擇、安裝、啟用、記錄四個階段
  • install record 會保留 trust、artifact、version、source、resolvedAt 等資訊
  • manifest activation 會告訴 core 這個 plugin 什麼時候要被載入
  • plugin 與 skill 都會經過 ClawHub,但 plugin 更接近 runtime 擴充,skill 更接近工作能力

下一步

這三天我一直在講 plugin 是「系統模組」、是 runtime 擴充。

但講到這裡,其實只回答了「它是什麼」,還沒回答一個更具體的問題:

所謂的 runtime 能力,到底是指什麼?plugin 拿得到、skill 拿不到的,是哪些東西?

明天我想挑其中最有份量的一項來看。那是一組 API,能讓 plugin 編排一連串跨越多個 agent 的工作,而且那些工作的進度可以活過整個系統重啟。

多 agent 協作不能只靠上下文,TaskFlow 補上調度這一層


上一篇
第 27 天:skill 在 ClawHub 裡扮演什麼角色
下一篇
第 29 天:多 agent 協作不能只靠上下文,TaskFlow 補上調度這一層
系列文
30 天走進 OpenClaw:一個 AI Agent 的誕生、掙扎與進化 共 31 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言