Modern Agent Engineering

Agentes persistentes: Dispatch, Cowork, computer use y workflows siempre activos

Los agentes persistentes se están convirtiendo en funcionalidades de producto, no solo en arquitectura backend. Descubre cómo se relacionan Dispatch, Cowork, computer use y el modo background de OpenAI, con ejemplos completos en TypeScript desde cero hasta un nivel avanzado.

20 min de lectura

Antes, los agentes persistentes sonaban como un detalle de arquitectura. En March 2026, empezaron a parecer una categoría de producto.

El March 17, 2026, Anthropic lanzó Dispatch. El March 23, 2026, Anthropic conectó Dispatch con computer use para que Claude pudiera seguir trabajando en tu computadora después de que le asignaras una tarea desde tu teléfono. El March 27, 2026, OpenAI agregó el modo background a la Responses API y lo presentó explícitamente en el contexto de productos como Codex, Deep Research y Operator.

Ese es el cambio del que trata este artículo.

La pregunta ya no es «¿puede un agente recordar el contexto?».

En cambio:

  • ¿puede seguir trabajando después del prompt inicial?
  • ¿puede ejecutarse según un horario?
  • ¿puede transferir trabajo entre dispositivos?
  • ¿puede usar primero herramientas directas y recurrir al control del navegador o del escritorio solo cuando sea necesario?
  • ¿puede hacer una pausa para pedir aprobación antes de hacer algo riesgoso?

Así es como se ve en la práctica la experiencia de usuario de un agente persistente.

TL;DR

  • Los agentes persistentes se están convirtiendo en funcionalidades de producto, no solo en patrones de backend. Dispatch, Cowork y el modo background de OpenAI apuntan en esa dirección.
  • La primitiva central es una tarea durable, no una solicitud HTTP de larga duración ni una transcripción de chat enorme.
  • La estrategia más segura suele ser: primero APIs y conectores, después automatización determinista y, como último recurso, computer use.
  • Computer use es una capacidad de respaldo, no una excusa para omitir el diseño del sistema.
  • Si quieres un «compañero de trabajo siempre disponible», necesitas cinco cosas: estado de las tareas, programaciones, aprobaciones, artefactos y notificaciones.
  • Una primera implementación útil es sorprendentemente pequeña: una tabla de tareas, un bucle de worker, un endpoint de polling y un lugar para almacenar los resultados.
  • La versión avanzada agrega ejecución en background, trabajos recurrentes y acciones en el escritorio o el navegador que requieren aprobación.

Qué cambió en March 2026

Los materiales públicos de producto de Anthropic describen ahora un workflow que se parece mucho más al de un compañero de trabajo que al de un chatbot.

La introducción a Cowork oficial dice que Cowork usa la misma arquitectura de agentes que Claude Code dentro de Claude Desktop, trabaja directamente en tu computadora y te permite describir el resultado que buscas para volver más tarde y encontrar el trabajo terminado. El anuncio oficial de Dispatch + computer use va más allá: puedes asignarle una tarea a Claude desde tu teléfono, dejar que siga trabajando en tu computadora y revisar el resultado final más tarde.

La actualización de OpenAI llega desde la plataforma, no desde un producto para consumidores. El anuncio de la Responses API dice que el modo background existe porque los productos basados en agentes como Codex, Deep Research y Operator pueden tardar minutos, y los desarrolladores necesitan un modelo de ejecución asíncrona que sobreviva a timeouts y reconexiones.

Son superficies distintas, pero el patrón de producto es el mismo:

  • un usuario expresa el resultado que busca
  • el sistema lo convierte en una tarea durable
  • la tarea puede continuar después de que el usuario se va
  • el resultado llega como un artefacto, no solo como una respuesta en el chat

Qué es realmente un agente persistente

Un agente persistente no es solo un LLM con memoria.

Es un sistema con:

  • estado durable de las tareas, como queued, running, waiting_approval, completed y failed
  • ejecución reanudable, para que el trabajo sobreviva al cierre de la aplicación, las reconexiones o la latencia del modelo
  • artefactos como resultado, como un documento, una hoja de cálculo, un pull request o un informe
  • programaciones opcionales para trabajo diario o semanal
  • transferencia opcional entre dispositivos, para que la solicitud pueda empezar en un lugar y terminar en otro
  • puntos de aprobación opcionales antes de acciones de alto impacto

