iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0

開場故事

第 18 天我們已經把 skill.md 找到了,也知道 OpenClaw 會把它做成 snapshot。

但故事還沒結束。

因為真正難的不是「有沒有 skill」,而是:

  • 它怎麼進到這一輪的 system/developer instructions?
  • 它怎麼影響工具可見性?
  • 它怎麼在執行過程中維持一致?
  • 它到底是內容、規則,還是行為邊界?

換句話說,skill.md 被讀到只是開始。
真正重要的是它被處理成什麼。

第 19 天我想看的就是這件事:

skill 從定義到執行,OpenClaw 怎麼處理它?

今天要解的問題

  • skill snapshot 之後,資料去哪裡?
  • skillsSnapshot?.prompt 為什麼重要?
  • skill 會不會影響工具可用性?
  • skillUsagePaths 在做什麼?
  • skill 是怎麼從「檔案」變成「行為上下文」的?

架構總覽

skill 的處理可以想成四段:

  1. 定義:workspace 裡的 skill.md
  2. 整理:變成可重用的 snapshot
  3. 注入:進到 developer instructions 與 runtime context
  4. 作用:影響模型如何回答、如何用工具、如何遵守限制

這裡最容易誤會的是第三段和第四段。

很多人會以為 skill 是某種「執行插件」。
其實不是。
它更像是:

讓模型知道自己現在該用什麼方式工作的一份上下文。

OpenClaw 不是把 skill 當腳本跑,而是把 skill 當成運行規範的一部分。

原始碼節錄

先看第 18 天的 snapshot 怎麼被一路帶進 turn。

📄 原始碼:src/agents/agent-command.ts:1400-1428

const skillFilter = resolveEffectiveAgentSkillFilter(cfg, sessionAgentId);
const currentSkillsSnapshot = sessionEntry?.skillsSnapshot;
const [{ getRemoteSkillEligibility, resolveReusableWorkspaceSkillSnapshot }, { canExecRequestNode }] = await Promise.all([
	loadSkillsRuntime(),
	loadExecDefaultsRuntime()
]);
const skillSnapshotState = resolveReusableWorkspaceSkillSnapshot({
	skillFilter,
	currentSkillsSnapshot,
	workspaceDir,
	agentDir,
	agentId: sessionAgentId,
	canExecRequestNode,
	getRemoteSkillEligibility
});
const skillsSnapshot = skillSnapshotState.snapshot;

這裡的重點是:

  • OpenClaw 會先算出可用範圍
  • 再把 workspace skills 整理成 snapshot
  • snapshot 會成為這一輪 turn 的能力背景

接著看它怎麼進到 developer instructions。

📄 原始碼:extensions/codex/src/app-server/thread-lifecycle.ts:3062-3062

function buildDeveloperInstructions(params) {
	return [
		"Running inside OpenClaw. Use dynamic tools for messaging, cron, sessions, media, gateway, and nodes when available.",
		"Use Codex native `spawn_agent` for Codex subagents. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation; if it is not already loaded, search for `sessions_spawn` in the `openclaw` dynamic tool namespace before calling it.",
		"Preserve channel/session context. Visible channel replies: use `message`, do not describe would-reply.",
		renderCodexRuntimePromptOverlay(params),
		params.extraSystemPrompt,
		params.skillsSnapshot?.prompt
	].filter((section) => typeof section === "string" && section.trim()).join("\\n\\n");
}

這段非常直接。

skillsSnapshot?.prompt 被併到 developer instructions,代表 skill 不是外圍資料,而是模型看得見的指令背景。

再看 skill 相關資訊怎麼進到 runtime hooks。

📄 原始碼:src/agents/agent-tools.ts:1131-1151

const hookContext = {
	agentId,
	...(options?.config ? { config: options.config } : {}),
	cwd: codingRoot,
	workspaceDir: workspaceRoot,
	...(options?.skillsSnapshot ? { skillsSnapshot: options.skillsSnapshot } : {}),
	...(options?.skillUsagePaths ? { skillUsagePaths: options.skillUsagePaths } : {}),
	...(sandboxRoot && allowWorkspaceWrites
		? { sandbox: { root: sandboxRoot, bridge: sandboxFsBridge! } }
		: {}),
	sessionKey: options?.sessionKey,
	sessionId: options?.sessionId,
	runId: options?.runId,
	approvalReviewerDeviceId: options?.approvalReviewerDeviceId,
	channelId: options?.hookChannelId ?? options?.currentChannelId,
	...(turnSourceChannel ? { turnSourceChannel } : {}),
	...(turnSourceTo ? { turnSourceTo } : {}),
	...(options?.agentAccountId ? { turnSourceAccountId: options.agentAccountId } : {}),
	...(options?.currentThreadTs ? { turnSourceThreadId: options.currentThreadTs } : {}),
	...(options?.trace ? { trace: options.trace } : {}),
	loopDetection: resolveToolLoopDetectionConfig({ cfg: options?.config, agentId }),
	onToolOutcome: options?.onToolOutcome,
	allocateToolOutcomeOrdinal: options?.allocateToolOutcomeOrdinal,
};

