OpenClaw 原始碼解讀:一個「個人 AI 助理」的多通道閘道架構

OpenClaw 是一個野心十足的開源專案——它要讓你在自己的裝置上運行一個 AI 助理,同時接入 WhatsApp、Telegram、Slack、Discord、Signal、iMessage 等十餘個通訊平台。讀這份原始碼,你能學到如何設計一套 Plugin 驅動的通道抽象層、如何在單一 WebSocket Gateway 上管理多 Agent 的 Session 生命週期、如何組裝 System Prompt 成為可配置的「人格引擎」,以及如何用 Node Registry 模式讓桌面與行動裝置無縫協作。這些設計概念可以直接借鏡到任何需要「多入口 + 單核心」架構的專案中。

OpenClaw GitHub Repository — 專案原始碼,包含完整的 Gateway 架構、Channel Plugin 系統、和所有本文解讀的設計實作

專案概覽

OpenClaw 是一個 本地優先(local-first)的個人 AI 助理,以 TypeScript 撰寫,運行在 Node.js 22+ 上。核心是一個 Gateway(閘道)控制平面,透過 WebSocket 連接所有客戶端——CLI、macOS 選單列 App、iOS/Android 節點、Web 介面,以及各通訊平台的 Channel Plugin。

技術棧:TypeScript + Node.js,使用 @sinclair/typebox 做 Schema 驗證,@anthropic-ai/sdkopenai SDK 與 LLM 交互,搭配 @mariozechner/pi-coding-agent 作為底層 Agent Runtime。

專案規模龐大,src/ 下有超過 20 個子目錄、數百個 TypeScript 檔案,另有 macOS(Swift)、iOS(Swift)、Android(Kotlin)原生 App。

架構總覽

src/
├── gateway/          # 🏛️ Gateway 控制平面:WS 伺服器、HTTP 端點、Session 管理
│   ├── server.impl.ts       # 閘道啟動主流程
│   ├── server-methods/      # WS RPC 方法處理器(agent、sessions、chat、nodes...)
│   ├── server-ws-runtime.ts # WebSocket 連線處理
│   ├── protocol/            # TypeBox Schema 定義的協定規格
│   └── node-registry.ts     # 裝置節點註冊與遠端呼叫
├── agents/           # 🤖 Agent Runtime:嵌入式 Pi Agent、工具、模型管理
│   ├── pi-embedded-runner.ts    # Agent 執行引擎
│   ├── pi-embedded-subscribe.ts # 串流訂閱與分塊
│   ├── system-prompt.ts         # System Prompt 組裝引擎
│   ├── model-fallback.ts        # 模型 Failover 策略
│   ├── pi-tools.ts              # 工具註冊與策略過濾
│   ├── skills/                  # Skills 平台
│   └── sandbox/                 # Docker Sandbox 隔離
├── channels/         # 📱 通道層:Plugin 架構的多平台通訊抽象
│   ├── plugins/             # Channel Plugin 定義、載入、Catalog
│   ├── registry.ts          # 核心通道中繼資料
│   └── dock.ts              # 通道對接介面
├── auto-reply/       # 🔄 自動回覆引擎:訊息派發、指令解析、回覆管線
│   ├── dispatch.ts          # 入站訊息分發
│   ├── reply/               # 回覆生成管線(含 streaming、chunking、directives)
│   └── commands-registry.ts # 聊天指令 (/status、/new、/think...)
├── plugins/          # 🔌 通用 Plugin Registry:工具、Hook、HTTP、CLI 擴展
│   ├── registry.ts          # 統一 Plugin 註冊中心
│   ├── discovery.ts         # Plugin 自動發現
│   └── runtime.ts           # Plugin 執行期管理
├── cli/              # ⌨️ CLI 表面:commander.js 風格的指令系統
├── config/           # ⚙️ 組態管理:JSON5 格式、Schema 驗證、熱重載
├── browser/          # 🌐 瀏覽器控制:CDP 整合的 Chrome 自動化
├── canvas-host/      # 🎨 Canvas A2UI:Agent 驅動的視覺化工作區
├── infra/            # 🏗️ 基礎設施:Heartbeat、診斷、更新檢查
└── hooks/            # 🪝 Hook 系統:事件驅動的擴展點
Code language: PHP (php)