Eso es distinto de la pregunta que abordé en el artículo sobre stateful vs. stateless. Ese artículo trata sobre dónde reside el estado del agente desde el punto de vista de la arquitectura. Este trata sobre cómo se manifiesta la persistencia como comportamiento del producto de cara al usuario.

Ejemplos de productos reales

Lo bueno de esta tendencia es que ya no tenemos que imaginarla desde cero. El material público de los productos ya muestra cómo toma forma.

1. Dispatch como medio para transferir trabajo entre dispositivos

La publicación de Anthropic del March 23, 2026 dice que Dispatch te permite mantener una conversación continua con Claude entre el teléfono y la computadora. Describe explícitamente cómo empezar una tarea en tu teléfono, hacer otra cosa y abrir después el trabajo terminado en tu computadora.

Es un cambio real en el producto porque la «memoria del agente» ya no es solo estado oculto en el backend. Se convierte en una interfaz visible para gestionar tareas:

  • una tarea existe incluso cuando la aplicación está cerrada
  • la tarea tiene un ciclo de vida
  • la tarea puede crear artefactos mientras estás ausente

2. Cowork como espacio de trabajo siempre disponible

La documentación de Cowork de Anthropic dice que Cowork puede trabajar directamente con archivos locales, dividir tareas en flujos de trabajo paralelos y crear resultados elaborados como hojas de cálculo, presentaciones y documentos con formato.

Eso significa que el producto no solo sirve para «responder mi pregunta». También sirve para:

  • revisar mis archivos
  • realizar una tarea de varios pasos
  • volver más tarde y encontrar el trabajo terminado

Eso se acerca mucho más al modelo de un compañero de equipo que trabaja de forma asíncrona.

3. Resúmenes programados y métricas recurrentes

El anuncio de Dispatch de Anthropic da ejemplos concretos: pedirle a Claude que revise tus correos todas las mañanas, obtenga algunas métricas cada semana o inicie una sesión de Cowork o Claude Code para preparar un informe o un pull request.

Estos ejemplos importan porque convierten la persistencia de una capacidad pasiva en una forma de ejecutar workflows recurrentes.

4. Computer use cuando las integraciones no son suficientes

El mismo anuncio del March 23 dice que Claude preferirá primero herramientas precisas, como los conectores, y usará el control de la computadora cuando falte la herramienta necesaria. Anthropic presenta computer use como una forma de abrir archivos, usar el navegador y ejecutar herramientas de desarrollo automáticamente, con permisos y medidas de protección.

Ese es el modelo mental correcto. Computer use es poderoso, pero la idea no es que «el agente puede hacer clic en cualquier cosa». La idea es:

  • usar sistemas directos cuando estén disponibles
  • usar el control del navegador o del escritorio solo cuando sea necesario
  • mantener a una persona en el circuito para los pasos riesgosos

5. El modo background de OpenAI como primitiva de la API

La documentación pública de OpenAI deja clara la implicación para el producto. El modo background existe para que puedas crear experiencias como Codex, Deep Research y Operator sin conexiones de frontend de larga duración.

Así que, si Anthropic muestra la interfaz del producto y OpenAI muestra la primitiva de plataforma, la lección compartida es clara:

los agentes persistentes necesitan objetos de trabajo durables

no solo prompts largos

ni historiales de chat más grandes.

Un mejor modelo mental

Esta es la estructura más clara que conozco para los agentes persistentes:

user request
  -> task record
  -> planner / agent worker
  -> tools, APIs, files, or computer use
  -> artifact output
  -> notification or pickup surface

Si agregas programaciones y aprobaciones, queda así:

user or scheduler
  -> durable task
  -> queued
  -> running
  -> waiting_approval (optional)
  -> resumed
  -> completed artifact
  -> user notified on phone, desktop, or email

Ese es el patrón que vamos a construir.

Paso 1: Construye el agente persistente útil más pequeño

Empieza con algo pequeño:

  • una API para crear tareas
  • una API para consultar el estado de las tareas
  • una tabla SQLite
  • un bucle de worker
  • todavía sin llamadas al modelo

Suena casi demasiado simple, pero enseña la estructura correcta.

Instalación

mkdir persistent-agent-demo
cd persistent-agent-demo
npm init -y
npm install express better-sqlite3 nanoid zod
npm install -D typescript tsx @types/express @types/node

server.ts

import Database from "better-sqlite3";
import express from "express";
import { nanoid } from "nanoid";
import { z } from "zod";

const app = express();
app.use(express.json());

const db = new Database("tasks.db");

