Integra OpenClaw con pi-coding-agent: Guía de arquitectura
Descripción general
Sección titulada «Descripción general»OpenClaw utiliza el SDK de pi para integrar un agente de codificación con IA en su arquitectura de Gateway de mensajería. En lugar de ejecutar pi como un subproceso o usar el modo RPC, OpenClaw importa e instancia directamente el AgentSession de pi a través de createAgentSession(). Este enfoque embebido te ofrece:
- Control total sobre el ciclo de vida de la sesión y el manejo de eventos
- Inyección de herramientas personalizadas (mensajería, sandbox, acciones específicas de canal)
- Personalización del system prompt por canal o contexto
- Persistencia de sesiones con soporte para ramificación y compactación
- Rotación de perfiles de autenticación multi-cuenta con failover
- Cambio de modelo independiente del proveedor
Dependencias de paquetes
Sección titulada «Dependencias de paquetes»{ "@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"}| Paquete | Propósito |
|---|---|
pi-ai | Abstracciones core de LLM: Model, streamSimple, tipos de mensajes, APIs de proveedores |
pi-agent-core | Bucle del agente, ejecución de herramientas, tipos de AgentMessage |
pi-coding-agent | SDK de alto nivel: createAgentSession, SessionManager, AuthStorage, ModelRegistry, herramientas integradas |
pi-tui | Componentes de UI para terminal (usados en el modo TUI local de OpenClaw) |
Estructura de archivos
Sección titulada «Estructura de archivos»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│ └── ...└── ...Los runtimes de acciones de mensajes específicos del canal ahora residen en los directorios de extensiones propiedad de los plugins en lugar de estar bajo src/agents/tools, por ejemplo:
- Los archivos del runtime de acciones del plugin de Discord
- El archivo del runtime de acciones del plugin de Slack
- El archivo del runtime de acciones del plugin de Telegram
- El archivo del runtime de acciones del plugin de WhatsApp
Flujo de integración principal
Sección titulada «Flujo de integración principal»1. Ejecutar un agente embebido
Sección titulada «1. Ejecutar un agente embebido»El punto de entrada principal es runEmbeddedPiAgent() en 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. Creación de la sesión
Sección titulada «2. Creación de la sesión»Dentro de runEmbeddedAttempt() (llamado por runEmbeddedPiAgent()), se utiliza el SDK de pi:
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. Suscripción a eventos
Sección titulada «3. Suscripción a eventos»subscribeEmbeddedPiSession() se suscribe a los eventos de 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,});Los eventos manejados incluyen:
message_start/message_end/message_update(streaming de texto/pensamiento)tool_execution_start/tool_execution_update/tool_execution_endturn_start/turn_endagent_start/agent_endauto_compaction_start/auto_compaction_end
4. Prompting
Sección titulada «4. Prompting»Después de la configuración, se envía el prompt a la sesión:
await session.prompt(effectivePrompt, { images: imageResult.images });El SDK gestiona el bucle completo del agente: envío al LLM, ejecución de llamadas a herramientas y streaming de respuestas.
La inyección de imágenes es local al prompt: OpenClaw carga las referencias de imágenes del prompt actual y las pasa mediante images solo para ese turno. No vuelve a escanear turnos del historial antiguo para reinyectar payloads de imágenes.
Arquitectura de Herramientas
Sección titulada «Arquitectura de Herramientas»Pipeline de Herramientas
Sección titulada «Pipeline de Herramientas»- Base Tools:
codingToolsde pi (read, bash, edit, write) - Custom Replacements: OpenClaw reemplaza bash con
exec/processy personaliza read/edit/write para el sandbox - OpenClaw Tools: messaging, browser, canvas, sessions, cron, Gateway, etc.
- Channel Tools: Herramientas de acción específicas para Discord/Telegram/Slack/WhatsApp
- Policy Filtering: Herramientas filtradas por perfil, provider, agent, grupo y políticas de sandbox
- Schema Normalization: Schemas limpios para manejar las peculiaridades de Gemini/OpenAI
- AbortSignal Wrapping: Herramientas envueltas para respetar señales de aborto
Adaptador de Definición de Herramientas
Sección titulada «Adaptador de Definición de Herramientas»AgentTool de pi-agent-core tiene una firma de execute diferente a la de ToolDefinition de pi-coding-agent. El adaptador en pi-tool-definition-adapter.ts sirve de puente entre ambas:
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); }, }));}Estrategia de División de Herramientas
Sección titulada «Estrategia de División de Herramientas»splitSdkTools() pasa todas las herramientas a través de customTools:
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) { return { builtInTools: [], // Empty. We override everything customTools: toToolDefinitions(options.tools), };}Esto garantiza que el filtrado de políticas de OpenClaw, la integración con el sandbox y el conjunto extendido de herramientas se mantengan consistentes en todos los providers.
Construcción del System Prompt
Sección titulada «Construcción del System Prompt»El system prompt se construye en buildAgentSystemPrompt() (system-prompt.ts). Este proceso monta un prompt completo con secciones que incluyen Tooling, Tool Call Style, guardrails de seguridad, referencia a la CLI de OpenClaw, Skills, Docs, Workspace, Sandbox, Messaging, Reply Tags, Voice, Silent Replies, Heartbeats y metadatos de Runtime. También incluye Memory y Reactions cuando están activados, además de archivos de contexto opcionales y contenido extra para el system prompt. Las secciones se recortan para el modo de prompt mínimo que usan los subagents.
El prompt se aplica después de crear la sesión mediante applySystemPromptOverrideToSession():
const systemPromptOverride = createSystemPromptOverride(appendPrompt);applySystemPromptOverrideToSession(session, systemPromptOverride);Gestión de Sesiones
Sección titulada «Gestión de Sesiones»Archivos de Sesión
Sección titulada «Archivos de Sesión»Las sesiones son archivos JSONL con una estructura de árbol (vinculados mediante id/parentId). El SessionManager de pi maneja la persistencia:
const sessionManager = SessionManager.open(params.sessionFile);OpenClaw envuelve esto con guardSessionManager() para asegurar los resultados de las herramientas.
Caché de Sesiones
Sección titulada «Caché de Sesiones»session-manager-cache.ts guarda en caché las instancias de SessionManager para evitar procesar los archivos repetidamente:
await prewarmSessionFile(params.sessionFile);sessionManager = SessionManager.open(params.sessionFile);trackSessionManagerAccess(params.sessionFile);Límite de Historial
Sección titulada «Límite de Historial»limitHistoryTurns() recorta el historial de la conversación según el tipo de canal (DM o grupo).
Compactación
Sección titulada «Compactación»La compactación automática se activa cuando hay un desbordamiento de contexto. compactEmbeddedPiSessionDirect() se encarga de la compactación manual:
const compactResult = await compactEmbeddedPiSessionDirect({ sessionId, sessionFile, provider, model, ...});Autenticación y Resolución de Modelos
Sección titulada «Autenticación y Resolución de Modelos»Perfiles de Autenticación
Sección titulada «Perfiles de Autenticación»OpenClaw mantiene un almacén de perfiles de autenticación con varias API keys por cada provider:
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });Los perfiles rotan si ocurren fallos, haciendo un seguimiento del tiempo de enfriamiento (cooldown):
await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });const rotated = await advanceAuthProfile();Resolución de Modelos
Sección titulada «Resolución de Modelos»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
Sección titulada «Failover»FailoverError activa el respaldo del modelo cuando está configurado:
if (fallbackConfigured && isFailoverErrorMessage(errorText)) { throw new FailoverError(errorText, { reason: promptFailoverReason ?? "unknown", provider, model: modelId, profileId, status: resolveFailoverStatus(promptFailoverReason), });}Extensiones de Pi
Sección titulada «Extensiones de Pi»OpenClaw carga extensiones de pi personalizadas para habilitar comportamientos especializados en tus flujos de trabajo. Te recomiendo integrar estas herramientas para optimizar la lógica de tus agentes.
Salvaguarda de Compactación (Compaction Safeguard)
Sección titulada «Salvaguarda de Compactación (Compaction Safeguard)»El archivo src/agents/pi-hooks/compaction-safeguard.ts añade protecciones críticas al proceso de compactación. Esto incluye una gestión adaptativa del presupuesto de tokens, junto con resúmenes de fallos en herramientas y operaciones de archivos. Es la mejor opción para mantener la estabilidad del contexto.
if (resolveCompactionMode(params.cfg) === "safeguard") { setCompactionSafeguardRuntime(params.sessionManager, { maxHistoryShare }); paths.push(resolvePiExtensionPath("compaction-safeguard"));}Recorte de Contexto (Context Pruning)
Sección titulada «Recorte de Contexto (Context Pruning)»src/agents/pi-hooks/context-pruning.ts implementa el recorte de contexto basado en el TTL de la caché. Esta técnica permite liberar recursos de forma eficiente:
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") { setContextPruningRuntime(params.sessionManager, { settings, contextWindowTokens, isToolPrunable, lastCacheTouchAt, }); paths.push(resolvePiExtensionPath("context-pruning"));}Streaming y Respuestas por Bloques
Sección titulada «Streaming y Respuestas por Bloques»Fragmentación por Bloques (Block Chunking)
Sección titulada «Fragmentación por Bloques (Block Chunking)»EmbeddedBlockChunker se encarga de gestionar el streaming de texto para organizarlo en bloques de respuesta discretos:
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;Eliminación de Etiquetas de Pensamiento/Final
Sección titulada «Eliminación de Etiquetas de Pensamiento/Final»El sistema procesa la salida de streaming para limpiar los bloques <think>/<thinking> y extraer el contenido específico de <final>. Así garantizas que el usuario reciba solo la información relevante.
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => { // Strip <think>...</think> content // If enforceFinalTag, only return <final>...</final> content};Directivas de Respuesta
Sección titulada «Directivas de Respuesta»Las directivas de respuesta, como [[media:url]], [[voice]] o [[reply:id]], se analizan y se extraen directamente del chunk procesado:
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);Manejo de errores
Sección titulada «Manejo de errores»Clasificación de errores
Sección titulada «Clasificación de errores»pi-embedded-helpers.ts clasifica los errores para que puedas gestionarlos de forma adecuada:
isContextOverflowError(errorText) // Context too largeisCompactionFailureError(errorText) // Compaction failedisAuthAssistantError(lastAssistant) // Auth failureisRateLimitAssistantError(...) // Rate limitedisFailoverAssistantError(...) // Should failoverclassifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "timeout" | ...Fallback del nivel de pensamiento
Sección titulada «Fallback del nivel de pensamiento»Si un nivel de pensamiento (thinking level) no es compatible, el sistema realiza un fallback:
const fallbackThinking = pickFallbackThinkingLevel({ message: errorText, attempted: attemptedThinking,});if (fallbackThinking) { thinkLevel = fallbackThinking; continue;}Integración con Sandbox
Sección titulada «Integración con Sandbox»Cuando el modo sandbox está activado, las herramientas y las rutas de acceso se restringen:
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}Manejo específico por proveedor
Sección titulada «Manejo específico por proveedor»Cada proveedor tiene sus propias particularidades y OpenClaw las gestiona automáticamente para que tú no tengas que preocuparte por los detalles técnicos de bajo nivel.
Anthropic
Sección titulada «Anthropic»- Limpieza de cadenas mágicas de rechazo y validación de turnos para roles consecutivos.
- Compatibilidad de parámetros específica para Claude Code.
Google/Gemini
Sección titulada «Google/Gemini»- Correcciones en el orden de los turnos mediante
applyGoogleTurnOrderingFix. - Saneamiento del esquema de herramientas (
sanitizeToolsForGoogle) y del historial de sesión (sanitizeSessionHistory).
- Herramienta
apply_patchpara modelos Codex. - Gestión de la degradación del nivel de razonamiento (thinking level).
Integración con TUI
Sección titulada «Integración con TUI»OpenClaw también cuenta con un modo TUI local que utiliza componentes de pi-tui directamente:
import { ... } from "@mariozechner/pi-tui";Esto te ofrece una experiencia de terminal interactiva parecida al modo nativo de pi.
Diferencias clave con Pi CLI
Sección titulada «Diferencias clave con Pi CLI»| Aspecto | Pi CLI | OpenClaw Embedded |
|---|---|---|
| Invocación | comando pi / RPC | SDK mediante createAgentSession() |
| Herramientas | Herramientas de codificación por defecto | Suite de herramientas personalizada de OpenClaw |
| System prompt | AGENTS.md + prompts | Dinámico por canal/contexto |
| Almacenamiento de sesiones | ~/.pi/agent/sessions/ | ~/.openclaw/agents/<agentId>/sessions/ (o $OPENCLAW_STATE_DIR/agents/<agentId>/sessions/) |
| Autenticación | Credencial única | Multi-perfil con rotación |
| Extensiones | Cargadas desde el disco | Programático + rutas de disco |
| Manejo de eventos | Renderizado de TUI | Basado en callbacks (onBlockReply, etc.) |
Consideraciones futuras
Sección titulada «Consideraciones futuras»Áreas para un posible rediseño:
- Alineación de firmas de herramientas: Actualmente estamos adaptando entre las firmas de pi-agent-core y pi-coding-agent.
- Envoltorio del gestor de sesiones:
guardSessionManagerañade seguridad, aunque aumenta la complejidad. - Carga de extensiones: Podrías usar el
ResourceLoaderde pi de forma más directa. - Complejidad del manejador de streaming:
subscribeEmbeddedPiSessionha crecido demasiado. - Particularidades de proveedores: Existen muchos flujos de código específicos de proveedores que pi podría gestionar potencialmente.
La cobertura de integración de Pi abarca estas 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 vivo/opcionales:
src/agents/pi-embedded-runner-extraparams.live.test.ts(actívalo conOPENCLAW_LIVE_TEST=1)
Para conocer los comandos de ejecución actuales, consulta Pi Development Workflow.
OpenClaw Expert
Sigues atascado?
Si esta pagina no resolvio tu caso, pregunta a OpenClaw Expert para pasos concretos.