核心設計解析

設計一:Gateway 作為單一控制平面

OpenClaw 最核心的架構決策是 一切都經過 Gateway。Gateway 是一個 WebSocket 伺服器,所有客戶端(CLI、App、Web、Node)都透過 WebSocket 連線到 Gateway,所有通道訊息也透過 Gateway 轉發。

src/gateway/server.impl.ts — Gateway 啟動主流程,包含 Plugin 載入、WS/HTTP 伺服器初始化、Channel 啟動的完整管線

export async function startGatewayServer(
  port = 18789,
  opts: GatewayServerOptions = {},
): Promise<GatewayServer> {
  // ... 組態載入、遷移、驗證
  const { pluginRegistry, gatewayMethods: baseGatewayMethods } = loadGatewayPlugins({
    cfg: cfgAtStart,
    workspaceDir: defaultWorkspaceDir,
    log,
    coreGatewayHandlers,
    baseMethods,
  });

  const channelMethods = listChannelPlugins().flatMap((plugin) => plugin.gatewayMethods ?? []);
  const gatewayMethods = Array.from(new Set([...baseGatewayMethods, ...channelMethods]));

  attachGatewayWsHandlers({
    wss, clients, port,
    gatewayMethods,
    events: GATEWAY_EVENTS,
    extraHandlers: {
      ...pluginRegistry.gatewayHandlers,
      ...execApprovalHandlers,
    },
    // ... 注入大量 context
  });
}
Code language: JavaScript (javascript)

為什麼這樣設計? Gateway 模式讓 OpenClaw 實現了幾件關鍵的事:

  1. Protocol 統一:無論是 WhatsApp 還是 Discord 的訊息,進入 Gateway 後都被標準化為統一的 Session 事件
  2. 狀態集中:Session 管理、Chat Abort、Dedupe 等狀態都在 Gateway 內存中維護,避免分散式狀態的複雜性
  3. Plugin 可注入 Gateway 方法:Plugin 不只提供工具,還能注入新的 WS RPC 方法,擴展 Gateway 的能力

startGatewayServer 函式超過 400 行,啟動流程按順序完成:組態驗證 → Plugin 載入 → TLS 設定 → WS/HTTP 伺服器 → Channel 啟動 → Cron 排程 → Node 發現。整個 Gateway 就是一條精心編排的啟動管線。


設計二:Channel Plugin 的 Adapter 模式

OpenClaw 支援 13+ 個通訊平台,但原始碼中並沒有巨大的 if-else 或 switch-case。它的秘密是一套精心設計的 Channel Plugin 介面,使用 Adapter 模式將每個通道的行為拆分成獨立的關注面。

src/channels/plugins/types.plugin.ts — Channel Plugin 核心型別定義,定義了十餘個可選 Adapter 介面

export type ChannelPlugin<ResolvedAccount = any> = {
  id: ChannelId;
  meta: ChannelMeta;
  capabilities: ChannelCapabilities;

  // 每個 adapter 處理一個關注面
  config: ChannelConfigAdapter<ResolvedAccount>;     // 組態解析
  setup?: ChannelSetupAdapter;                        // 初始化設定
  pairing?: ChannelPairingAdapter;                    // DM 配對驗證
  security?: ChannelSecurityAdapter<ResolvedAccount>; // 安全策略
  groups?: ChannelGroupAdapter;                       // 群組行為
  mentions?: ChannelMentionAdapter;                   // @提及處理
  outbound?: ChannelOutboundAdapter;                  // 外發訊息
  streaming?: ChannelStreamingAdapter;                // 串流分塊
  threading?: ChannelThreadingAdapter;                // 執行緒 / 回覆
  messaging?: ChannelMessagingAdapter;                // 訊息操作
  gateway?: ChannelGatewayAdapter<ResolvedAccount>;   // Gateway WS 處理
  onboarding?: ChannelOnboardingAdapter;              // CLI 嚮導
  agentTools?: ChannelAgentToolFactory | ChannelAgentTool[]; // 通道專屬工具
};
Code language: JavaScript (javascript)