db.exec(`
  CREATE TABLE IF NOT EXISTS tasks (
    id TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    prompt TEXT NOT NULL,
    status TEXT NOT NULL,
    result TEXT,
    run_at TEXT,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
  );
`);

type TaskStatus =
  | "queued"
  | "running"
  | "completed"
  | "failed";

type TaskRow = {
  id: string;
  title: string;
  prompt: string;
  status: TaskStatus;
  result: string | null;
  run_at: string | null;
  created_at: string;
  updated_at: string;
};

const createTaskSchema = z.object({
  title: z.string().min(1),
  prompt: z.string().min(1),
  runAt: z.string().datetime().optional(),
});

function now() {
  return new Date().toISOString();
}

function createTask(input: z.infer<typeof createTaskSchema>) {
  const task: TaskRow = {
    id: nanoid(),
    title: input.title,
    prompt: input.prompt,
    status: "queued",
    result: null,
    run_at: input.runAt ?? null,
    created_at: now(),
    updated_at: now(),
  };

  db.prepare(`
    INSERT INTO tasks (id, title, prompt, status, result, run_at, created_at, updated_at)
    VALUES (@id, @title, @prompt, @status, @result, @run_at, @created_at, @updated_at)
  `).run(task);

  return task;
}

function getTask(id: string) {
  return db.prepare("SELECT * FROM tasks WHERE id = ?").get(id) as TaskRow | undefined;
}

function findNextRunnableTask() {
  return db.prepare(`
    SELECT *
    FROM tasks
    WHERE status = 'queued'
      AND (run_at IS NULL OR run_at <= ?)
    ORDER BY created_at ASC
    LIMIT 1
  `).get(now()) as TaskRow | undefined;
}

async function executeTask(task: TaskRow) {
  db.prepare(`
    UPDATE tasks
    SET status = 'running', updated_at = ?
    WHERE id = ?
  `).run(now(), task.id);

  try {
    await new Promise((resolve) => setTimeout(resolve, 1500));

    const result = [
      `Task: ${task.title}`,
      "",
      "Persistent-agent demo result:",
      `- Original prompt: ${task.prompt}`,
      "- Status moved from queued -> running -> completed",
      "- This is where a real agent worker would write the artifact",
    ].join("\n");

    db.prepare(`
      UPDATE tasks
      SET status = 'completed', result = ?, updated_at = ?
      WHERE id = ?
    `).run(result, now(), task.id);
  } catch (error) {
    db.prepare(`
      UPDATE tasks
      SET status = 'failed', result = ?, updated_at = ?
      WHERE id = ?
    `).run(String(error), now(), task.id);
  }
}

let workerBusy = false;

setInterval(async () => {
  if (workerBusy) return;

  const task = findNextRunnableTask();
  if (!task) return;

  workerBusy = true;
  try {
    await executeTask(task);
  } finally {
    workerBusy = false;
  }
}, 1000);

app.post("/tasks", (req, res) => {
  const parsed = createTaskSchema.safeParse(req.body);
  if (!parsed.success) {
    return res.status(400).json(parsed.error.flatten());
  }

  const task = createTask(parsed.data);
  res.status(201).json(task);
});

app.get("/tasks/:id", (req, res) => {
  const task = getTask(req.params.id);
  if (!task) {
    return res.status(404).json({ error: "Task not found" });
  }

  res.json(task);
});

app.listen(3000, () => {
  console.log("Persistent agent demo listening on http://localhost:3000");
});

Ejecuta el servidor:

npx tsx server.ts

Crea una tarea:

curl -X POST http://localhost:3000/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Morning briefing",
    "prompt": "Summarize sales, support issues, and today'\''s calendar."
  }'

Consúltala un segundo después:

curl http://localhost:3000/tasks/<task-id>

Esto todavía no es inteligente, pero tiene la estructura correcta. En esta etapa, eso importa más que la inteligencia.

Paso 2: Agrega un frontend sencillo

Una vez que funciona el backend, la interfaz útil más simple es un tablero de tareas.

TaskBoard.tsx

import { useEffect, useState } from "react";

type Task = {
  id: string;
  title: string;
  status: "queued" | "running" | "completed" | "failed";
  result: string | null;
};

