跳到內容

整合 OpenClaw 與 pi SDK:打造高效 AI 程式編寫代理

OpenClaw 使用 pi SDK 將 AI 編碼代理嵌入到其訊息傳遞 Gateway 架構中。OpenClaw 不會將 pi 作為子進程啟動或使用 RPC 模式,而是透過 createAgentSession() 直接匯入並實例化 pi 的 AgentSession。這種嵌入式方法提供了以下優勢:

  • 完全掌控會話生命週期與事件處理
  • 自訂工具注入(訊息傳遞、沙盒、特定頻道操作)
  • 針對不同頻道或上下文自訂系統提示詞(System prompt)
  • 支援分支與壓縮的會話持久化
  • 具備故障轉移的多帳號認證配置輪替
  • 與供應商無關的模型切換功能
{
"@mariozechner/pi-agent-core": "0.64.0",
"@mariozechner/pi-ai": "0.64.0",
"@mariozechner/pi-coding-agent": "0.64.0",
"@mariozechner/pi-tui": "0.64.0"
}
套件用途
pi-ai核心 LLM 抽象:Model、streamSimple、訊息類型、供應商 API
pi-agent-core代理迴圈、工具執行、AgentMessage 類型
pi-coding-agent高階 SDK:createAgentSession、SessionManager、AuthStorage、ModelRegistry、內建工具
pi-tui終端 UI 元件(用於 OpenClaw 的本地 TUI 模式)
src/agents/
├── pi-embedded-runner.ts # Re-exports from pi-embedded-runner/
├── pi-embedded-runner/
│ ├── run.ts # Main entry: runEmbeddedPiAgent()
│ ├── run/
│ │ ├── attempt.ts # Single attempt logic with session setup
│ │ ├── params.ts # RunEmbeddedPiAgentParams type
│ │ ├── payloads.ts # Build response payloads from run results
│ │ ├── images.ts # Vision model image injection
│ │ └── types.ts # EmbeddedRunAttemptResult
│ ├── abort.ts # Abort error detection
│ ├── cache-ttl.ts # Cache TTL tracking for context pruning
│ ├── compact.ts # Manual/auto compaction logic
│ ├── extensions.ts # Load pi extensions for embedded runs
│ ├── extra-params.ts # Provider-specific stream params
│ ├── google.ts # Google/Gemini turn ordering fixes
│ ├── history.ts # History limiting (DM vs group)
│ ├── lanes.ts # Session/global command lanes
│ ├── logger.ts # Subsystem logger
│ ├── model.ts # Model resolution via ModelRegistry
│ ├── runs.ts # Active run tracking, abort, queue
│ ├── sandbox-info.ts # Sandbox info for system prompt
│ ├── session-manager-cache.ts # SessionManager instance caching
│ ├── session-manager-init.ts # Session file initialization
│ ├── system-prompt.ts # System prompt builder
│ ├── tool-split.ts # Split tools into builtIn vs custom
│ ├── types.ts # EmbeddedPiAgentMeta, EmbeddedPiRunResult
│ └── utils.ts # ThinkLevel mapping, error description
├── pi-embedded-subscribe.ts # Session event subscription/dispatch
├── pi-embedded-subscribe.types.ts # SubscribeEmbeddedPiSessionParams
├── pi-embedded-subscribe.handlers.ts # Event handler factory
├── pi-embedded-subscribe.handlers.lifecycle.ts
├── pi-embedded-subscribe.handlers.types.ts
├── pi-embedded-block-chunker.ts # Streaming block reply chunking
├── pi-embedded-messaging.ts # Messaging tool sent tracking
├── pi-embedded-helpers.ts # Error classification, turn validation
├── pi-embedded-helpers/ # Helper modules
├── pi-embedded-utils.ts # Formatting utilities
├── pi-tools.ts # createOpenClawCodingTools()
├── pi-tools.abort.ts # AbortSignal wrapping for tools
├── pi-tools.policy.ts # Tool allowlist/denylist policy
├── pi-tools.read.ts # Read tool customizations
├── pi-tools.schema.ts # Tool schema normalization
├── pi-tools.types.ts # AnyAgentTool type alias
├── pi-tool-definition-adapter.ts # AgentTool -> ToolDefinition adapter
├── pi-settings.ts # Settings overrides
├── pi-hooks/ # Custom pi hooks
│ ├── compaction-safeguard.ts # Safeguard extension
│ ├── compaction-safeguard-runtime.ts
│ ├── context-pruning.ts # Cache-TTL context pruning extension
│ └── context-pruning/
├── model-auth.ts # Auth profile resolution
├── auth-profiles.ts # Profile store, cooldown, failover
├── model-selection.ts # Default model resolution
├── models-config.ts # models.json generation
├── model-catalog.ts # Model catalog cache
├── context-window-guard.ts # Context window validation
├── failover-error.ts # FailoverError class
├── defaults.ts # DEFAULT_PROVIDER, DEFAULT_MODEL
├── system-prompt.ts # buildAgentSystemPrompt()
├── system-prompt-params.ts # System prompt parameter resolution
├── system-prompt-report.ts # Debug report generation
├── tool-summaries.ts # Tool description summaries
├── tool-policy.ts # Tool policy resolution
├── transcript-policy.ts # Transcript validation policy
├── skills.ts # Skill snapshot/prompt building
├── skills/ # Skill subsystem
├── sandbox.ts # Sandbox context resolution
├── sandbox/ # Sandbox subsystem
├── channel-tools.ts # Channel-specific tool injection
├── openclaw-tools.ts # OpenClaw-specific tools
├── bash-tools.ts # exec/process tools
├── apply-patch.ts # apply_patch tool (OpenAI)
├── tools/ # Individual tool implementations
│ ├── browser-tool.ts
│ ├── canvas-tool.ts
│ ├── cron-tool.ts
│ ├── gateway-tool.ts
│ ├── image-tool.ts
│ ├── message-tool.ts
│ ├── nodes-tool.ts
│ ├── session*.ts
│ ├── web-*.ts
│ └── ...
└── ...