為什麼這樣設計? 每個通訊平台的 API 差異極大——WhatsApp 用 QR code 配對、Telegram 用 Bot Token、Discord 有 Guild 概念、iMessage 只能在 macOS 上運行。把這些差異拆成十幾個可選的 Adapter 介面,讓每個 Plugin 只實作自己需要的部分,不需要的 Adapter 留 undefined

Channel Plugin 的載入使用了 Registry + Cache 模式:

src/channels/plugins/load.ts — Channel Plugin 載入與快取機制,Registry 更換時自動清除快取

const cache = new Map<ChannelId, ChannelPlugin>();
let lastRegistry: PluginRegistry | null = null;

function ensureCacheForRegistry(registry: PluginRegistry | null) {
  if (registry === lastRegistry) return;
  cache.clear();
  lastRegistry = registry;
}

export async function loadChannelPlugin(id: ChannelId): Promise<ChannelPlugin | undefined> {
  const registry = getActivePluginRegistry();
  ensureCacheForRegistry(registry);
  const cached = cache.get(id);
  if (cached) return cached;
  const pluginEntry = registry?.channels.find((entry) => entry.plugin.id === id);
  if (pluginEntry) {
    cache.set(id, pluginEntry.plugin);
    return pluginEntry.plugin;
  }
  return undefined;
}
Code language: JavaScript (javascript)

這個 Cache 會在 Plugin Registry 更換時自動清空,確保熱重載時不會使用到舊的 Plugin 實例。


設計三:統一 Plugin Registry 架構

Channel Plugin 只是整個 Plugin 系統的一部分。OpenClaw 有一個通用的 Plugin Registry,可以註冊六種不同類型的擴展。

src/plugins/registry.ts — 統一 Plugin 註冊中心,一個 Plugin 可同時提供工具、Hook、Channel、Gateway 方法等多種擴展

export type PluginRecord = {
  id: string;
  name: string;
  version?: string;
  kind?: PluginKind;
  source: string;
  origin: PluginOrigin;  // "config" | "workspace" | "global" | "bundled"
  enabled: boolean;
  status: "loaded" | "disabled" | "error";

  // 一個 Plugin 可以同時提供多種擴展
  toolNames: string[];
  hookNames: string[];
  channelIds: string[];
  providerIds: string[];
  gatewayMethods: string[];
  cliCommands: string[];
  services: string[];
  commands: string[];
  httpHandlers: number;
  hookCount: number;
};
Code language: JavaScript (javascript)

為什麼這樣設計? 這種「一個 Plugin 多個註冊面向」的設計,讓 Plugin 作者可以在一個套件中同時提供 Channel 支援、Gateway 方法、CLI 指令、Agent 工具——不需要拆成多個獨立的套件。Plugin 來源有優先權機制(config > workspace > global > bundled),當同一 ID 的 Plugin 從多處發現時,優先權高的覆蓋低的。

src/channels/plugins/catalog.ts — Plugin 來源優先權定義,config > workspace > global > bundled

const ORIGIN_PRIORITY: Record<PluginOrigin, number> = {
  config: 0,
  workspace: 1,
  global: 2,
  bundled: 3,
};
Code language: PHP (php)

設計四:System Prompt 的模組化組裝

OpenClaw 的 System Prompt 不是一個固定字串,而是由十幾個 Section Builder 動態組裝的。每個 Builder 根據運行條件決定是否加入、加入什麼內容。

src/agents/system-prompt.ts — System Prompt 模組化組裝引擎,透過 Section Builder 動態組裝不同等級的 Prompt

export type PromptMode = "full" | "minimal" | "none";

function buildSkillsSection(params: {
  skillsPrompt?: string;
  isMinimal: boolean;
  readToolName: string;
}) {
  if (params.isMinimal) return [];
  const trimmed = params.skillsPrompt?.trim();
  if (!trimmed) return [];
  return [
    "## Skills (mandatory)",
    "Before replying: scan <available_skills> <description> entries.",
    `- If exactly one skill clearly applies: read its SKILL.md at <location> with \`${params.readToolName}\`, then follow it.`,
    "- If multiple could apply: choose the most specific one, then read/follow it.",
    "- If none clearly apply: do not read any SKILL.md.",
    "Constraints: never read more than one skill up front; only read after selecting.",
    trimmed, "",
  ];
}