export function TaskBoard() {
  const [taskId, setTaskId] = useState("");
  const [task, setTask] = useState<Task | null>(null);
  const [title, setTitle] = useState("Weekly metrics");
  const [prompt, setPrompt] = useState(
    "Pull the latest revenue, signups, and churn numbers."
  );

  async function submit() {
    const response = await fetch("http://localhost:3000/tasks", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ title, prompt }),
    });

    const created = (await response.json()) as Task;
    setTaskId(created.id);
    setTask(created);
  }

  useEffect(() => {
    if (!taskId) return;

    const timer = setInterval(async () => {
      const response = await fetch(`http://localhost:3000/tasks/${taskId}`);
      const next = (await response.json()) as Task;
      setTask(next);

      if (next.status === "completed" || next.status === "failed") {
        clearInterval(timer);
      }
    }, 1000);

    return () => clearInterval(timer);
  }, [taskId]);

  return (
    <main style={{ maxWidth: 720, margin: "40px auto", fontFamily: "sans-serif" }}>
      <h1>Persistent Agent Demo</h1>
      <input value={title} onChange={(e) => setTitle(e.target.value)} />
      <textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} />
      <button onClick={submit}>Create task</button>

      {task && (
        <section>
          <h2>{task.title}</h2>
          <p>Status: {task.status}</p>
          {task.result && <pre>{task.result}</pre>}
        </section>
      )}
    </main>
  );
}

Esta interfaz es sencilla, pero ya captura el comportamiento del producto:

  • enviar trabajo
  • irte
  • volver
  • leer el artefacto

Ese es el comienzo de la experiencia de usuario de un agente persistente.

Paso 3: Reemplaza el worker simulado con el modo background de OpenAI

Ahora conservamos el sistema de tareas, pero reemplazamos la ejecución simulada por trabajo asíncrono real con un modelo.

Instala el SDK:

npm install openai

Luego amplía un poco el esquema:

ALTER TABLE tasks ADD COLUMN provider TEXT;
ALTER TABLE tasks ADD COLUMN external_id TEXT;

openai-worker.ts

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

type TaskRow = {
  id: string;
  title: string;
  prompt: string;
  status: string;
  result: string | null;
  provider?: string | null;
  external_id?: string | null;
};

function extractOutputText(response: any) {
  if (typeof response.output_text === "string" && response.output_text.length > 0) {
    return response.output_text;
  }

  return JSON.stringify(response.output ?? [], null, 2);
}

export async function startOpenAITask(task: TaskRow) {
  const response = await client.responses.create({
    model: "gpt-5.4",
    input: task.prompt,
    background: true,
  });

  db.prepare(`
    UPDATE tasks
    SET status = 'running',
        provider = 'openai',
        external_id = ?,
        updated_at = ?
    WHERE id = ?
  `).run(response.id, now(), task.id);
}

export async function pollOpenAITasks() {
  const runningTasks = db.prepare(`
    SELECT *
    FROM tasks
    WHERE provider = 'openai'
      AND external_id IS NOT NULL
      AND status = 'running'
  `).all() as TaskRow[];

  for (const task of runningTasks) {
    const response = await client.responses.retrieve(task.external_id!);

    if (response.status === "completed") {
      db.prepare(`
        UPDATE tasks
        SET status = 'completed',
            result = ?,
            updated_at = ?
        WHERE id = ?
      `).run(extractOutputText(response), now(), task.id);
      continue;
    }

    if (response.status === "failed" || response.status === "cancelled") {
      db.prepare(`
        UPDATE tasks
        SET status = 'failed',
            result = ?,
            updated_at = ?
        WHERE id = ?
      `).run(`OpenAI response ended with status: ${response.status}`, now(), task.id);
    }
  }
}

Ahora tu solicitud HTTP sigue siendo breve, pero el trabajo puede continuar.

Esa es la misma primitiva de producto que está detrás de «vuelve más tarde para encontrar el trabajo terminado».

Paso 4: Agrega tareas recurrentes

Aquí es donde la sensación de tener un «compañero de trabajo siempre disponible» empieza a hacerse realidad.

Instala un programador de tareas:

npm install node-cron

Crea una tabla de programaciones:

CREATE TABLE IF NOT EXISTS schedules (
  id TEXT PRIMARY KEY,
  title TEXT NOT NULL,
  prompt TEXT NOT NULL,
  cron TEXT NOT NULL,
  timezone TEXT NOT NULL,
  created_at TEXT NOT NULL
);

scheduler.ts

import cron from "node-cron";
import { nanoid } from "nanoid";

type ScheduleRow = {
  id: string;
  title: string;
  prompt: string;
  cron: string;
  timezone: string;
};

export function loadSchedules() {
  return db.prepare("SELECT * FROM schedules").all() as ScheduleRow[];
}