特定頻道的訊息操作執行環境現在位於外掛程式所屬的擴充功能目錄中,而不是 src/agents/tools 下,例如:

  • Discord 外掛程式操作執行檔
  • Slack 外掛程式操作執行檔
  • Telegram 外掛程式操作執行檔
  • WhatsApp 外掛程式操作執行檔

主要進入點是 pi-embedded-runner/run.ts 中的 runEmbeddedPiAgent():

import { runEmbeddedPiAgent } from "./agents/pi-embedded-runner.js";
const result = await runEmbeddedPiAgent({
sessionId: "user-123",
sessionKey: "main:whatsapp:+1234567890",
sessionFile: "/path/to/session.jsonl",
workspaceDir: "/path/to/workspace",
config: openclawConfig,
prompt: "Hello, how are you?",
provider: "anthropic",
model: "claude-sonnet-4-6",
timeoutMs: 120_000,
runId: "run-abc",
onBlockReply: async (payload) => {
await sendToChannel(payload.text, payload.mediaUrls);
},
});

在 runEmbeddedAttempt()(由 runEmbeddedPiAgent() 呼叫)內部,會使用 pi SDK:

import {
createAgentSession,
DefaultResourceLoader,
SessionManager,
SettingsManager,
} from "@mariozechner/pi-coding-agent";
const resourceLoader = new DefaultResourceLoader({
cwd: resolvedWorkspace,
agentDir,
settingsManager,
additionalExtensionPaths,
});
await resourceLoader.reload();
const { session } = await createAgentSession({
cwd: resolvedWorkspace,
agentDir,
authStorage: params.authStorage,
modelRegistry: params.modelRegistry,
model: params.model,
thinkingLevel: mapThinkingLevel(params.thinkLevel),
tools: builtInTools,
customTools: allCustomTools,
sessionManager,
settingsManager,
resourceLoader,
});
applySystemPromptOverrideToSession(session, systemPromptOverride);

subscribeEmbeddedPiSession() 用於訂閱 pi 的 AgentSession 事件:

const subscription = subscribeEmbeddedPiSession({
session: activeSession,
runId: params.runId,
verboseLevel: params.verboseLevel,
reasoningMode: params.reasoningLevel,
toolResultFormat: params.toolResultFormat,
onToolResult: params.onToolResult,
onReasoningStream: params.onReasoningStream,
onBlockReply: params.onBlockReply,
onPartialReply: params.onPartialReply,
onAgentEvent: params.onAgentEvent,
});

處理的事件包括:

  • message_start / message_end / message_update(串流文字/思考過程)
  • tool_execution_start / tool_execution_update / tool_execution_end
  • turn_start / turn_end
  • agent_start / agent_end
  • compaction_start / compaction_end

設定完成後,會對會話進行提示(Prompting):

await session.prompt(effectivePrompt, { images: imageResult.images });

SDK 會處理完整的代理迴圈:發送至 LLM、執行工具呼叫、串流回應。

圖像注入是針對單次提示詞的:OpenClaw 會從當前提示詞載入圖像參照,並僅針對該輪次透過 images 傳遞它們。它不會重新掃描舊的歷史輪次來重新注入圖像負載。

OpenClaw 的工具管線設計旨在確保各類指令與外部服務能順暢運作。以下是該架構的組成層級:

  1. Base Tools: 使用 pi 的 codingTools(包含 read、bash、edit、write)。
  2. Custom Replacements: OpenClaw 將 bash 替換為 exec/process,並針對 sandbox 自定義 read/edit/write。
  3. OpenClaw Tools: 包含 messaging、browser、canvas、sessions、cron、Gateway 等功能。
  4. Channel Tools: 針對 Discord/Telegram/Slack/WhatsApp 的特定動作工具。
  5. Policy Filtering: 根據 profile、provider、agent、group、sandbox 策略進行工具篩選。
  6. Schema Normalization: 針對 Gemini/OpenAI 的特性清理 JSON 結構。
  7. AbortSignal Wrapping: 將工具進行封裝以遵循 abort signals。

pi-agent-core 的 AgentTool 與 pi-coding-agent 的 ToolDefinition 在 execute 簽名上有所不同。位於 pi-tool-definition-adapter.ts 中的適配器解決了此差異:

export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
return tools.map((tool) => ({
name: tool.name,
label: tool.label ?? name,
description: tool.description ?? "",
parameters: tool.parameters,
execute: async (toolCallId, params, onUpdate, _ctx, signal) => {
// pi-coding-agent signature differs from pi-agent-core
return await tool.execute(toolCallId, params, signal, onUpdate);
},
}));
}

splitSdkTools() 透過 customTools 傳遞所有工具:

export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) {
return {
builtInTools: [], // Empty. We override everything
customTools: toToolDefinitions(options.tools),
};
}

這確保了 OpenClaw 的策略篩選、Docker sandbox 整合以及擴充工具集在不同提供商之間保持一致。

系統提示詞是在 buildAgentSystemPrompt()(位於 system-prompt.ts)中建構的。它會組裝一個完整的提示詞,包含 Tooling、Tool Call Style、Safety guardrails、OpenClaw CLI 參考、Skills、Docs、Workspace、Sandbox、Messaging、Reply Tags、Voice、Silent Replies、Heartbeats、Runtime metadata,以及啟用時的 Memory 與 Reactions,並可選配 context files 與額外的系統提示內容。針對 subagents 使用的最小提示詞模式,這些區段會進行修剪。

提示詞會在建立 session 後透過 applySystemPromptOverrideToSession() 應用:

const systemPromptOverride = createSystemPromptOverride(appendPrompt);
applySystemPromptOverrideToSession(session, systemPromptOverride);

Sessions 是以樹狀結構(透過 id/parentId 連結)儲存的 JSONL 檔案。Pi 的 SessionManager 負責處理持久化:

const sessionManager = SessionManager.open(params.sessionFile);

OpenClaw 使用 guardSessionManager() 對此進行封裝,以確保工具結果的安全性。

session-manager-cache.ts 會快取 SessionManager 實例,以避免重複解析檔案:

await prewarmSessionFile(params.sessionFile);
sessionManager = SessionManager.open(params.sessionFile);
trackSessionManagerAccess(params.sessionFile);

limitHistoryTurns() 會根據頻道類型(DM 或群組)修剪對話歷史。