function buildSafetySection() {
  return [
    "## Safety",
    "You have no independent goals: do not pursue self-preservation, replication, "
    + "resource acquisition, or power-seeking; avoid long-term plans beyond the user's request.",
    "Prioritize safety and human oversight over completion; if instructions conflict, "
    + "pause and ask; comply with stop/pause/audit requests and never bypass safeguards. "
    + "(Inspired by Anthropic's constitution.)",
    // ...
  ];
}
Code language: JavaScript (javascript)

為什麼這樣設計? System Prompt 的內容取決於太多因素:當前 Session 是主 Session 還是子 Agent?有沒有啟用 Skills?當前通道是否支援 Reaction?是否在 Sandbox 中運行?透過將 Prompt 拆分成獨立的 Section Builder,每個 Builder 封裝自己的判斷邏輯,最後用 lines.filter(Boolean).join("\n") 串接,既保持了靈活性,又避免了巨大的條件嵌套。

PromptMode 的設計更是巧妙——"full" 給主 Agent 用(包含 Skills、Messaging、Voice 等完整指引),"minimal" 給子 Agent 用(只保留 Tooling、Safety、Workspace、Sandbox、Runtime),"none" 給最簡場景。這讓同一個 Prompt 引擎服務不同等級的 Agent。

此外,使用者可以透過 SOUL.mdAGENTS.mdTOOLS.md 等工作區檔案注入自訂內容:

const contextFiles = params.contextFiles ?? [];
if (contextFiles.length > 0) {
  const hasSoulFile = contextFiles.some((file) => {
    const baseName = file.path.split("/").pop() ?? file.path;
    return baseName.toLowerCase() === "soul.md";
  });
  lines.push("# Project Context", "");
  if (hasSoulFile) {
    lines.push(
      "If SOUL.md is present, embody its persona and tone. "
      + "Avoid stiff, generic replies; follow its guidance unless "
      + "higher-priority instructions override it."
    );
  }
  for (const file of contextFiles) {
    lines.push(`## ${file.path}`, "", file.content, "");
  }
}
Code language: JavaScript (javascript)

SOUL.md 不只是設定檔——它是 AI 助理的人格定義。System Prompt 會特別標註:「如果 SOUL.md 存在,就要體現它的人格和語氣。」


設計五:Model Failover 策略鏈

OpenClaw 支援多個 LLM Provider(Anthropic、OpenAI、Google Gemini、本地模型),並且設計了精密的 Failover 機制。

src/agents/model-fallback.ts — Model Failover 策略鏈,處理 Abort/Timeout 區分、冷卻期、Allowlist 過濾

function shouldRethrowAbort(err: unknown): boolean {
  return isAbortError(err) && !isTimeoutError(err);
}

function buildAllowedModelKeys(
  cfg: OpenClawConfig | undefined,
  defaultProvider: string,
): Set<string> | null {
  const rawAllowlist = (() => {
    const modelMap = cfg?.agents?.defaults?.models ?? {};
    return Object.keys(modelMap);
  })();
  if (rawAllowlist.length === 0) return null;
  const keys = new Set<string>();
  for (const raw of rawAllowlist) {
    const parsed = parseModelRef(String(raw ?? ""), defaultProvider);
    if (!parsed) continue;
    keys.add(modelKey(parsed.provider, parsed.model));
  }
  return keys.size > 0 ? keys : null;
}
Code language: JavaScript (javascript)

為什麼這樣設計? 幾個關鍵的設計決策:

  1. Abort vs Timeout 區分:使用者主動 Abort 應立即停止(不 Failover),但 Timeout 是可恢復的錯誤,應該切換到下一個模型嘗試
  2. Auth Profile 冷卻期:某個 OAuth Profile 失敗後,會進入冷卻期(isProfileInCooldown),短時間內不再使用該 Profile,避免反覆撞牆
  3. Allowlist 過濾:Failover 候選模型必須在使用者配置的 models 允許清單中,不會隨意切換到未授權的模型

