OpenClawとpi SDKを統合:AIエージェントを直接組み込む方法
OpenClaw は pi SDK を活用し、AI コーディングエージェントをメッセージング Gateway アーキテクチャに組み込んでいます。pi をサブプロセスとして起動したり RPC モードを使用したりするのではなく、OpenClaw は createAgentSession() を介して pi の AgentSession を直接インポートし、インスタンス化します。この組み込みアプローチにより、以下のメリットが得られます。
- セッションのライフサイクルとイベント処理の完全な制御
- カスタムツールの注入(メッセージング、サンドボックス、チャンネル固有のアクション)
- チャンネルやコンテキストごとのシステムプロンプトのカスタマイズ
- ブランチやコンパクションをサポートしたセッションの永続化
- フェイルオーバーを備えたマルチアカウント認証プロファイルのローテーション
- プロバイダーに依存しないモデルの切り替え
パッケージの依存関係
Section titled “パッケージの依存関係”{ "@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 モードで使用) |
ファイル構造
Section titled “ファイル構造”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 プラグインのアクションランタイムファイル
コア統合フロー
Section titled “コア統合フロー”1. 組み込みエージェントの実行
Section titled “1. 組み込みエージェントの実行”メインのエントリーポイントは 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); },});2. セッションの作成
Section titled “2. セッションの作成”runEmbeddedPiAgent() から呼び出される runEmbeddedAttempt() 内で、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);3. イベントのサブスクライブ
Section titled “3. イベントのサブスクライブ”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_endturn_start/turn_endagent_start/agent_endcompaction_start/compaction_end
4. プロンプトの実行
Section titled “4. プロンプトの実行”セットアップ後、セッションに対してプロンプトが送信されます。
await session.prompt(effectivePrompt, { images: imageResult.images });SDK は、LLM への送信、ツール呼び出しの実行、応答のストリーミングといったエージェントループ全体を処理します。
画像の注入はプロンプト単位で行われます。OpenClaw は現在のプロンプトから画像参照を読み込み、そのターンのみ images を介して渡します。古い履歴ターンを再スキャンして画像ペイロードを再注入することはありません。
ツールアーキテクチャ
Section titled “ツールアーキテクチャ”ツールパイプライン
Section titled “ツールパイプライン”OpenClawのツールパイプラインは、エージェントが環境とやり取りするための基盤です。以下の順序で処理が行われます。
- Base Tools: piの
codingTools(read、bash、edit、write)を使用します。 - Custom Replacements: OpenClawはbashを
exec/processに置き換え、サンドボックス向けにread/edit/writeをカスタマイズします。 - OpenClaw Tools: messaging、browser、canvas、sessions、cron、Gatewayなどのツール群です。
- Channel Tools: Discord、Telegram、Slack、WhatsApp固有のアクションツールです。
- Policy Filtering: プロファイル、プロバイダー、エージェント、グループ、サンドボックスのポリシーに基づいてツールをフィルタリングします。
- Schema Normalization: GeminiやOpenAIの特性に合わせてスキーマをクリーンアップします。
- AbortSignal Wrapping: ツールがAbortSignalを尊重するようにラップされます。
ツール定義アダプター
Section titled “ツール定義アダプター”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); }, }));}ツール分割戦略
Section titled “ツール分割戦略”splitSdkTools() は、すべてのツールを customTools 経由で渡します。
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) { return { builtInTools: [], // Empty. We override everything customTools: toToolDefinitions(options.tools), };}これにより、OpenClawのポリシーフィルタリング、サンドボックス統合、および拡張ツールセットがプロバイダー間で一貫して維持されます。
システムプロンプトの構築
Section titled “システムプロンプトの構築”システムプロンプトは system-prompt.ts 内の buildAgentSystemPrompt() で構築されます。この関数は、ツール、ツール呼び出しスタイル、安全ガードレール、OpenClaw CLIリファレンス、スキル、ドキュメント、ワークスペース、サンドボックス、メッセージング、返信タグ、音声、サイレント返信、ハートビート、ランタイムメタデータ、さらに有効化された場合のメモリとリアクション、およびオプションのコンテキストファイルや追加のシステムプロンプト内容を含む完全なプロンプトを組み立てます。サブエージェントで使用される最小限のプロンプトモードでは、各セクションがトリミングされます。
プロンプトはセッション作成後に applySystemPromptOverrideToSession() を通じて適用されます。
const systemPromptOverride = createSystemPromptOverride(appendPrompt);applySystemPromptOverrideToSession(session, systemPromptOverride);セッション管理
Section titled “セッション管理”セッションファイル
Section titled “セッションファイル”セッションはツリー構造(id/parentIdによるリンク)を持つ JSONLファイルです。piの SessionManager が永続化を処理します。
const sessionManager = SessionManager.open(params.sessionFile);OpenClawは、ツールの結果を安全に扱うために、これを guardSessionManager() でラップしています。
セッションキャッシュ
Section titled “セッションキャッシュ”session-manager-cache.ts は、ファイルの繰り返し解析を避けるために SessionManager インスタンスをキャッシュします。
await prewarmSessionFile(params.sessionFile);sessionManager = SessionManager.open(params.sessionFile);trackSessionManagerAccess(params.sessionFile);limitHistoryTurns() は、チャンネルタイプ(DM対グループ)に基づいて会話履歴をトリミングします。
コンパクション
Section titled “コンパクション”コンテキストが溢れた場合、自動コンパクションがトリガーされます。一般的なオーバーフローのシグネチャには、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, ...});認証とモデル解決
Section titled “認証とモデル解決”認証プロファイル
Section titled “認証プロファイル”OpenClawは、プロバイダーごとに複数の API キーを持つ認証プロファイルストアを保持しています。
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });プロファイルは、クールダウン追跡を伴う失敗時にローテーションされます。
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 AuthStorageauthStorage.setRuntimeApiKey(model.provider, apiKeyInfo.apiKey);フェイルオーバー
Section titled “フェイルオーバー”設定されている場合、FailoverError がモデルのフォールバックをトリガーします。
if (fallbackConfigured && isFailoverErrorMessage(errorText)) { throw new FailoverError(errorText, { reason: promptFailoverReason ?? "unknown", provider, model: modelId, profileId, status: resolveFailoverStatus(promptFailoverReason), });}Pi Extensions
Section titled “Pi Extensions”OpenClaw は、特定の動作を実現するためにカスタムの Pi Extensions を読み込むことができます。これらはシステムの機能を拡張し、より高度な制御を可能にします。
Compaction Safeguard
Section titled “Compaction Safeguard”src/agents/pi-hooks/compaction-safeguard.ts は、コンパクション処理にガードレールを追加します。これには、適応型のトークン予算管理や、ツール実行の失敗、ファイル操作のサマリー作成などが含まれます。
if (resolveCompactionMode(params.cfg) === "safeguard") { setCompactionSafeguardRuntime(params.sessionManager, { maxHistoryShare }); paths.push(resolvePiExtensionPath("compaction-safeguard"));}Context Pruning
Section titled “Context Pruning”src/agents/pi-hooks/context-pruning.ts は、キャッシュの TTL(Time To Live)に基づいたコンテキストのプルーニング(不要な情報の削除)を実装しています。
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") { setContextPruningRuntime(params.sessionManager, { settings, contextWindowTokens, isToolPrunable, lastCacheTouchAt, }); paths.push(resolvePiExtensionPath("context-pruning"));}Streaming & Block Replies
Section titled “Streaming & Block Replies”OpenClaw のストリーミングおよびブロック返信機能は、生成されたテキストを効率的に処理し、適切な形式でユーザーに届けるための仕組みを提供します。
Block Chunking
Section titled “Block Chunking”EmbeddedBlockChunker は、ストリーミングされるテキストを個別の返信ブロックへと管理・分割する役割を担います。
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;Thinking/Final Tag Stripping
Section titled “Thinking/Final Tag Stripping”ストリーミング出力は処理され、<think> や <thinking> ブロックが取り除かれるほか、<final> タグで囲まれたコンテンツが抽出されます。
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => { // Strip <think>...</think> content // If enforceFinalTag, only return <final>...</final> content};Reply Directives
Section titled “Reply Directives”[[media:url]]、[[voice]]、[[reply:id]] といった返信ディレクティブは、解析され、それぞれの要素として抽出されます。
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);エラーハンドリング
Section titled “エラーハンドリング”開発中に発生する予期せぬ問題に対処することは、安定したアプリケーションを構築する上で欠かせないプロセスです。OpenClaw のエラーハンドリング機能は、発生した問題を適切に分類し、システムが正しく復旧できるように設計されています。
pi-embedded-helpers.ts は、エラーを適切に処理するために以下のように分類を行います。
isContextOverflowError(errorText) // Context too largeisCompactionFailureError(errorText) // Compaction failedisAuthAssistantError(lastAssistant) // Auth failureisRateLimitAssistantError(...) // Rate limitedisFailoverAssistantError(...) // Should failoverclassifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "timeout" | ...もし指定された思考レベルがサポートされていない場合、システムは自動的にフォールバック処理を実行します。
const fallbackThinking = pickFallbackThinkingLevel({ message: errorText, attempted: attemptedThinking,});if (fallbackThinking) { thinkLevel = fallbackThinking; continue;}サンドボックス統合
Section titled “サンドボックス統合”OpenClaw のサンドボックス統合機能を利用することで、実行環境を安全に分離し、リソースへのアクセスを制限することが可能です。サンドボックスモードが有効な場合、ツールやパスは厳格に制御されます。
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}プロバイダーごとの固有処理
Section titled “プロバイダーごとの固有処理”各AIモデルの特性や制約に合わせて、OpenClawはプロバイダーごとに最適化された処理を行っています。これにより、API経由でのやり取りがより安定し、意図した通りの挙動を実現できるようになっています。
Anthropic
Section titled “Anthropic”Anthropicのモデルを使用する際、特定の出力形式やツール利用の制約を適切に処理するための機能です。
- Refusal(拒否)が発生した際のマジック文字列をクリーンアップします。
- 連続するロール(役割)に対するバリデーションを調整します。
- アップストリームのPiツールパラメータに対する厳格なバリデーションを実行します。
Google/Gemini
Section titled “Google/Gemini”GoogleおよびGeminiモデル特有のツール定義やスキーマの取り扱いを最適化します。
- プラグインが所有するツールスキーマのサニタイズ処理を行い、JSON形式の整合性を保ちます。
OpenAI
Section titled “OpenAI”OpenAIの各モデル、特にCodexモデルや推論モデルの特性に合わせた調整を行います。
- Codexモデル向けに
apply_patchツールを適用します。 - 推論レベル(Thinking level)のダウングレードが発生した際のハンドリングを行います。
TUI 統合
Section titled “TUI 統合”OpenClawには、pi-tuiコンポーネントを直接利用したローカルのTUIモードが備わっています。この機能により、ターミナル上で直接対話的な操作が可能になります。
import { ... } from "@mariozechner/pi-tui";この統合により、piのネイティブモードと同様のインタラクティブなターミナル体験をOpenClaw上で提供しています。
Pi CLI との主な違い
Section titled “Pi CLI との主な違い”OpenClaw を利用する際、従来の Pi CLI と比較してどのような点が異なるのかを理解しておくことは非常に重要です。以下の表に、両者の主な違いをまとめました。
| 項目 | Pi CLI | OpenClaw Embedded |
|---|---|---|
| 呼び出し方法 | pi コマンド / RPC | SDK 経由の createAgentSession() |
| ツール | デフォルトのコーディングツール | カスタム OpenClaw ツールスイート |
| システムプロンプト | AGENTS.md + プロンプト | チャネル/コンテキストごとの動的生成 |
| セッションストレージ | ~/.pi/agent/sessions/ | ~/.openclaw/agents/<agentId>/sessions/ (または $OPENCLAW_STATE_DIR/agents/<agentId>/sessions/) |
| 認証 | 単一の認証情報 | ローテーション可能なマルチプロファイル |
| 拡張機能 | ディスクから読み込み | プログラムによる読み込み + ディスクパス |
| イベント処理 | TUI レンダリング | コールバックベース (onBlockReply など) |
今後の検討事項
Section titled “今後の検討事項”開発を進める中で、将来的に見直しや改善が必要となる可能性のある領域がいくつか存在します。これらを把握しておくことで、より効率的な開発が可能になります。
- ツールシグネチャの調整: 現在、pi-agent-core と pi-coding-agent のシグネチャ間で適応処理を行っています。
- セッションマネージャーのラップ:
guardSessionManagerは安全性を高めますが、同時に複雑さも増大させています。 - 拡張機能の読み込み: pi の
ResourceLoaderをより直接的に活用できる可能性があります。 - ストリーミングハンドラーの複雑性:
subscribeEmbeddedPiSessionの規模が大きくなりすぎています。 - プロバイダー固有の動作: pi 側で一元管理できる可能性のある、プロバイダー固有のコードパスが多く存在します。
OpenClaw の Pi 統合に関するカバレッジは、以下のテストスイートによって網羅されています。
src/agents/pi-*.test.tssrc/agents/pi-auth-json.test.tssrc/agents/pi-embedded-*.test.tssrc/agents/pi-embedded-helpers*.test.tssrc/agents/pi-embedded-runner*.test.tssrc/agents/pi-embedded-runner/**/*.test.tssrc/agents/pi-embedded-subscribe*.test.tssrc/agents/pi-tools*.test.tssrc/agents/pi-tool-definition-adapter*.test.tssrc/agents/pi-settings.test.tssrc/agents/pi-hooks/**/*.test.ts
ライブテストやオプトイン形式のテストについては、以下を参照してください。
src/agents/pi-embedded-runner-extraparams.live.test.ts(OPENCLAW_LIVE_TEST=1を有効にする必要があります)
現在の実行コマンドについては、Pi Development Workflow を確認してください。
OpenClaw Expert
まだ解決しませんか?
このページで解決しない場合は、OpenClaw Expertに直接質問してください。