當上下文溢出時會觸發自動壓縮。常見的溢出錯誤訊息包含 request_too_large、context length exceeded、input exceeds the maximum number of tokens、input token count exceeds the maximum number of input tokens、input is too long for the model 以及 ollama error: context length exceeded。compactEmbeddedPiSessionDirect() 負責處理手動壓縮:

const compactResult = await compactEmbeddedPiSessionDirect({
sessionId, sessionFile, provider, model, ...
});

OpenClaw 維護一個認證 profile 儲存庫,每個提供商可擁有多個 API 金鑰:

const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });

Profiles 會在失敗時進行輪替,並附帶冷卻時間追蹤:

await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });
const rotated = await advanceAuthProfile();
import { resolveModel } from "./pi-embedded-runner/model.js";
const { model, error, authStorage, modelRegistry } = resolveModel(
provider,
modelId,
agentDir,
config,
);
// Uses pi's ModelRegistry and AuthStorage
authStorage.setRuntimeApiKey(model.provider, apiKeyInfo.apiKey);

當設定故障轉移時,FailoverError 會觸發模型回退:

if (fallbackConfigured && isFailoverErrorMessage(errorText)) {
throw new FailoverError(errorText, {
reason: promptFailoverReason ?? "unknown",
provider,
model: modelId,
profileId,
status: resolveFailoverStatus(promptFailoverReason),
});
}

AI Setup Assistant

OpenClaw 透過載入自訂的 pi extensions 來實現各種特殊行為,讓你的代理程式運作更靈活。

src/agents/pi-hooks/compaction-safeguard.ts 為壓縮過程增加了防護機制,包含自適應的 token 預算分配,以及工具執行失敗與檔案操作的摘要功能:

if (resolveCompactionMode(params.cfg) === "safeguard") {
setCompactionSafeguardRuntime(params.sessionManager, { maxHistoryShare });
paths.push(resolvePiExtensionPath("compaction-safeguard"));
}

src/agents/pi-hooks/context-pruning.ts 實作了基於 cache-TTL 的上下文修剪功能,確保記憶體使用維持在合理範圍:

if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
setContextPruningRuntime(params.sessionManager, {
settings,
contextWindowTokens,
isToolPrunable,
lastCacheTouchAt,
});
paths.push(resolvePiExtensionPath("context-pruning"));
}

當你使用 OpenClaw 處理串流輸出時,系統會自動管理區塊回覆與內容解析,讓對話體驗更順暢。

EmbeddedBlockChunker 負責管理將串流文字切分為獨立的回覆區塊,確保輸出格式正確:

const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;

串流輸出會經過處理,系統會自動移除 <think> 或 <thinking> 區塊,並從中提取 <final> 標籤內的內容:

const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => {
// Strip <think>...</think> content
// If enforceFinalTag, only return <final>...</final> content
};

系統會解析並提取回覆指令,例如 [[media:url]]、[[voice]] 以及 [[reply:id]],以便進行後續處理:

const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);

在開發過程中,處理各種異常狀況是確保系統穩定性的關鍵。OpenClaw 的錯誤處理機制透過模組化的方式,讓你能精準捕捉並分類執行時發生的問題。

pi-embedded-helpers.ts 會將錯誤進行分類,以便系統能採取對應的處理措施:

isContextOverflowError(errorText) // Context too large
isCompactionFailureError(errorText) // Compaction failed
isAuthAssistantError(lastAssistant) // Auth failure
isRateLimitAssistantError(...) // Rate limited
isFailoverAssistantError(...) // Should failover
classifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "timeout" | ...

當系統偵測到目前的 thinking level 不被支援時,會自動觸發回退邏輯,確保任務能繼續執行:

const fallbackThinking = pickFallbackThinkingLevel({
message: errorText,
attempted: attemptedThinking,
});
if (fallbackThinking) {
thinkLevel = fallbackThinking;
continue;
}

當你在 OpenClaw 中啟用 sandbox 模式時,所有的工具存取與路徑權限都會受到嚴格限制,以確保執行環境的安全性。

當 sandbox 模式開啟時,系統會根據配置與工作區路徑來鎖定執行範圍:

const sandbox = await resolveSandboxContext({
config: params.config,
sessionKey: sandboxSessionKey,
workspaceDir: resolvedWorkspace,
});
if (sandboxRoot) {
// Use sandboxed read/edit/write tools
// Exec runs in container
// Browser uses bridge URL
}

針對不同的 AI 模型供應商,OpenClaw 實作了特定的處理邏輯,以確保與各家 API 的相容性與穩定性。這些機制能自動處理各平台在訊息格式或工具呼叫上的細微差異。

針對 Anthropic 的模型,我們加入了一些必要的預處理步驟,確保請求能順利通過驗證。

  1. 清除拒絕回應的 magic string。
  2. 針對連續角色進行轉換驗證。
  3. 嚴格執行上游 Pi 工具參數驗證。

對於 Google 的 Gemini 模型,我們主要處理工具架構的相容性問題。

  1. 執行由插件擁有的工具 schema 清理工作。

針對 OpenAI 的模型,我們處理了特定模型架構的特殊需求。

  1. 為 Codex 模型提供 apply_patch 工具。
  2. 處理思考層級(thinking level)的降級邏輯。

OpenClaw 內建了本地 TUI 模式,直接調用 pi-tui 組件來提供互動式終端體驗。這讓你在使用 OpenClaw 時,能享有與 pi 原生模式相同的操作感受。

src/tui/tui.ts
import { ... } from "@mariozechner/pi-tui";

這項整合讓你在終端機中操作時,能獲得更直觀且流暢的互動體驗。

在開發過程中,我們經常會遇到不同工具鏈之間行為不一致的困擾,理解這些底層差異能幫你更順利地進行遷移。OpenClaw 與 Pi CLI 在架構設計上有顯著不同,以下表格列出了核心區別,幫助你快速掌握 OpenClaw 的運作邏輯。

AspectPi CLIOpenClaw Embedded
Invocationpi command / RPCSDK via createAgentSession()
ToolsDefault coding toolsCustom OpenClaw tool suite
System promptAGENTS.md + promptsDynamic per-channel/context
Session storage~/.pi/agent/sessions/~/.openclaw/agents/<agentId>/sessions/ (or $OPENCLAW_STATE_DIR/agents/<agentId>/sessions/)
AuthSingle credentialMulti-profile with rotation
ExtensionsLoaded from diskProgrammatic + disk paths
Event handlingTUI renderingCallback-based (onBlockReply, etc.)

我們在持續優化架構的過程中,識別出了一些需要重構或改進的領域,這些調整將有助於提升系統的穩定性與擴充性。

  1. Tool signature alignment: Currently adapting between pi-agent-core and pi-coding-agent signatures
  2. Session manager wrapping: guardSessionManager adds safety but increases complexity
  3. Extension loading: Could use pi’s ResourceLoader more directly
  4. Streaming handler complexity: subscribeEmbeddedPiSession has grown large
  5. Provider quirks: Many provider-specific codepaths that pi could potentially handle

為了確保 Pi 整合的穩定性,我們建立了一套完整的測試套件,涵蓋了從基礎驗證到複雜場景的各個面向。

Pi integration coverage spans these suites:

  • src/agents/pi-*.test.ts
  • src/agents/pi-auth-json.test.ts
  • src/agents/pi-embedded-*.test.ts
  • src/agents/pi-embedded-helpers*.test.ts
  • src/agents/pi-embedded-runner*.test.ts
  • src/agents/pi-embedded-runner/**/*.test.ts
  • src/agents/pi-embedded-subscribe*.test.ts
  • src/agents/pi-tools*.test.ts
  • src/agents/pi-tool-definition-adapter*.test.ts
  • src/agents/pi-settings.test.ts
  • src/agents/pi-hooks/**/*.test.ts

Live/opt-in:

  • src/agents/pi-embedded-runner-extraparams.live.test.ts (enable OPENCLAW_LIVE_TEST=1)

For current run commands, see Pi Development Workflow.

AI Setup Assistant

OpenClaw

OpenClaw Expert

還是卡住了?

如果這篇文件沒有解決你的情境,直接問 OpenClaw Expert,拿到可執行步驟。