這種策略鏈模式把「什麼錯誤該重試、什麼錯誤該切換、什麼錯誤該放棄」的決策邏輯完全外化,而不是埋在 Agent 執行的 try-catch 裡。


設計六:Node Registry 與遠端裝置呼叫

OpenClaw 不只是一個聊天機器人——它能控制你的 macOS、iPhone、Android 裝置。這透過 Node Registry 實現。

src/gateway/node-registry.ts — 裝置節點註冊與遠端 RPC 呼叫,將手機和電腦視為 Gateway 的可呼叫節點

export type NodeSession = {
  nodeId: string;
  connId: string;
  client: GatewayWsClient;
  caps: string[];          // 裝置能力:camera、screen、location...
  commands: string[];      // 裝置可執行的命令
  permissions?: Record<string, boolean>; // OS 層級的權限狀態
  platform?: string;       // macOS、iOS、Android
  connectedAtMs: number;
};

export class NodeRegistry {
  private nodesById = new Map<string, NodeSession>();
  private pendingInvokes = new Map<string, PendingInvoke>();

  async invoke(params: {
    nodeId: string;
    command: string;
    params?: unknown;
    timeoutMs?: number;
  }): Promise<NodeInvokeResult> {
    const node = this.nodesById.get(params.nodeId);
    if (!node) {
      return {
        ok: false,
        error: { code: "NOT_CONNECTED", message: "node not connected" },
      };
    }
    // ... 透過 WebSocket 發送命令給裝置,等待回應
  }
}
Code language: JavaScript (javascript)

為什麼這樣設計? Node Registry 把「裝置」視為 Gateway 的一等公民。每個裝置連線時自我描述(我有什麼能力、什麼命令、什麼權限),Gateway 記錄這些資訊。當 Agent 需要拍照、錄螢幕、取位置時,Gateway 查詢 Node Registry,找到有對應能力的裝置,透過 WS 發送 invoke 命令。

pendingInvokes 使用 Request-Response 模式搭配 timeout,確保遠端呼叫不會無限等待。這個模式本質上是在 WebSocket 上實現了一套 RPC。


設計七:Tool Policy 的多層過濾

OpenClaw 的工具系統不只是「註冊工具」這麼簡單。它有一套多層的 Tool Policy 機制,控制在不同場景下哪些工具可用。

src/agents/pi-tools.ts — 工具註冊與多層 Policy 過濾,實現全域/群組/子Agent/Sandbox/Plugin 五層安全策略

import {
  filterToolsByPolicy,
  isToolAllowedByPolicies,
  resolveEffectiveToolPolicy,
  resolveGroupToolPolicy,
  resolveSubagentToolPolicy,
} from "./pi-tools.policy.js";
Code language: JavaScript (javascript)

工具的可用性取決於多個層次:

  1. 全域 Tool Policy:組態檔中定義的 allowlist / denylist
  2. Group Tool Policy:群組 Session 可能有更嚴格的工具限制
  3. Subagent Tool Policy:子 Agent 的工具集是主 Agent 的子集
  4. Sandbox Tool Policy:在 Docker Sandbox 中,某些工具被完全禁用
  5. Plugin Tool Policy:Plugin 提供的工具可以標記為 optional

為什麼這樣設計? 安全是一個核心關切。OpenClaw 連接的是真實的通訊平台,入站 DM 應被視為「不可信輸入」。一個來自群組的訊息觸發的 Agent,不應該有權限執行 system.run 或控制瀏覽器。多層 Policy 讓每個「安全邊界」都可以獨立配置,而不是一個粗粒度的開關。


設計八:Bootstrap Files 與上下文注入

Agent 的上下文不只有 System Prompt——OpenClaw 還有一套 Bootstrap Files 機制,從工作區載入使用者定義的檔案,注入到 Agent 的上下文中。

src/agents/bootstrap-files.ts — Bootstrap Files 上下文注入機制,從工作區載入 SOUL.md、AGENTS.md、TOOLS.md 等自訂檔案