這裡的 skillsSnapshotskillUsagePaths 很重要。

它代表 skill 不只是 prompt,還可能影響:

  • 哪些路徑與哪些文件會被視為 skill 用途
  • 哪些工具調用算是 skill 相關的行為
  • 執行中要怎麼追蹤 skill 的使用情況

也就是說,skill 不是只在模型腦袋裡飄一段字。
它有可能還會被 runtime 追蹤、被工具 hook 看見。

白話拆解

1. skill.md 是「定義」,skillsSnapshot 是「實例」

這個分工很像 class 和 instance。

  • skill.md 是原始定義
  • skillsSnapshot 是當下 turn 可用的實例狀態

這樣的好處是:

  • 定義和執行分離
  • 版本切換比較安全
  • debug 時知道現在吃的是哪一版 skill

2. skill 不只是提醒模型,它也會影響工作邊界

如果你只是把 skill 放在提示文字裡,它可能只是「建議」。

但當它被放進 developer instructions、hook context、usage paths 之後,它就會變成更實際的工作邊界:

  • 模型知道該怎麼做
  • runtime 知道要追哪些行為
  • 工具層知道這些能力是不是屬於這輪 turn

這就是 OpenClaw 比較成熟的地方。
它不是只讓模型「看到」,而是讓整個 runtime 都知道。

3. 為什麼 skillUsagePaths 值得看

因為它代表 skill 不只是文字說明,還可能涉及實際資源路徑。

對長流程系統來說,這很重要:

  • 哪些文件是 skill 的一部分
  • 哪些變更會讓 snapshot 失效
  • 哪些使用痕跡要被記錄

如果沒有這一層,你很難知道 skill 到底有沒有真的被使用。

4. skill 的處理不是「一個點」,而是一條鏈

你可以把它想成:

skill.md -> snapshot -> prompt -> runtime context -> model behavior -> tool usage

這條鏈的任何一段出問題,都會讓 skill 看起來像「沒生效」。

所以第 19 天的重點不是某個單一函式,而是整條處理管線。

5. 這也是為什麼 skill 比 plugin 更像能力層

plugin 比較像外掛入口,skill 比較像內建知識與工作方式。

plugin 主要解決「接哪裡」。
skill 主要解決「怎麼想、怎麼做」。

OpenClaw 把這兩個分得很清楚,整個系統就比較不會亂。

設計取捨

  • 好處是 skill 的行為可以被版本化、可追蹤
  • 好處是 prompt 注入後,模型真的會沿用 skill 的上下文
  • 好處是 hook context 可以看到 skill 的使用痕跡
  • 好處是 skill 與 plugin 的責任邊界清楚
  • 代價是 skill 處理鏈比較長,不是單一設定就能懂
  • 代價是技能更新後,可能要等 snapshot 刷新才會反映

但這種設計很像成熟系統應該有的樣子。
它不追求「看起來簡單」,它追求「行為穩定」。

今天的結論

  • skill.md 是定義,不是執行本體
  • OpenClaw 會先把 skills 做成 snapshot,再放進 turn 的上下文
  • params.skillsSnapshot?.prompt 會直接進 developer instructions
  • hookContext 會帶上 skillsSnapshotskillUsagePaths
  • skill 因此同時影響模型理解、runtime 追蹤和工具行為
  • skill 的真正角色,是能力上下文,而不是獨立外掛

下一步

前兩天把 cronskill 這兩個特殊 turn / 能力層都補完之後,下一篇就可以把前 20 天整個收斂起來。

前 20 天收斂,把 OpenClaw 畫成一張地圖


上一篇
第 18 天:OpenClaw 怎麼找到並讀進 skill.md?
下一篇
第 20 天:前 20 天收斂,把 OpenClaw 畫成一張地圖
系列文
30 天走進 OpenClaw:一個 AI Agent 的誕生、掙扎與進化23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言