export function registerSchedules() {
  for (const schedule of loadSchedules()) {
    cron.schedule(
      schedule.cron,
      () => {
        createTask({
          title: schedule.title,
          prompt: schedule.prompt,
        });
      },
      { timezone: schedule.timezone }
    );
  }
}

db.prepare(`
  INSERT OR IGNORE INTO schedules (id, title, prompt, cron, timezone, created_at)
  VALUES (?, ?, ?, ?, ?, ?)
`).run(
  "weekday-briefing",
  "Morning executive briefing",
  "Summarize overnight support issues, revenue anomalies, and today's meetings.",
  "0 7 * * 1-5",
  "America/New_York",
  now()
);

Esto te da un workflow recurrente real:

  • cada día hábil a las 7:00 AM
  • se crea una tarea durable
  • el worker la procesa
  • el usuario consulta el resultado más tarde

Esto ya se acerca a los ejemplos que Anthropic usa públicamente para Dispatch.

Paso 5: Agrega puntos de aprobación antes de usar computer use

Este es el punto donde muchos equipos adquieren un exceso de confianza.

Si el agente va a:

  • interactuar con un panel de administración legacy
  • navegar por un dashboard financiero
  • editar una configuración de producción
  • enviar un formulario en un navegador

deberías agregar una pausa explícita.

Amplía la tabla de tareas:

ALTER TABLE tasks ADD COLUMN approval_reason TEXT;
ALTER TABLE tasks ADD COLUMN approved_at TEXT;

Ahora agrega un estado nuevo:

type TaskStatus =
  | "queued"
  | "running"
  | "waiting_approval"
  | "completed"
  | "failed";

Planificador de aprobaciones

function requiresApproval(task: TaskRow) {
  const riskyKeywords = [
    "submit",
    "delete",
    "production",
    "billing",
    "bank",
    "finance",
  ];

  return riskyKeywords.some((keyword) =>
    task.prompt.toLowerCase().includes(keyword)
  );
}

function queueForApproval(task: TaskRow, reason: string) {
  db.prepare(`
    UPDATE tasks
    SET status = 'waiting_approval',
        approval_reason = ?,
        updated_at = ?
    WHERE id = ?
  `).run(reason, now(), task.id);
}

app.post("/tasks/:id/approve", (req, res) => {
  db.prepare(`
    UPDATE tasks
    SET status = 'queued',
        approved_at = ?,
        updated_at = ?
    WHERE id = ?
      AND status = 'waiting_approval'
  `).run(now(), now(), req.params.id);

  res.json({ ok: true });
});

Luego cambia la lógica del worker:

async function executeTask(task: TaskRow) {
  if (requiresApproval(task) && !task.approved_at) {
    queueForApproval(
      task,
      "This task may open a browser or submit data to a high-impact system."
    );
    return;
  }

  // Continue with your model or automation worker here.
}

Parece un cambio pequeño, pero establece un límite importante en el producto.

El usuario deja de sentir que el agente está trabajando a escondidas en background. Siente que el sistema está colaborando con él.

Paso 6: Agrega una alternativa determinista en el navegador

Este es el puente seguro más simple hacia el uso de computer use.

En lugar de empezar con un bucle de escritorio controlado por completo por un modelo, empieza con un flujo de navegador protegido para un sistema conocido.

Instala Playwright:

npm install playwright
npx playwright install

legacy-portal.ts

import { chromium } from "playwright";

export async function runLegacyPortalFlow(taskId: string) {
  const browser = await chromium.launch({
    headless: false,
    chromiumSandbox: true,
    env: {},
    args: ["--disable-extensions", "--disable-file-system"],
  });

  const page = await browser.newPage({
    viewport: { width: 1280, height: 720 },
  });

  try {
    await page.goto(process.env.LEGACY_PORTAL_URL!);
    await page.getByLabel("Username").fill(process.env.LEGACY_PORTAL_USER!);
    await page.getByLabel("Password").fill(process.env.LEGACY_PORTAL_PASSWORD!);
    await page.getByRole("button", { name: "Sign in" }).click();

    await page.getByRole("link", { name: "Invoices" }).click();
    await page.getByRole("button", { name: "Export CSV" }).click();

    db.prepare(`
      UPDATE tasks
      SET status = 'completed',
          result = ?,
          updated_at = ?
      WHERE id = ?
    `).run("Legacy portal export completed successfully.", now(), taskId);
  } catch (error) {
    db.prepare(`
      UPDATE tasks
      SET status = 'failed',
          result = ?,
          updated_at = ?
      WHERE id = ?
    `).run(String(error), now(), taskId);
  } finally {
    await browser.close();
  }
}