export async function resolveBootstrapContextForRun(params: {
  workspaceDir: string;
  config?: OpenClawConfig;
  sessionKey?: string;
  agentId?: string;
  warn?: (message: string) => void;
}): Promise<{
  bootstrapFiles: WorkspaceBootstrapFile[];
  contextFiles: EmbeddedContextFile[];
}> {
  const bootstrapFiles = await resolveBootstrapFilesForRun(params);
  const contextFiles = buildBootstrapContextFiles(bootstrapFiles, {
    maxChars: resolveBootstrapMaxChars(params.config),
    warn: params.warn,
  });
  return { bootstrapFiles, contextFiles };
}
Code language: JavaScript (javascript)

為什麼這樣設計? Bootstrap Files 分成三個層次:

  • AGENTS.md:Agent 的行為指引(類似 Claude 的 CLAUDE.md
  • SOUL.md:Agent 的人格定義(語氣、風格、背景故事)
  • TOOLS.md:工具使用指引

這些檔案按 Session 過濾(filterBootstrapFilesForSession),所以不同的 Session / Agent 可以看到不同的上下文。還有 Bootstrap Hooks 機制(applyBootstrapHookOverrides),允許在載入後動態修改內容。maxChars 限制確保不會因為過大的上下文檔案而超出模型的 Context Window。

Prompt 設計解讀

OpenClaw 的 System Prompt 設計值得單獨分析。它不是一段靜態文字,而是一個 運行時動態組裝 的模組化系統。

src/agents/system-prompt.ts — 完整的 Prompt Section 定義,包含 Skills、Safety、Messaging、Voice 等十五個條件式段落

核心 Prompt Sections(按順序):

Section 用途 條件
Skills 指引 Agent 如何選用 Skill promptMode !== "minimal" 且有 Skills
Memory Recall 指引 Agent 搜索記憶庫 memory_search 工具
Safety AI 安全守則 總是包含
Reply Tags 訊息回覆標籤指引 promptMode !== "minimal"
Messaging 跨通道訊息指引 promptMode !== "minimal"
Voice (TTS) 語音合成提示 有 TTS 配置
Workspace 工作目錄資訊 總是包含
Docs 文件路徑指引 有 docs 路徑
Sandbox Sandbox 資訊 在 Sandbox 環境
User Identity 使用者身份 promptMode !== "minimal"
Current Date & Time 時區資訊 有時區設定
Silent Replies 無話可說時的處理 promptMode !== "minimal"
Heartbeats 心跳回應規則 promptMode !== "minimal"
Runtime 運行環境摘要 總是包含
Project Context SOUL.md 等工作區檔案 有 contextFiles

幾個值得注意的 Prompt 設計技巧:

  1. Safety Section 借鏡 Anthropic 憲法:明確指出 Agent 不應追求自我保存、複製、資源獲取或權力擴張
  2. Silent Reply Token:設計了 SILENT_REPLY_TOKEN,讓 Agent 在無話可說時有明確的回應方式,而不是硬擠出內容
  3. Heartbeat Protocol:Agent 收到心跳探測時回覆 HEARTBEAT_OK,Gateway 據此判斷 Agent 是否正常運作
  4. Subagent Context Header 區分"minimal" 模式用 “Subagent Context”,"full" 模式用 “Group Chat Context”,讓 Agent 知道自己的角色

設計理念萃取

  • Gateway 模式統一所有入口 — 無論多少通道、多少裝置,只要經過一個 WebSocket 控制平面,就能用統一的 Session 模型管理一切。這個模式適用於任何需要整合多個外部 API 的系統。
  • Adapter 介面拆分關注面 — Channel Plugin 把 config、setup、security、outbound 等行為拆成十幾個獨立的 Adapter 介面,每個 Plugin 只實作需要的部分。這比繼承 BaseChannel 然後 override 方法更靈活,也更容易測試。
  • System Prompt 是可組裝的 — 不要把 Prompt 寫成一個大字串。把每個功能段落封裝成 Builder 函式,根據運行時條件決定是否包含,最後 join 成完整 Prompt。這讓 Prompt 的維護和擴展跟程式碼一樣有結構。
  • 多層安全策略的分離 — Tool Policy 分成全域、群組、子 Agent、Sandbox、Plugin 五層,每層獨立配置。這種「Defense in Depth」的思路值得借鏡到任何有權限控制需求的系統。
  • 裝置即節點 — Node Registry 把手機、電腦視為 Gateway 的可呼叫節點,每個節點自我描述能力和權限。這個 Pattern 可以推廣到任何需要「中心調度 + 邊緣執行」的場景。

延伸思考

OpenClaw 的架構本質上是一個 Hub-and-Spoke 模型——Gateway 是 Hub,通道、裝置、Agent 都是 Spoke。如果你正在建構一個需要整合多個外部 API 的系統,可以考慮這種模式:用一個中央控制平面統一協定、管理狀態、分發事件。

Channel Plugin 的 Adapter 模式特別適合用在「同類但差異大」的整合場景——例如多個支付閘道、多個雲端儲存 Provider、多個通知服務。定義好 Adapter 介面,讓每個實作者只關心自己需要的面向。

System Prompt 的模組化組裝思路可以直接應用到任何 AI Agent 專案。當你的 Agent 需要在不同場景(不同使用者角色、不同工具集、不同安全等級)下運行時,把 Prompt 拆成條件式的 Section Builder 會比維護多套 Prompt 模板更可持續。

最後,OpenClaw 展示了一個重要的觀點:開源 AI 助理的核心不只是 LLM 呼叫——更是圍繞 LLM 的整套基礎設施(Session 管理、多通道路由、安全策略、裝置整合、Plugin 系統)。這些「周邊」的工程設計,往往決定了 AI 助理能否真正落地到日常使用。

Vibe Coding:把觀念變成程式碼

以下是你可以直接給 AI 的 prompt 指令,將本文介紹的設計觀念應用到你的專案中:

Gateway 單一控制平面

場景:你正在建構一個需要整合多個外部 API(支付、通知、社群平台等)的系統,每個 API 有不同的協定和資料格式,你想用一個中央閘道統一管理。

給 AI 的指令

我要為我的專案設計一個 Gateway 控制平面,參考 OpenClaw 的 Hub-and-Spoke 架構。需求如下:

1. 用 WebSocket 作為內部通訊協定,所有客戶端(Web、App、CLI)都透過 WS 連線到 Gateway
2. Gateway 負責:協定統一(將外部 API 的不同格式轉換為內部統一事件)、狀態集中管理(Session、連線狀態)、事件分發
3. 支援 Plugin 注入新的 WS RPC 方法,讓 Gateway 的能力可擴展

我的專案是 [描述你的專案],使用 [你的技術棧]。

請幫我設計 Gateway 的核心介面和啟動流程,包含:Server 類別定義、WS 連線處理、RPC 方法註冊機制、Plugin 方法注入點。

效果:AI 會產出一個 Gateway 伺服器的骨架程式碼,包含 WebSocket 連線管理、RPC 方法路由、Plugin 擴展點,以及一個清晰的啟動管線(組態載入 -> Plugin 註冊 -> WS 伺服器啟動)。

Channel Plugin Adapter 模式

場景:你需要整合多個「同類但差異大」的外部服務(例如多個支付閘道、多個雲端儲存、多個通知服務),希望用統一介面封裝差異,但每個服務的能力和設定流程都不同。

給 AI 的指令

我要用 Adapter 模式設計一套 Plugin 介面,參考 OpenClaw 的 ChannelPlugin 設計。我的場景是整合多個 [你的整合對象,例如:支付閘道]。

設計要求:
1. 定義一個主 Plugin 型別,包含 id、meta、capabilities 等基本欄位
2. 把不同的行為面向拆成獨立的 Adapter 介面(例如 config、setup、security、outbound 等),每個都是可選的(optional)
3. 每個 Plugin 只需實作自己支援的 Adapter,不支援的留 undefined
4. 加上 Registry + Cache 機制,支援 Plugin 的動態載入和熱重載時自動清除快取

請用 TypeScript 實作,並提供一個具體的 Plugin 實作範例。

效果:AI 會產出一組 TypeScript 介面定義,將整合行為拆分成多個可選的 Adapter,以及一個帶快取的 Plugin Registry。你會得到一個主介面、數個 Adapter 子介面、Registry 載入邏輯,和至少一個具體的 Plugin 實作範例。

System Prompt 模組化組裝

場景:你的 AI Agent 專案需要根據不同場景(不同使用者角色、不同工具集、不同安全等級、主 Agent vs 子 Agent)動態調整 System Prompt,目前維護多套 Prompt 模板已經難以管理。

給 AI 的指令

我要重構我的 AI Agent 的 System Prompt,從一個大字串改成模組化組裝模式,參考 OpenClaw 的 Section Builder 設計。

目前的 Prompt 內容包含:[列出你的 Prompt 包含的段落,例如:角色定義、安全守則、工具使用指引、回應格式要求、上下文資訊等]

設計要求:
1. 定義 PromptMode(例如 “full” | “minimal” | “none”),不同模式包含不同的 Section
2. 每個 Section 封裝成獨立的 Builder 函式,接收參數後回傳字串陣列,根據條件決定是否產出內容
3. 支援從外部檔案(類似 OpenClaw 的 SOUL.md、AGENTS.md)注入自訂內容
4. 最後用 filter(Boolean).join(“\n”) 組裝成完整 Prompt

請幫我設計 Section Builder 的架構和主要的組裝函式。

效果:AI 會將你的 Prompt 拆解成多個獨立的 Builder 函式,每個函式封裝自己的條件判斷邏輯,加上一個主組裝函式,根據 PromptMode 和運行時參數動態組裝完整的 System Prompt。

多層 Tool Policy 安全策略

場景:你的 AI Agent 系統會接收來自外部的不可信輸入(例如使用者訊息、第三方 Webhook),需要根據不同的安全邊界限制 Agent 可使用的工具,避免權限過度開放。

給 AI 的指令

我要為我的 AI Agent 系統設計多層的 Tool Policy 過濾機制,參考 OpenClaw 的 Defense in Depth 策略。

我的系統有以下安全邊界:[描述你的邊界,例如:管理員 vs 一般使用者、內部呼叫 vs 外部 API、正式環境 vs 沙箱環境]

設計要求:
1. 每一層 Policy 獨立定義 allowlist / denylist
2. 多層 Policy 按優先順序疊加過濾:全域 Policy -> 角色 Policy -> 場景 Policy -> 沙箱 Policy
3. 提供 resolveEffectiveToolPolicy 函式,將多層 Policy 合併為最終的可用工具清單
4. 提供 isToolAllowedByPolicies 函式,用於單一工具的快速檢查

請用 TypeScript 實作完整的 Policy 解析邏輯。

效果:AI 會產出一套分層的 Tool Policy 系統,包含 Policy 型別定義、多層合併邏輯、快速檢查函式,讓你能在不同的安全邊界獨立配置工具存取權限,實現縱深防禦。

Model Failover 策略鏈

場景:你的專案使用多個 LLM Provider(例如 Anthropic、OpenAI、本地模型),需要在某個 Provider 故障時自動切換,但要區分「使用者主動取消」和「逾時等可恢復錯誤」。

給 AI 的指令

我要為我的 LLM 呼叫層設計 Failover 策略鏈,參考 OpenClaw 的 Model Failover 設計。

我使用的 Provider 有:[列出你的 Provider]

設計要求:
1. 區分 Abort(使用者主動取消,不重試)和 Timeout(可恢復錯誤,切換下一個模型)
2. 失敗的 Provider 進入冷卻期,短時間內不再嘗試
3. Failover 候選模型必須在使用者配置的允許清單中
4. 將「什麼錯誤該重試、什麼該切換、什麼該放棄」的決策邏輯外化成策略函式,而非埋在 try-catch 中

請設計完整的 Failover 策略鏈,包含錯誤分類、冷卻期管理、候選模型篩選、和主要的重試迴圈。

效果:AI 會產出一套完整的 LLM Failover 機制,包含錯誤類型判斷函式(shouldRetry / shouldFallback / shouldAbort)、Provider 冷卻期管理、Allowlist 過濾、以及一個清晰的 Failover 執行迴圈。

延伸閱讀

0

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *