Arquitectura y conceptos fundamentales

Por: Artiko
papercliparquitecturamodelo de datosconceptos

La base de todo: el modelo de datos

Antes de crear tu primera empresa o configurar tu primer agente, necesitas entender cómo Paperclip organiza la información. El modelo de datos no es un detalle técnico secundario: define la gramática del sistema. Una vez que entiendas estas cinco entidades y cómo se relacionan, todo lo demás tiene sentido natural.

Las cinco entidades fundamentales de Paperclip son:

  1. Company — La entidad contenedora principal
  2. Agent — Cualquier bot que hace trabajo
  3. Task — Una unidad de trabajo trazada
  4. Heartbeat — El schedule de ejecución de un agente
  5. Budget — El control de costos por agente

Hay también entidades de soporte: Initiative (un goal de empresa), Skill (conocimiento inyectable en runtime), AuditLog (registro inmutable de eventos), y Adapter (la configuración técnica para invocar al agente).

Company: el contenedor de todo

Una Company en Paperclip no es solo un nombre. Es una unidad de aislamiento completa: todos los agentes, tareas, tickets, y datos de una empresa están encapsulados dentro de ella y son inaccesibles desde otras empresas en el mismo deployment.

classDiagram
    class Company {
        +string id
        +string name
        +string description
        +string industry
        +string[] goals
        +Initiative[] initiatives
        +Agent[] agents
        +Budget companyBudget
        +BoardConfig board
        +DateTime createdAt
    }
    
    class Initiative {
        +string id
        +string title
        +string description
        +string status
        +Task[] tasks
        +string ownerId
    }
    
    class BoardConfig {
        +string memberName
        +string email
        +string supervisionLevel
        +string[] approvalRequired
    }
    
    Company "1" --> "*" Initiative
    Company "1" --> "1" BoardConfig
    Company "1" --> "*" Agent

El campo goals son strings de texto libre que describen hacia dónde va la empresa. A diferencia de las Initiatives (que tienen estados, tareas, y dueños), los goals son intenciones de alto nivel. Ejemplo: "Crecer el MRR a $50k antes de Q4 2026" o "Tener cobertura de tests por encima del 80% en todos los servicios críticos".

Las Initiatives son más estructuradas: tienen un título, descripción, estado (planned, in_progress, completed), y están vinculadas a tareas concretas. Puedes pensar en los goals como la visión y en las initiatives como la estrategia.

Agent: la entidad que hace trabajo

Un Agent en Paperclip representa cualquier bot o proceso que puede recibir tareas y ejecutarlas. Lo importante es que Paperclip es agnóstico al tipo de agente: puede ser Claude Code, Codex, un proceso bash, una llamada HTTP a cualquier API externa, o cualquier combinación.

classDiagram
    class Agent {
        +string id
        +string name
        +string role
        +string description
        +string companyId
        +AdapterType adapterType
        +AdapterConfig adapterConfig
        +AgentStatus status
        +string managerId
        +string[] reportIds
        +Budget budget
        +Skill[] skills
        +Heartbeat[] heartbeats
        +DateTime createdAt
    }
    
    class AdapterConfig {
        +string type
        +string command
        +Record env
        +number timeout
        +string workDir
    }
    
    class AgentStatus {
        <<enumeration>>
        idle
        running
        paused
        budget_exhausted
        error
    }
    
    Agent "1" --> "1" AdapterConfig
    Agent "1" --> "1" AgentStatus
    Agent "1" --> "*" Heartbeat

Los campos más importantes de un agente:

Task: la unidad de trabajo trazada

Una Task (o Ticket) es el corazón operacional de Paperclip. Todo trabajo que hace un agente está representado como una task. No existe trabajo no trazado en Paperclip: si un agente ejecutó algo, hay un ticket.

classDiagram
    class Task {
        +string id
        +string title
        +string description
        +string companyId
        +TaskStatus status
        +TaskPriority priority
        +string assigneeId
        +string creatorId
        +string parentId
        +Task[] subtasks
        +AuditLog[] auditLog
        +ToolCall[] toolCalls
        +string[] mentions
        +DateTime checkedOutAt
        +string checkedOutBy
        +DateTime completedAt
    }
    
    class TaskStatus {
        <<enumeration>>
        backlog
        todo
        in_progress
        review
        done
        cancelled
    }
    
    class AuditLog {
        +string id
        +string taskId
        +string agentId
        +string action
        +Record payload
        +DateTime timestamp
    }
    
    class ToolCall {
        +string id
        +string taskId
        +string toolName
        +Record input
        +Record output
        +number durationMs
        +number costUsd
        +DateTime timestamp
    }
    
    Task "1" --> "*" Task : subtasks
    Task "1" --> "*" AuditLog
    Task "1" --> "*" ToolCall

El campo checkedOutAt y checkedOutBy implementan el mecanismo de checkout atómico. Cuando un agente va a trabajar en una tarea, ejecuta un checkout optimista. Si el checkout tiene éxito (nadie más la tenía tomada), el agente procede. Si falla, la tarea ya está siendo procesada por otro agente.

El auditLog es inmutable: solo se pueden añadir entradas, nunca modificar o eliminar las existentes. Esto garantiza la integridad del historial.

Los toolCalls registran cada llamada a herramientas que el agente hizo durante la ejecución: qué herramienta usó, con qué parámetros, qué obtuvo de respuesta, cuánto tardó, y cuánto costó en dólares.

Heartbeat: el reloj del agente

Un Heartbeat define cuándo se despierta un agente. Es la pieza que transforma un agente pasivo (que solo responde cuando se le invoca) en un agente activo (que trabaja según un schedule propio).

classDiagram
    class Heartbeat {
        +string id
        +string agentId
        +string schedule
        +HeartbeatType type
        +string payload
        +HeartbeatStatus lastStatus
        +DateTime lastRun
        +DateTime nextRun
        +number runCount
        +number failureCount
        +boolean enabled
    }
    
    class HeartbeatType {
        <<enumeration>>
        cron
        event
        manual
    }
    
    class HeartbeatStatus {
        <<enumeration>>
        success
        failure
        running
        skipped
    }
    
    Heartbeat "1" --> "1" HeartbeatType
    Heartbeat "1" --> "1" HeartbeatStatus

El campo schedule usa sintaxis cron estándar para los heartbeats de tipo cron:

# Cada 5 minutos
*/5 * * * *

# Cada lunes a las 9am
0 9 * * 1

# El primer día de cada mes a medianoche
0 0 1 * *

# Cada hora, de lunes a viernes
0 * * * 1-5

Los heartbeats de tipo event se disparan cuando ocurre algo en el sistema: una nueva tarea asignada al agente, una mención (@mention), una tarea de alta prioridad entrando en el backlog, etc.

El campo payload define qué contexto inicial se le entrega al agente cuando se despierta. Puede ser un texto libre o un JSON con instrucciones específicas para esa ejecución.

Budget: el control de costos

El Budget es la entidad que hace posible la gobernanza financiera. Cada agente tiene su propio presupuesto mensual, y el sistema rastrea cada gasto en tiempo real.

classDiagram
    class Budget {
        +string id
        +string agentId
        +number monthlyLimitUsd
        +number spentThisMonthUsd
        +number remainingUsd
        +BudgetPeriod period
        +BudgetStatus status
        +DateTime periodStartDate
        +DateTime periodEndDate
        +BudgetTransaction[] transactions
    }
    
    class BudgetTransaction {
        +string id
        +string budgetId
        +string taskId
        +string toolCallId
        +number amountUsd
        +string description
        +DateTime timestamp
    }
    
    class BudgetStatus {
        <<enumeration>>
        active
        warning
        exhausted
        suspended
    }
    
    Budget "1" --> "*" BudgetTransaction

Cuando un agente realiza una llamada a un LLM (a través de su adapter), el costo se calcula automáticamente y se registra como una transacción del budget. Paperclip usa los precios oficiales de cada proveedor para el cálculo.

Cuando el spentThisMonthUsd supera el 80% del monthlyLimitUsd, el status pasa a warning y el Board recibe una notificación. Cuando supera el 100%, el status pasa a exhausted y el agente deja de aceptar nuevas tareas.

Al inicio de cada mes calendario, los contadores se reinician automáticamente.

El flujo completo: de goals a ejecución

Entender cada entidad por separado es necesario pero no suficiente. Lo que hace poderoso a Paperclip es cómo estas entidades trabajan juntas para llevar el contexto desde los goals de la empresa hasta la ejecución concreta del agente.

sequenceDiagram
    participant Board as Board (Tú)
    participant Sys as Sistema Paperclip
    participant CEO as Agente CEO
    participant CTO as Agente CTO
    participant Eng as Agente Ingeniero
    
    Board->>Sys: Define goals de la Company
    Sys->>CEO: Heartbeat (daily, 9am)
    CEO->>Sys: Revisar goals y tareas pendientes
    CEO->>Sys: Crear Initiative "Migración a TypeScript"
    CEO->>Sys: Crear Task "Planificar migración" → asignar a CTO
    Sys->>CTO: Heartbeat (on: task_assigned)
    CTO->>Sys: Checkout Task "Planificar migración"
    CTO->>Sys: Crear subtareas para el equipo
    CTO->>Eng: Asignar Task "Migrar módulo auth"
    Sys->>Eng: Heartbeat (on: task_assigned)
    Eng->>Sys: Checkout Task "Migrar módulo auth"
    Eng->>Sys: Ejecutar tool calls (leer código, escribir código, tests)
    Eng->>Sys: Completar task + report
    Sys->>CTO: Notificar task completada
    CTO->>Sys: Review y aprobar
    Sys->>Board: Resumen diario de progreso

Este flujo tiene varias propiedades importantes:

El contexto fluye hacia abajo. Los goals se convierten en initiatives, las initiatives en tareas, las tareas en subtareas. Cada nivel hereda el contexto del nivel superior. Cuando el agente Ingeniero trabaja en “Migrar módulo auth”, sabe que es parte de la Initiative “Migración a TypeScript” y entiende el objetivo final.

El reporte fluye hacia arriba. Los completions, los reportes, los bloqueos: todo sube por la jerarquía. El CTO sabe cuándo el ingeniero termina su tarea. El CEO sabe cuándo el CTO cierra una initiative. El Board recibe resúmenes de alto nivel.

Los checkouts garantizan atomicidad. En el diagrama, el paso “Checkout Task” es crítico. Antes de que el CTO y el Ingeniero empiecen a trabajar, checkean la tarea. Si hubiera otro agente intentando tomar la misma tarea simultáneamente, el checkout garantiza que solo uno lo logra.

El ciclo de vida completo de una tarea

Una tarea en Paperclip pasa por estados bien definidos:

stateDiagram-v2
    [*] --> backlog : Creada
    backlog --> todo : Priorizada
    todo --> in_progress : Checkout por agente
    in_progress --> review : Agente completa trabajo
    review --> done : Aprobado
    review --> in_progress : Revisión requiere cambios
    done --> [*]
    
    backlog --> cancelled : Board cancela
    todo --> cancelled : Board cancela
    in_progress --> cancelled : Board cancela / agente paused
    in_progress --> backlog : Checkout liberado (timeout)

backlog: La tarea existe pero no tiene prioridad ni asignación activa. El CEO o manager la puede priorizar cuando lo decida.

todo: La tarea está priorizada y lista para ser tomada por el agente asignado. El próximo heartbeat del agente la recogerá.

in_progress: Un agente tiene el checkout activo. Está trabajando en ella. Si el agente falla, el checkout expira después del timeout configurado (por defecto 1 hora) y la tarea vuelve a todo.

review: El agente terminó el trabajo y lo marcó como listo para revisión. Dependiendo de la configuración, la revisión la puede hacer el manager-agente o el Board directamente.

done: La tarea está completamente cerrada. El audit log y los tool calls están inmutables desde este punto.

cancelled: El Board o un manager canceló la tarea. El historial de lo que se hizo hasta ese punto se preserva.

Fat payload vs thin ping: dos estrategias de heartbeat

Cuando un heartbeat se dispara, Paperclip invoca al agente con cierto contexto inicial. Hay dos filosofías para diseñar ese contexto:

Fat payload (payload rico): El heartbeat incluye toda la información relevante que el agente necesita para empezar a trabajar inmediatamente. El agente no necesita hacer queries adicionales al sistema para entender qué tiene que hacer.

{
  "type": "daily_review",
  "company_goals": ["Crecer MRR a $50k", "Reducir churn"],
  "pending_tasks": [
    {"id": "t_123", "title": "Migrar módulo auth", "priority": "high"},
    {"id": "t_124", "title": "Añadir tests de integración", "priority": "medium"}
  ],
  "budget_remaining": 45.30,
  "open_blockers": []
}

Ventajas: Menos llamadas a la API → menor latencia → menor costo. El agente tiene todo lo que necesita en un solo payload.

Desventajas: El payload puede quedar desactualizado si el heartbeat se genera con antelación. Si entre la generación del payload y la ejecución del agente alguien crea 5 nuevas tareas, el agente no las ve.

Thin ping (ping mínimo): El heartbeat simplemente despierta al agente con un mensaje mínimo. El agente es responsable de consultar el sistema para obtener el estado actual.

{
  "type": "wake",
  "reason": "scheduled_daily_review",
  "timestamp": "2026-04-05T09:00:00Z"
}

Ventajas: El agente siempre trabaja con información actualizada. Más robusto ante cambios de estado.

Desventajas: Requiere que el agente haga más llamadas al sistema, lo que aumenta el costo de tokens y la latencia.

La recomendación general: usar fat payload para agentes de análisis y reporte (donde la consistencia del snapshot importa) y thin ping para agentes de ejecución (donde la actualidad de la información es crítica).

Cómo fluye el contexto hacia el agente

Cuando Paperclip invoca al agente (a través del adapter correspondiente), construye un contexto de sistema que combina:

graph TD
    subgraph "Contexto del Sistema"
    A[Company description + goals]
    B[Agent role + description]
    C[Skills inyectadas en runtime]
    D[Historial reciente de tareas]
    E[Org chart del agente]
    end
    
    subgraph "Contexto de la Invocación"
    F[Heartbeat payload]
    G[Tarea asignada si aplica]
    H[@mentions recibidas]
    end
    
    A --> Z[Prompt del agente]
    B --> Z
    C --> Z
    D --> Z
    E --> Z
    F --> Z
    G --> Z
    H --> Z

Este contexto se construye de forma determinista y cacheada donde es posible, para minimizar los tokens de contexto. La Company description y los goals raramente cambian, por lo que se cachean. Las tareas asignadas y los mentions se agregan frescos en cada invocación.

Las skills: conocimiento inyectable en runtime

Las Skills son archivos Markdown que se incluyen en el contexto del agente cuando se le invoca. Son el mecanismo principal para darle a un agente conocimiento específico sin hardcodearlo en su configuración.

graph LR
    SM[Skills Manager] --> |selecciona skills relevantes| Builder
    Agent[Configuración del Agente] --> |skills asignadas| Builder
    Builder[Context Builder] --> |construye| Ctx[Contexto Final]
    Ctx --> |invoca| Adapter
    Adapter --> |ejecuta| ExtAgent[Agente Externo]

Una skill puede ser:

Las skills se pueden asignar a un agente específico, a todos los agentes de un rol, o a todos los agentes de la empresa. También se pueden activar condicionalmente según el tipo de tarea.

Adapters: el puente entre Paperclip y los agentes externos

El Adapter es la pieza técnica que sabe cómo invocar a un agente externo específico. Paperclip incluye adapters para los casos más comunes, y el sistema es extensible para adapters personalizados.

graph TD
    PC[Paperclip Core] -->|invoca| Dispatch[Adapter Dispatcher]
    Dispatch -->|tipo: openclaw| OC[OpenClaw Adapter]
    Dispatch -->|tipo: claude_code| CC[Claude Code Adapter]
    Dispatch -->|tipo: codex_local| CDX[Codex Local Adapter]
    Dispatch -->|tipo: http| HTTP[HTTP Adapter]
    Dispatch -->|tipo: process| PROC[Process/Bash Adapter]
    
    OC --> ExtOC[OpenClaw CLI]
    CC --> ExtCC[Claude Code CLI]
    CDX --> ExtCDX[Codex Local]
    HTTP --> ExtHTTP[API Endpoint]
    PROC --> ExtPROC[Shell Command]

Cada adapter implementa la misma interfaz:

interface Adapter {
  invoke(context: AgentContext): Promise<AgentResult>
  estimateCost(context: AgentContext): Promise<number>
  isAvailable(): Promise<boolean>
}

El adapter HTTP es especialmente poderoso porque permite integrar cualquier servicio externo que tenga una API REST. Si tienes un sistema propietario de agentes, solo necesitas exponer un endpoint que acepte el contexto de Paperclip y devuelva el resultado.

El audit log: la memoria institucional inmutable

Toda acción en Paperclip queda registrada en el audit log. No solo las acciones de agentes: también las acciones del Board (pausas, overrides, aprobaciones), las transiciones de estado de las tareas, y los cambios de configuración.

El audit log tiene tres propiedades garantizadas por el sistema:

  1. Inmutabilidad: Las entradas del audit log solo se pueden añadir, nunca modificar o eliminar. Esto está enforced a nivel de base de datos.
  2. Completitud: Toda acción visible en la UI tiene una entrada correspondiente en el audit log. No hay “modo silencioso” donde algo ocurra sin registrarse.
  3. Estructura consistente: Cada entrada tiene: timestamp, actorId (quién lo hizo), action (qué se hizo), targetId (sobre qué entidad), payload (los detalles específicos).

Esto convierte al audit log en la fuente de verdad del sistema. Si hay un desacuerdo sobre qué pasó, el audit log resuelve la disputa.

Resumen del modelo de datos

erDiagram
    Company ||--o{ Agent : "tiene"
    Company ||--o{ Initiative : "tiene"
    Company ||--|| BoardConfig : "tiene"
    Agent ||--o{ Heartbeat : "tiene"
    Agent ||--|| Budget : "tiene"
    Agent ||--o{ Skill : "usa"
    Agent ||--|| AdapterConfig : "tiene"
    Agent ||--o{ Agent : "manager/reports"
    Initiative ||--o{ Task : "contiene"
    Task ||--o{ Task : "subtareas"
    Task ||--o{ AuditLog : "registra"
    Task ||--o{ ToolCall : "registra"
    Budget ||--o{ BudgetTransaction : "registra"
    ToolCall ||--|| BudgetTransaction : "genera"

Con este modelo de datos en mente, el próximo paso es crear tu primera empresa y ver cómo todos estos conceptos cobran vida en la UI de Paperclip.