Architecture d'intégration Pi
Architecture d’intégration Pi
Section intitulée « Architecture d’intégration Pi »Ce document décrit comment OpenClaw s’intègre avec pi-coding-agent et ses packages frères (pi-ai, pi-agent-core, pi-tui) pour alimenter ses capacités d’agent IA.
Vue d’ensemble
Section intitulée « Vue d’ensemble »OpenClaw utilise le SDK pi pour embarquer un agent de codage IA dans son architecture de passerelle de messagerie. Au lieu de générer pi en tant que sous-processus ou d’utiliser le mode RPC, OpenClaw importe et instancie directement le AgentSession de pi via createAgentSession(). Cette approche embarquée fournit :
- Contrôle total du cycle de vie de la session et de la gestion des événements
- Injection d’outils personnalisés (messagerie, bac à sable, actions spécifiques au canal)
- Personnalisation du prompt système par canal/contexte
- Persistance de la session avec support du branchement/compactage
- Rotation des profils d’authentification multi-comptes avec basculement
- Bascule de modèle agnostique de fournisseur
Dépendances de package
Section intitulée « Dépendances de package »{ "@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"}| Package | Objectif |
|---|---|
pi-ai | Abstractions centrales LLM : Model, streamSimple, types de messages, APIs de fournisseur |
pi-agent-core | Boucle d’agent, exécution d’outils, types AgentMessage |
pi-coding-agent | SDK de haut niveau : createAgentSession, SessionManager, AuthStorage, ModelRegistry, outils intégrés |
pi-tui | Composants d’interface utilisateur terminal (utilisés dans le mode OpenClaw local d’TUI) |
Structure des fichiers
Section intitulée « Structure des fichiers »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│ └── ...└── ...Les runtimes d’action de message spécifiques aux canaux résident désormais dans les répertoires d’extension appartenant aux plugins
au lieu d’être sous src/agents/tools, par exemple :
- les fichiers d’exécution des actions du plugin Discord
- le fichier d’exécution des actions du plugin Slack
- le fichier d’exécution des actions du plugin Telegram
- le fichier d’exécution des actions du plugin WhatsApp
Flux d’intégration principal
Section intitulée « Flux d’intégration principal »1. Exécution d’un agent intégré
Section intitulée « 1. Exécution d’un agent intégré »Le point d’entrée principal est runEmbeddedPiAgent() dans 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. Création de session
Section intitulée « 2. Création de session »Dans runEmbeddedAttempt() (appelé par runEmbeddedPiAgent()), le SDK pi est utilisé :
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. Abonnement aux événements
Section intitulée « 3. Abonnement aux événements »subscribeEmbeddedPiSession() s’abonne aux événements AgentSession de 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,});Les événements gérés incluent :
message_start/message_end/message_update(streaming text/thinking)tool_execution_start/tool_execution_update/tool_execution_endturn_start/turn_endagent_start/agent_endauto_compaction_start/auto_compaction_end
4. Prompting
Section intitulée « 4. Prompting »Après la configuration, la session est sollicitée :
await session.prompt(effectivePrompt, { images: imageResult.images });Le SDK gère la boucle complète de l’agent : envoi au LLM, exécution des appels de tools, diffusion des réponses.
L’injection d’images est locale au prompt : OpenClaw charge les références d’images à partir du prompt actuel et
les transmet via images pour ce tour uniquement. Il ne ré-analyse pas les tours d’historique plus anciens
pour réinjecter les payloads d’images.
Architecture des tools
Section intitulée « Architecture des tools »Pipeline des tools
Section intitulée « Pipeline des tools »- Outils de base :
codingToolsde pi (read, bash, edit, write) - Remplacements personnalisés : OpenClaw remplace bash par
exec/process, personnalise read/edit/write pour le bac à sable - Outils OpenClaw : messagerie, navigateur, canvas, sessions, cron, passerelle, etc.
- Outils de canal : outils d’action spécifiques à Discord/Telegram/Slack/WhatsApp
- Filtrage par stratégie : Outils filtrés par profil, fournisseur, agent, groupe, stratégies de bac à sable
- Normalisation des schémas : Schémas nettoyés pour les particularités de Gemini/OpenAI
- Encapsulation d’AbortSignal : Outils encapsulés pour respecter les signaux d’abandon
Adaptateur de définition d’outil
Section intitulée « Adaptateur de définition d’outil »Le AgentTool de pi-agent-core a une signature execute différente de celle du ToolDefinition de pi-coding-agent. L’adaptateur dans pi-tool-definition-adapter.ts fait le pont entre les deux :
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); }, }));}Stratégie de division d’outils
Section intitulée « Stratégie de division d’outils »splitSdkTools() transmet tous les outils via customTools :
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) { return { builtInTools: [], // Empty. We override everything customTools: toToolDefinitions(options.tools), };}Cela garantit que le filtrage par stratégie, l’intégration du bac à sable et l’ensemble d’outils étendus de OpenClaw restent cohérents d’un fournisseur à l’autre.
Construction du prompt système
Section intitulée « Construction du prompt système »Le système d’invite est construit dans buildAgentSystemPrompt() (system-prompt.ts). Il assemble une invite complète avec des sections incluant Tooling, Tool Call Style, Safety guardrails, la référence du OpenClaw CLI, Skills, Docs, Workspace, Sandbox, Messaging, Reply Tags, Voice, Silent Replies, Heartbeats, les métadonnées d’exécution, plus Memory et Reactions lorsqu’ils sont activés, ainsi que des fichiers de contexte optionnels et du contenu d’invite système supplémentaire. Les sections sont réduites pour le mode d’invite minimal utilisé par les sous-agents.
L’invite est appliquée après la création de la session via applySystemPromptOverrideToSession() :
const systemPromptOverride = createSystemPromptOverride(appendPrompt);applySystemPromptOverrideToSession(session, systemPromptOverride);Gestion de session
Section intitulée « Gestion de session »Fichiers de session
Section intitulée « Fichiers de session »Les sessions sont des fichiers JSONL avec une structure arborescente (liaison id/parentId). Le SessionManager de Pi gère la persistance :
const sessionManager = SessionManager.open(params.sessionFile);OpenClaw encapsule cela avec guardSessionManager() pour la sécurité des résultats des outils.
Mise en cache de session
Section intitulée « Mise en cache de session »session-manager-cache.ts met en cache les instances SessionManager pour éviter l’analyse répétée des fichiers :
await prewarmSessionFile(params.sessionFile);sessionManager = SessionManager.open(params.sessionFile);trackSessionManagerAccess(params.sessionFile);Limitation de l’historique
Section intitulée « Limitation de l’historique »limitHistoryTurns() réduit l’historique des conversations en fonction du type de channel (DM vs groupe).
Compactage
Section intitulée « Compactage »L’auto-compaction se déclenche en cas de dépassement de contexte. compactEmbeddedPiSessionDirect() gère la compaction manuelle :
const compactResult = await compactEmbeddedPiSessionDirect({ sessionId, sessionFile, provider, model, ...});Authentification et résolution de modèle
Section intitulée « Authentification et résolution de modèle »Profils d’authentification
Section intitulée « Profils d’authentification »OpenClaw maintient un magasin de profils d’authentification avec plusieurs clés API par fournisseur :
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });Les profils tournent en cas d’échec avec un suivi du temps de refroidissement :
await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });const rotated = await advanceAuthProfile();Résolution de modèle
Section intitulée « Résolution de modèle »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);Basculement
Section intitulée « Basculement »FailoverError déclenche le basculement de model lorsque configuré :
if (fallbackConfigured && isFailoverErrorMessage(errorText)) { throw new FailoverError(errorText, { reason: promptFailoverReason ?? "unknown", provider, model: modelId, profileId, status: resolveFailoverStatus(promptFailoverReason), });}Extensions Pi
Section intitulée « Extensions Pi »OpenClaw charge des extensions pi personnalisées pour un comportement spécialisé :
Garantie de compactage
Section intitulée « Garantie de compactage »src/agents/pi-hooks/compaction-safeguard.ts ajoute des garde-fous à la compaction, notamment une budgétisation adaptative des jetons ainsi que des résumés des échecs d’outils et des opérations de fichiers :
if (resolveCompactionMode(params.cfg) === "safeguard") { setCompactionSafeguardRuntime(params.sessionManager, { maxHistoryShare }); paths.push(resolvePiExtensionPath("compaction-safeguard"));}Élagage du contexte
Section intitulée « Élagage du contexte »src/agents/pi-hooks/context-pruning.ts implémente l’élagage du contexte basé sur le TTL du cache :
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") { setContextPruningRuntime(params.sessionManager, { settings, contextWindowTokens, isToolPrunable, lastCacheTouchAt, }); paths.push(resolvePiExtensionPath("context-pruning"));}Réponses en flux et par blocs
Section intitulée « Réponses en flux et par blocs »Découpage en blocs
Section intitulée « Découpage en blocs »EmbeddedBlockChunker gère le flux de texte en blocs de réponse distincts :
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;Suppression des balises Thinking/Final
Section intitulée « Suppression des balises Thinking/Final »La sortie en continu est traitée pour supprimer les blocs <think>/<thinking> et extraire le contenu <final> :
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => { // Strip <think>...</think> content // If enforceFinalTag, only return <final>...</final> content};Directives de réponse
Section intitulée « Directives de réponse »Les directives de réponse comme [[media:url]], [[voice]], [[reply:id]] sont analysées et extraites :
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);Gestion des erreurs
Section intitulée « Gestion des erreurs »Classification des erreurs
Section intitulée « Classification des erreurs »pi-embedded-helpers.ts classe les erreurs pour un traitement approprié :
isContextOverflowError(errorText) // Context too largeisCompactionFailureError(errorText) // Compaction failedisAuthAssistantError(lastAssistant) // Auth failureisRateLimitAssistantError(...) // Rate limitedisFailoverAssistantError(...) // Should failoverclassifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "timeout" | ...Repli du niveau de réflexion
Section intitulée « Repli du niveau de réflexion »Si un niveau de réflexion n’est pas pris en charge, il revient à :
const fallbackThinking = pickFallbackThinkingLevel({ message: errorText, attempted: attemptedThinking,});if (fallbackThinking) { thinkLevel = fallbackThinking; continue;}Intégration Sandbox
Section intitulée « Intégration Sandbox »Lorsque le mode bac à sable est activé, les outils et les chemins sont contraints :
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}Gestion spécifique au fournisseur
Section intitulée « Gestion spécifique au fournisseur »Anthropic
Section intitulée « Anthropic »- Nettoyage de la chaîne magique de refus
- Validation des tours pour les rôles consécutifs
- Compatibilité des paramètres Claude Code
Google/Gemini
Section intitulée « Google/Gemini »- Corrections de l’ordre des tours (
applyGoogleTurnOrderingFix) - Assainissement du schéma d’outils (
sanitizeToolsForGoogle) - Assainissement de l’historique de session (
sanitizeSessionHistory)
- outil
apply_patchpour les modèles Codex - Gestion de la réduction du niveau de réflexion
Intégration TUI
Section intitulée « Intégration TUI »OpenClaw possède également un mode local TUI qui utilise directement les composants pi-tui :
import { ... } from "@mariozechner/pi-tui";Cela fournit l’expérience interactive de terminal similaire au mode natif de pi.
Principales différences avec le Pi CLI
Section intitulée « Principales différences avec le Pi CLI »| Aspect | Pi CLI | OpenClaw intégré |
|---|---|---|
| Invocation | commande pi / RPC | SDK via createAgentSession() |
| Outils | Outils de codage par défaut | Suite d’outils personnalisée OpenClaw |
| Prompt système | AGENTS.md + prompts | Dynamique par canal/contexte |
| Stockage de session | ~/.pi/agent/sessions/ | ~/.openclaw/agents/<agentId>/sessions/ (ou $OPENCLAW_STATE_DIR/agents/<agentId>/sessions/) |
| Auth | Identifiant unique | Multi-profil avec rotation |
| Extensions | Chargé depuis le disque | Chemins programmables + disque |
| Gestion des événements | TUI rendering | Basé sur des rappels (onBlockReply, etc.) |
Considérations futures
Section intitulée « Considérations futures »Domaines susceptibles d’être retravaillés :
- Alignement des signatures d’outils : Adaptation actuellement entre les signatures pi-agent-core et pi-coding-agent
- Encapsulation du gestionnaire de session :
guardSessionManagerajoute de la sécurité mais augmente la complexité - Chargement des extensions : Pourrait utiliser le
ResourceLoaderde pi plus directement - Complexité du gestionnaire de streaming :
subscribeEmbeddedPiSessionest devenu volumineux - Spécificités des providers : De nombreux chemins de code spécifiques aux providers que pi pourrait potentiellement gérer
La couverture de l’intération Pi s’étend sur ces suites :
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
En direct/opt-in :
src/agents/pi-embedded-runner-extraparams.live.test.ts(activerOPENCLAW_LIVE_TEST=1)
Pour les commandes d’exécution actuelles, voir Pi Development Workflow.