OpenClaw Architektur: Pi SDK nahtlos integrieren
Übersicht
Abschnitt betitelt „Übersicht“OpenClaw nutzt das pi SDK, um einen AI Coding Agent direkt in seine Messaging Gateway Architektur einzubetten. Anstatt pi als Subprozess zu starten oder den RPC-Modus zu verwenden, importiert und instanziiert OpenClaw die AgentSession von pi direkt über createAgentSession(). Dieser eingebettete Ansatz bietet dir:
- Volle Kontrolle über den Session-Lifecycle und das Event-Handling
- Injection von Custom Tools (Messaging, Sandbox, kanalspezifische Aktionen)
- Anpassung des System Prompts pro Kanal/Kontext
- Session-Persistenz mit Unterstützung für Branching/Compaction
- Rotation von Multi-Account Auth-Profilen mit Failover
- Provider-agnostisches Model-Switching
Paket-Abhängigkeiten
Abschnitt betitelt „Paket-Abhängigkeiten“{ "@mariozechner/pi-agent-core": "0.61.1", "@mariozechner/pi-ai": "0.61.1", "@mariozechner/pi-coding-agent": "0.61.1", "@mariozechner/pi-tui": "0.61.1"}| Paket | Zweck |
|---|---|
pi-ai | Core LLM Abstraktionen: Model, streamSimple, Message-Typen, Provider-APIs |
pi-agent-core | Agent-Loop, Tool-Ausführung, AgentMessage Typen |
pi-coding-agent | High-Level SDK: createAgentSession, SessionManager, AuthStorage, ModelRegistry, integrierte Tools |
pi-tui | Terminal UI Komponenten (genutzt im lokalen TUI-Modus von OpenClaw) |
Dateistruktur
Abschnitt betitelt „Dateistruktur“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│ └── ...└── ...Kanalspezifische Message Action Runtimes befinden sich jetzt in den Plugin-eigenen Extension-Verzeichnissen statt unter src/agents/tools, zum Beispiel:
- die Discord Plugin Action Runtime Dateien
- die Slack Plugin Action Runtime Datei
- die Telegram Plugin Action Runtime Datei
- die WhatsApp Plugin Action Runtime Datei
Zentraler Integrations-Flow
Abschnitt betitelt „Zentraler Integrations-Flow“1. Einen Embedded Agent ausführen
Abschnitt betitelt „1. Einen Embedded Agent ausführen“Der Haupteinstiegspunkt ist runEmbeddedPiAgent() in pi-embedded-runner/run.ts:
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-20250514", timeoutMs: 120_000, runId: "run-abc", onBlockReply: async (payload) => { await sendToChannel(payload.text, payload.mediaUrls); },});2. Session-Erstellung
Abschnitt betitelt „2. Session-Erstellung“Innerhalb von runEmbeddedAttempt() (aufgerufen durch runEmbeddedPiAgent()) wird das pi SDK verwendet:
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. Event-Subscription
Abschnitt betitelt „3. Event-Subscription“subscribeEmbeddedPiSession() abonniert die Events der AgentSession von pi:
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,});Folgende Events werden verarbeitet:
message_start/message_end/message_update(Streaming von Text/Thinking)tool_execution_start/tool_execution_update/tool_execution_endturn_start/turn_endagent_start/agent_endauto_compaction_start/auto_compaction_end
4. Prompting
Abschnitt betitelt „4. Prompting“Nach dem Setup wird die Session gepromptet:
await session.prompt(effectivePrompt, { images: imageResult.images });Das SDK übernimmt den kompletten Agent-Loop: Senden an das LLM, Ausführen von Tool-Calls und Streaming der Antworten.
Die Image-Injection ist Prompt-lokal: OpenClaw lädt Image-Referenzen aus dem aktuellen Prompt und übergibt sie via images nur für diesen Turn. Es findet kein erneutes Scannen älterer History-Turns statt, um Image-Payloads erneut zu injizieren.
Tool-Architektur
Abschnitt betitelt „Tool-Architektur“Tool-Pipeline
Abschnitt betitelt „Tool-Pipeline“Die Tool-Pipeline von OpenClaw folgt einem klaren Ablauf, um sicherzustellen, dass jeder Befehl sicher und im richtigen Kontext ausgeführt wird:
- Base Tools: pi’s
codingTools(read, bash, edit, write) - Custom Replacements: OpenClaw ersetzt bash durch
exec/processund passt read/edit/write für die Sandbox an. - OpenClaw Tools: messaging, browser, canvas, sessions, cron, gateway, etc.
- Channel Tools: Spezifische Action-Tools für Discord, Telegram, Slack oder WhatsApp.
- Policy Filtering: Tools werden nach Profil, Provider, Agent, Gruppe und Sandbox-Policies gefiltert.
- Schema Normalization: Schemas werden bereinigt, um Eigenheiten von Gemini oder OpenAI abzufangen.
- AbortSignal Wrapping: Tools werden so verpackt, dass sie Abort-Signale respektieren.
Tool Definition Adapter
Abschnitt betitelt „Tool Definition Adapter“Die AgentTool-Klasse von pi-agent-core nutzt eine andere execute-Signatur als die ToolDefinition von pi-coding-agent. Der Adapter in pi-tool-definition-adapter.ts schlägt hier die Brücke:
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); }, }));}Tool Split Strategy
Abschnitt betitelt „Tool Split Strategy“Die Funktion splitSdkTools() übergibt alle Tools über customTools:
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) { return { builtInTools: [], // Empty. We override everything customTools: toToolDefinitions(options.tools), };}Das stellt sicher, dass die Policy-Filterung von OpenClaw, die Sandbox-Integration, das erweiterte Toolset und die Provider-Konsistenz über alle Plattformen hinweg erhalten bleiben.
Erstellung des System Prompts
Abschnitt betitelt „Erstellung des System Prompts“Der System Prompt wird in buildAgentSystemPrompt() (system-prompt.ts) generiert. Er setzt einen vollständigen Prompt aus verschiedenen Sektionen zusammen. Dazu gehören Tooling, Tool Call Style, Safety-Leitplanken, die OpenClaw CLI-Referenz, Skills, Docs, Workspace, Sandbox, Messaging, Reply Tags, Voice, Silent Replies, Heartbeats und Runtime-Metadaten. Falls aktiviert, kommen Memory und Reactions sowie optionale Kontext-Dateien hinzu. Für Subagents werden diese Sektionen gekürzt, um einen minimalen Prompt-Modus zu nutzen.
Nachdem eine Session erstellt wurde, wird der Prompt über applySystemPromptOverrideToSession() angewendet:
const systemPromptOverride = createSystemPromptOverride(appendPrompt);applySystemPromptOverrideToSession(session, systemPromptOverride);Session-Management
Abschnitt betitelt „Session-Management“Session-Dateien
Abschnitt betitelt „Session-Dateien“Sessions werden als JSONL-Dateien mit einer Baumstruktur gespeichert (Verknüpfung über id/parentId). Der SessionManager von pi kümmert sich um die Speicherung:
const sessionManager = SessionManager.open(params.sessionFile);OpenClaw erweitert dies mit guardSessionManager(), um die Sicherheit der Tool-Ergebnisse zu gewährleisten.
Session-Caching
Abschnitt betitelt „Session-Caching“Die Datei session-manager-cache.ts speichert Instanzen des SessionManagers zwischen, damit Dateien nicht ständig neu eingelesen werden müssen:
await prewarmSessionFile(params.sessionFile);sessionManager = SessionManager.open(params.sessionFile);trackSessionManagerAccess(params.sessionFile);History-Limitierung
Abschnitt betitelt „History-Limitierung“Mit limitHistoryTurns() wird der Konversationsverlauf gekürzt. Dabei wird unterschieden, ob es sich um einen DM (Direktnachricht) oder einen Gruppenchat handelt.
Kompaktierung
Abschnitt betitelt „Kompaktierung“Wenn der Kontext überläuft, wird automatisch eine Kompaktierung ausgelöst. Für die manuelle Kompaktierung ist compactEmbeddedPiSessionDirect() zuständig:
const compactResult = await compactEmbeddedPiSessionDirect({ sessionId, sessionFile, provider, model, ...});Authentifizierung & Modell-Auflösung
Abschnitt betitelt „Authentifizierung & Modell-Auflösung“Auth-Profile
Abschnitt betitelt „Auth-Profile“OpenClaw verwaltet einen Speicher für Auth-Profile, der mehrere API-Keys pro Provider enthalten kann:
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });Wenn Fehler auftreten, rotieren die Profile automatisch, wobei ein Cooldown-Tracking erfolgt:
await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });const rotated = await advanceAuthProfile();Modell-Auflösung
Abschnitt betitelt „Modell-Auflösung“Die Auflösung des passenden Modells erfolgt so:
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);Failover
Abschnitt betitelt „Failover“Falls konfiguriert, löst ein FailoverError den Wechsel auf ein Ersatzmodell aus:
if (fallbackConfigured && isFailoverErrorMessage(errorText)) { throw new FailoverError(errorText, { reason: promptFailoverReason ?? "unknown", provider, model: modelId, profileId, status: resolveFailoverStatus(promptFailoverReason), });}Pi-Extensions
Abschnitt betitelt „Pi-Extensions“OpenClaw lädt custom Pi-Extensions, um spezialisiertes Verhalten zu ermöglichen:
Compaction Safeguard
Abschnitt betitelt „Compaction Safeguard“src/agents/pi-hooks/compaction-safeguard.ts fügt Guardrails zur Compaction hinzu. Das beinhaltet ein adaptives Token-Budgeting sowie Zusammenfassungen von Tool-Fehlern und Dateioperationen:
if (resolveCompactionMode(params.cfg) === "safeguard") { setCompactionSafeguardRuntime(params.sessionManager, { maxHistoryShare }); paths.push(resolvePiExtensionPath("compaction-safeguard"));}Context Pruning
Abschnitt betitelt „Context Pruning“src/agents/pi-hooks/context-pruning.ts setzt Context Pruning um, das auf der Cache-TTL basiert:
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") { setContextPruningRuntime(params.sessionManager, { settings, contextWindowTokens, isToolPrunable, lastCacheTouchAt, }); paths.push(resolvePiExtensionPath("context-pruning"));}Streaming & Block-Antworten
Abschnitt betitelt „Streaming & Block-Antworten“Block Chunking
Abschnitt betitelt „Block Chunking“EmbeddedBlockChunker verwaltet das Streaming von Text in diskrete Antwort-Blöcke:
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;Thinking/Final Tag Stripping
Abschnitt betitelt „Thinking/Final Tag Stripping“Der Streaming-Output wird verarbeitet, um <think>/<thinking>-Blöcke zu entfernen und <final>-Inhalte zu extrahieren:
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => { // Strip <think>...</think> content // If enforceFinalTag, only return <final>...</final> content};Reply Directives
Abschnitt betitelt „Reply Directives“Reply Directives wie [[media:url]], [[voice]] oder [[reply:id]] werden geparst und extrahiert:
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);Fehlerbehandlung
Abschnitt betitelt „Fehlerbehandlung“Fehlerklassifizierung
Abschnitt betitelt „Fehlerklassifizierung“In pi-embedded-helpers.ts werden Fehler klassifiziert, damit du sie gezielt abfangen kannst. Das ist der beste Weg, um auf spezifische Probleme wie Context-Überschreitungen oder Rate-Limits zu reagieren:
isContextOverflowError(errorText) // Context too largeisCompactionFailureError(errorText) // Compaction failedisAuthAssistantError(lastAssistant) // Auth failureisRateLimitAssistantError(...) // Rate limitedisFailoverAssistantError(...) // Should failoverclassifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "timeout" | ...Thinking Level Fallback
Abschnitt betitelt „Thinking Level Fallback“Falls ein Thinking Level nicht unterstützt wird, solltest du einen Fallback implementieren. So stellst du sicher, dass dein Workflow nicht unterbrochen wird, sondern mit einer alternativen Stufe fortgesetzt wird:
const fallbackThinking = pickFallbackThinkingLevel({ message: errorText, attempted: attemptedThinking,});if (fallbackThinking) { thinkLevel = fallbackThinking; continue;}Sandbox-Integration
Abschnitt betitelt „Sandbox-Integration“Wenn du den Sandbox-Modus aktivierst, werden Tools und Pfade automatisch eingeschränkt. Das ist eine wichtige Sicherheitsmaßnahme, um die Ausführung von Code zu isolieren:
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}Sobald sandboxRoot aktiv ist, gelten folgende Regeln für deine Umgebung:
- Du verwendest isolierte Tools für read/edit/write Operationen.
- Alle Exec-Befehle werden direkt innerhalb des Containers ausgeführt.
- Der Browser greift auf eine spezielle Bridge-URL zu.
- Der Zugriff auf das lokale Dateisystem wird strikt unterbunden.
Provider-spezifische Verarbeitung
Abschnitt betitelt „Provider-spezifische Verarbeitung“Jeder Provider hat seine eigenen Eigenheiten. Damit du dich nicht mit den Details der verschiedenen APIs herumschlagen musst, übernimmt OpenClaw die notwendigen Anpassungen im Hintergrund.
Anthropic
Abschnitt betitelt „Anthropic“- Refusal Magic String Scrubbing sowie Turn-Validierung für aufeinanderfolgende Rollen
- Kompatibilität für Claude Code Parameter
Google/Gemini
Abschnitt betitelt „Google/Gemini“- Fixes für die Turn-Reihenfolge (
applyGoogleTurnOrderingFix) und Tool-Schema Sanitization (sanitizeToolsForGoogle) - Session-History Sanitization (
sanitizeSessionHistory) sowie API-spezifische Bereinigungen
apply_patchTool für Codex-Modelle- Handling von Thinking-Level Downgrades
TUI-Integration
Abschnitt betitelt „TUI-Integration“OpenClaw bietet dir einen lokalen TUI-Modus, der pi-tui Komponenten direkt nutzt:
import { ... } from "@mariozechner/pi-tui";Das ermöglicht dir eine interaktive Terminal-Erfahrung, die dem nativen Modus von pi entspricht.
Wesentliche Unterschiede zum Pi CLI
Abschnitt betitelt „Wesentliche Unterschiede zum Pi CLI“| Aspekt | Pi CLI | OpenClaw Embedded |
|---|---|---|
| Aufruf | pi Command / RPC | SDK via createAgentSession() |
| Tools | Standard Coding-Tools | Eigene OpenClaw Tool-Suite |
| System-Prompt | AGENTS.md + Prompts | Dynamisch pro Channel/Kontext |
| Session-Speicher | ~/.pi/agent/sessions/ | ~/.openclaw/agents/<agentId>/sessions/ (oder $OPENCLAW_STATE_DIR/agents/<agentId>/sessions/) |
| Auth | Einzelne Zugangsdaten | Multi-Profile mit Rotation |
| Extensions | Von Disk geladen | Programmatisch + Disk-Pfade |
| Event-Handling | TUI-Rendering | Callback-basiert (onBlockReply, etc.) |
Zukünftige Überlegungen
Abschnitt betitelt „Zukünftige Überlegungen“Hier sind einige Bereiche, die wir eventuell noch einmal überarbeiten müssen:
- Anpassung der Tool-Signaturen: Aktuell findet eine manuelle Anpassung zwischen den Signaturen von pi-agent-core und pi-coding-agent statt.
- Session-Manager-Wrapper:
guardSessionManagersorgt zwar für Sicherheit, erhöht aber die Komplexität des Codes. - Laden von Extensions: Wir könnten den
ResourceLoadervon pi direkter nutzen. - Komplexität der Streaming-Handler: Die Funktion
subscribeEmbeddedPiSessionist mittlerweile sehr umfangreich geworden. - Provider-Eigenheiten: Es gibt viele Provider-spezifische Codepfade, die pi potenziell selbst handhaben könnte.
Die Pi-Integration wird durch diese Test-Suites abgedeckt:
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
Live/Opt-in:
src/agents/pi-embedded-runner-extraparams.live.test.ts(aktiviereOPENCLAW_LIVE_TEST=1)
Aktuelle Run-Commands findest du unter Pi Development Workflow.
OpenClaw Expert
Noch festgefahren?
Wenn diese Seite nicht hilft, frage OpenClaw Expert nach Schritt-fuer-Schritt-Loesungen.