¿Por qué empezar aquí en lugar de pasar directamente a un harness de computer use completamente autónomo?

Porque esto enseña el orden correcto:

  1. hacer que el workflow sea durable
  2. agregar aprobaciones
  3. restringir el entorno
  4. automatizar una ruta conocida
  5. solo entonces agregar navegación controlada por el modelo donde el código determinista deja de ser suficiente

Eso es mucho más fácil de operar de forma segura.

Cuándo usar computer use realmente controlado por un modelo

En algún momento, los scripts deterministas de Playwright dejan de ser suficientes.

Ahí es donde entran la guía de computer use de OpenAI y la herramienta de computer use de Anthropic. Ambas documentaciones oficiales recomiendan un entorno aislado y presentan la supervisión humana como parte de un despliegue seguro.

Usa computer use controlado por un modelo cuando:

  • la interfaz cambia con demasiada frecuencia para usar selectores frágiles
  • el entorno abarca varias herramientas o aplicaciones
  • necesitas navegación exploratoria en lugar de una secuencia fija definida en código

Aun así, mantén las mismas reglas:

  • navegador o VM aislados
  • aplicaciones o dominios en una lista de permitidos
  • aprobación antes de pasos de alto impacto
  • capturas de pantalla, logs y artefactos finales registrados

El modelo puede cambiar. El límite del producto no debería hacerlo.

Tres workflows reales que vale la pena construir

Si buscas puntos de partida concretos y relevantes para producción, estos son los tres mejores.

1. Agente de resumen matutino

  • Activador: cada día hábil a las 7:00 AM
  • Entradas: resúmenes de la bandeja de entrada, cola de soporte, calendario, KPIs de ayer
  • Resultado: un resumen en markdown
  • Aprobación: no necesaria
  • Computer use: solo si alguna fuente no tiene API

2. Agente de métricas semanales

  • Activador: cada lunes a las 8:00 AM
  • Entradas: consultas al warehouse, exportaciones de Stripe, dashboards de gasto publicitario
  • Resultado: hoja de cálculo y resumen ejecutivo
  • Aprobación: necesaria si debe abrir una herramienta financiera o exportar datos de ella
  • Computer use: buena alternativa cuando la herramienta financiera solo está disponible en el navegador

3. Agente de transferencia de PR

  • Activador: inicio manual desde el teléfono o la computadora
  • Entradas: repo, issue, tests fallidos, reglas de la rama
  • Resultado: patch, resultados de tests y PR en borrador
  • Aprobación: obligatoria antes de hacer push o merge
  • Computer use: útil cuando el workflow del repo depende de herramientas locales de escritorio o de estado disponible solo en el IDE

Estos ejemplos se acercan a los que Anthropic ya describe públicamente, por lo que son mejores puntos de partida que las demos de ciencia ficción.

Errores comunes

  • Confundir persistencia con memoria. Un historial de chat no es un sistema de tareas.
  • Usar computer use como primera herramienta. Usa primero APIs o conectores.
  • Omitir los estados de aprobación. Si el agente puede interactuar con dinero, producción o datos de clientes, agrega una pausa.
  • Esconder los artefactos dentro del chat. Por lo general, los agentes persistentes deberían devolver archivos, informes, pull requests o resultados estructurados.
  • Ejecutar todo en un solo proceso. El estado durable de las tareas debería sobrevivir a los reinicios.

La lección para el producto

La gran lección de Dispatch, Cowork, computer use y el modo background no es que los agentes se estén volviendo mágicos.

Es que los productos basados en agentes se están volviendo operativos.

Tienen:

  • bandejas de entrada
  • programaciones
  • ciclos de vida de tareas
  • aprobaciones
  • artefactos
  • interfaces para retomar el trabajo desde el teléfono, la computadora y el navegador

Ese es un objetivo de diseño mucho mejor que «hagamos que el chatbot sea más autónomo».

Si construyes tu primera versión alrededor de tareas durables, estados claros y aprobaciones bien delimitadas, podrás evolucionar hacia workflows siempre activos sin perder el control.

Fuentes

La documentación pública es mucho más clara sobre el comportamiento del producto que sobre los detalles de su implementación interna, especialmente en el caso de Dispatch. Por lo tanto, los ejemplos de código de este artículo son patrones de implementación para construir productos con agentes persistentes, no descripciones del backend privado de Anthropic obtenidas mediante ingeniería inversa.