Cómo construir agentes durables en producción con Vercel AI SDK 7 y WorkflowAgent
Tutorial completo: WorkflowAgent TypeScript que sobrevive reinicios, gestiona aprobaciones antes de acciones irreversibles y emite telemetría a Langfuse o Datadog. 60 minutos con Next.js y Claude Opus 4.8.
Cognitiva, agencia chilena de IA, publica esta guía el mismo día en que Vercel lanzó AI SDK 7 —25 de junio de 2026—, la versión que marca el paso definitivo de prototipo frágil a agente TypeScript de producción. El cambio central es WorkflowAgent: una clase que serializa el estado de ejecución del agente en cada paso y lo persiste en almacenamiento durable, de modo que un reinicio del proceso o un timeout de Vercel Functions no lo destruye. El agente retoma exactamente donde quedó, sin repetir pasos completados ni perder el contexto acumulado. La durabilidad es el primer pilar. El segundo es el sistema de aprobaciones de herramientas: AI SDK 7 detiene la ejecución antes de cualquier acción irreversible —enviar un correo, borrar un registro, ejecutar un script— y espera confirmación de un operador humano o de una política programática con firma HMAC que previene solicitudes forjadas. El tercero es la observabilidad nativa: registerTelemetry() conecta el agente a Langfuse, Datadog, Braintrust o cualquier backend OpenTelemetry con una sola llamada, y reporta duración por paso, tokens consumidos y tasa de éxito en tiempo real. Este tutorial de 60 minutos te lleva desde la instalación hasta un agente en producción con Next.js App Router y Claude Opus 4.8.
Del prototipo al agente resiliente: qué cambió en AI SDK 7
Un agente de IA que corre en un loop de generateText funciona bien en demos y entornos locales. Falla en producción por la misma razón que un script de larga duración: cuando el proceso muere —SIGTERM, timeout de serverless, reinicio del contenedor— el estado desaparece junto con él. El agente no tiene memoria de qué herramientas ejecutó, qué pasos completó ni en qué punto quedó el razonamiento. El resultado es una ejecución que se repite desde cero, duplicando acciones ya realizadas y confundiendo al usuario.
AI SDK 7, lanzado por Vercel el 25 de junio de 2026, ataca este problema directamente con WorkflowAgent: una clase que persiste el estado del agente en disco o en una base de datos tras cada paso de razonamiento. Cuando el proceso reinicia, WorkflowAgent lee el último checkpoint y reanuda la ejecución desde ahí. El loop deja de ser una cadena frágil en memoria para convertirse en una secuencia de pasos durables, cada uno verificable y auditable.
AI SDK 7 organiza sus capacidades para producción en cinco pilares:
- Desarrollo: ToolLoopAgent con contexto de herramientas tipado, control de razonamiento y soporte MCP Apps
- Ejecución: WorkflowAgent durable, aprobaciones de herramientas con firma HMAC, timeouts granulares y sandbox
- Integración: HarnessAgent como API unificada para Claude Code, Codex, Deep Agents y otras harnesses externas
- Observabilidad: registerTelemetry() con OpenTelemetry, métricas por paso (duración, tokens, tasa de éxito) y lifecycle callbacks
- Tiempo real: experimental_useRealtime() para sesiones de voz vía WebSocket y experimental_generateVideo() para generación de video
Prerrequisitos y estructura del proyecto
El tutorial usa Next.js 15 con App Router, TypeScript estricto y pnpm como gestor de paquetes. Necesitas una cuenta en Vercel con Fluid Compute habilitado —disponible en planes Pro y Enterprise— para soportar ejecuciones de más de 60 segundos sin timeout. El agente usa Claude Opus 4.8, por lo que debes tener una API key de Anthropic con acceso a ese modelo. Para la telemetría, basta el tier gratuito de Langfuse o una cuenta de Datadog.
Requisitos del entorno
- Node.js 20 o superior
- pnpm 9+
- Cuenta Vercel (plan Pro o Enterprise para Fluid Compute)
- API key de Anthropic con acceso a Claude Opus 4.8
- Cuenta en Langfuse (tier gratuito) o Datadog para telemetría
Estructura del proyecto
mi-agente/
├── app/
│ ├── api/
│ │ ├── agent/
│ │ │ └── route.ts
│ │ └── agent-approve/
│ │ └── route.ts
│ └── page.tsx
├── lib/
│ ├── model.ts
│ └── telemetry.ts
├── .env.local
└── vercel.jsonInstalar AI SDK 7 y configurar el modelo
Instala las dependencias. El paquete principal es ai en su versión 7 (o latest); @ai-sdk/anthropic provee el conector para Claude. Zod es obligatorio para definir los schemas de entrada de cada herramienta.
pnpm create next-app mi-agente --typescript --app --src-dir no
cd mi-agente
pnpm add ai@latest @ai-sdk/anthropic zod langfuseDefine las variables de entorno en .env.local. El campo WORKFLOW_SECRET protege el endpoint de aprobaciones con firma HMAC; genera un valor aleatorio con openssl rand -hex 32.
# .env.local
ANTHROPIC_API_KEY=tu-api-key-aqui
LANGFUSE_PUBLIC_KEY=pk-lf-tu-clave
LANGFUSE_SECRET_KEY=sk-lf-tu-clave
LANGFUSE_BASE_URL=https://cloud.langfuse.com
WORKFLOW_SECRET=genera-con-openssl-rand-hex-32Crea lib/model.ts para centralizar la definición del modelo. Esto permite cambiar el modelo en un solo lugar si necesitas migrar a una versión distinta de Claude.
// lib/model.ts
import { anthropic } from '@ai-sdk/anthropic'
export const model = anthropic('claude-opus-4-8')Construir el WorkflowAgent durable
WorkflowAgent recibe un workflowId que actúa como clave del checkpoint: si el proceso reinicia y vuelve a llamar al agente con el mismo workflowId, la ejecución retoma desde el último paso persistido en lugar de iniciar desde cero. El workflowId debe ser determinista para cada tarea: el ID de un ticket de soporte, el UUID de una orden, o el hash del prompt inicial son buenas opciones.
La API route crea una instancia del agente por request, pero el estado no vive en memoria del servidor: cada paso completo escribe su resultado en el almacén durable configurado. El parámetro maxSteps previene loops infinitos. Las herramientas se dividen en dos categorías: las que solo leen (execute directo) y las que modifican estado irreversiblemente (con el campo approval).
// app/api/agent/route.ts
import { WorkflowAgent, tool } from 'ai'
import { z } from 'zod'
import { model } from '@/lib/model'
export const maxDuration = 300
export async function POST(req: Request) {
const body = await req.json()
const { prompt, workflowId } = body
const agent = new WorkflowAgent({
model,
workflowId,
maxSteps: 20,
tools: {
consultarCRM: tool({
description: 'Consulta datos de un cliente en el CRM por su RUT',
inputSchema: z.object({
rut: z.string().describe('RUT del cliente sin puntos ni guion')
}),
execute: async ({ rut }) => {
const response = await fetch('/api/crm?rut=' + rut)
return response.json()
}
}),
actualizarRegistro: tool({
description: 'Actualiza un campo de un registro en el CRM. Requiere aprobacion.',
inputSchema: z.object({
id: z.string(),
campo: z.string(),
valor: z.string()
}),
approval: 'user-approval',
execute: async ({ id, campo, valor }) => {
const response = await fetch('/api/crm/' + id, {
method: 'PATCH',
body: JSON.stringify({ campo, valor })
})
return response.json()
}
})
}
})
const stream = await agent.streamRun(prompt)
return stream.toDataStreamResponse()
}La herramienta consultarCRM se ejecuta directamente porque solo lee datos: no requiere intervención humana. actualizarRegistro lleva approval: 'user-approval', lo que hace que WorkflowAgent pause la ejecución, notifique al frontend con el toolCallId y los argumentos propuestos, y espere la decisión del operador antes de continuar.
Aprobaciones de herramientas: detener el agente antes de lo irreversible
Cuando WorkflowAgent encuentra una herramienta con approval: 'user-approval', persiste el estado pendiente en el checkpoint y emite un evento de aprobación. El frontend puede mostrar ese evento al operador con los argumentos exactos que el agente propone usar. El agente permanece en estado suspendido hasta recibir la decisión, sin consumir tiempo de ejecución ni API de Anthropic.
Para lógica de aprobación más granular, puedes pasar una función en lugar de la cadena 'user-approval'. Esa función recibe el toolCall con todos sus argumentos y puede auto-aprobar según reglas de negocio, rechazar sin intervención humana, o reenviar al operador solo cuando el cambio supera un umbral de riesgo.
// Aprobacion programatica con escalada selectiva
actualizarRegistro: tool({
description: 'Actualiza un registro. Requiere aprobacion para campos criticos.',
inputSchema: z.object({ id: z.string(), campo: z.string(), valor: z.string() }),
approval: async ({ toolCall }) => {
const camposCriticos = ['estado_pago', 'monto', 'rut_titular']
if (!camposCriticos.includes(toolCall.input.campo)) {
// Auto-aprobar cambios no criticos
return { approved: true }
}
// Escalar al operador solo para campos criticos
const res = await fetch('/api/agent-approve', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ toolCall })
})
return res.json()
},
execute: async ({ id, campo, valor }) => updateCRMRecord(id, campo, valor)
})El endpoint de aprobaciones verifica la autenticidad de cada solicitud con HMAC-SHA256. Esto previene que un atacante externo forje aprobaciones enviando directamente al endpoint sin pasar por el agente.
// app/api/agent-approve/route.ts
import { createHmac, timingSafeEqual } from 'crypto'
export async function POST(req: Request) {
const body = await req.json()
const { toolCall, approved } = body
const secret = process.env.WORKFLOW_SECRET || ''
const payload = JSON.stringify(toolCall)
const expected = createHmac('sha256', secret).update(payload).digest('hex')
const incoming = req.headers.get('x-approval-signature') || ''
const isValid = timingSafeEqual(
Buffer.from(expected),
Buffer.from(incoming.padEnd(expected.length, '0'))
)
if (!isValid) {
return Response.json({ error: 'Firma invalida' }, { status: 401 })
}
return Response.json({ approved, toolCallId: toolCall.id })
}Observabilidad con registerTelemetry()
AI SDK 7 introduce registerTelemetry() como punto de entrada único para toda la telemetría de la aplicación. Una sola llamada al inicio cubre todos los agentes, todas las API routes y todos los modelos: no hay que instrumentar cada WorkflowAgent por separado. La telemetría reporta duración por paso, tokens consumidos, tasa de éxito y razón de finalización (maxSteps, aprobación rechazada, error de herramienta) para cada ciclo del agente.
Conectar Langfuse
Crea lib/telemetry.ts e importa initTelemetry() en tu app/layout.tsx para garantizar que el registro ocurra antes del primer request:
// lib/telemetry.ts — configuracion para Langfuse
import { registerTelemetry } from 'ai'
import Langfuse from 'langfuse'
const langfuse = new Langfuse({
publicKey: process.env.LANGFUSE_PUBLIC_KEY || '',
secretKey: process.env.LANGFUSE_SECRET_KEY || '',
baseUrl: process.env.LANGFUSE_BASE_URL || 'https://cloud.langfuse.com'
})
export function initTelemetry() {
registerTelemetry({
provider: langfuse,
metadata: {
project: 'agente-crm-produccion',
environment: process.env.NODE_ENV || 'production'
}
})
}Conectar Datadog
Si prefieres Datadog, instala dd-trace (pnpm add dd-trace) y registra el tracer de la misma forma:
// lib/telemetry.ts — configuracion para Datadog
import { registerTelemetry } from 'ai'
import tracer from 'dd-trace'
tracer.init({
service: 'agente-crm',
env: process.env.DD_ENV || 'production',
logInjection: true
})
export function initTelemetry() {
registerTelemetry({
provider: tracer,
metadata: { service: 'agente-crm' }
})
}En ambos casos, las trazas incluyen el árbol completo de pasos del WorkflowAgent: cada decisión del modelo, cada herramienta llamada (aprobada o rechazada), la duración exacta y el volumen de tokens por paso. Desde Langfuse puedes filtrar por workflowId para ver la historia completa de una ejecución que atravesó varios reinicios de proceso.
Desplegar en Vercel con Fluid Compute
Los agentes de larga ejecución necesitan un runtime que no los corte a los 30 segundos de un serverless convencional. Vercel Fluid Compute extiende el tiempo máximo de ejecución y minimiza los cold starts para funciones bajo carga constante. La configuración requiere dos pasos: declarar maxDuration en la route (ya lo hiciste) y confirmarlo en vercel.json:
{
"functions": {
"app/api/agent/route.ts": {
"maxDuration": 300
}
}
}Antes de hacer deploy, completa este checklist en el dashboard de Vercel:
- Agrega ANTHROPIC_API_KEY, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_BASE_URL y WORKFLOW_SECRET como variables de entorno en Environment Variables
- Verifica que tu plan incluye Fluid Compute (disponible en Pro y Enterprise)
- Ejecuta vercel deploy y confirma que el build termina sin errores de TypeScript
- En el panel Observability de Vercel, verifica que las primeras requests llegan y responden dentro del maxDuration configurado
Una vez desplegado, prueba el endpoint con una herramienta de tu elección. Incluye siempre el campo workflowId en el body: es el identificador que permite a WorkflowAgent retomar la ejecución si el proceso se reinicia durante una request de larga duración.
Verificar la durabilidad: pruebas después del deploy
Un agente durable no está verificado hasta que demuestras que sobrevive un reinicio real. El test más directo es enviar un prompt que requiera varios pasos, interrumpir el proceso a la mitad, y confirmar que al reiniciar con el mismo workflowId el agente continúa desde el checkpoint sin repetir trabajo.
- Inicia el agente con un workflowId fijo y un prompt que requiera 5 o más pasos secuenciales
- Interrumpe el proceso en el paso 3 (SIGTERM, reinicio del pod, o simulación de timeout)
- Relanza el proceso y envía exactamente el mismo workflowId
- Verifica en los logs de Langfuse o Datadog que los pasos 1 a 3 no se ejecutaron dos veces
- Confirma que el agente completó desde el paso 4 hasta el final con el contexto correcto
- Activa el gate de aprobaciones: envía un prompt que llame a la herramienta irreversible y verifica que el agente se pausa esperando confirmación
- Rechaza la aprobación y confirma que el agente termina con estado limpio, sin escrituras parciales en el CRM
Si en el dashboard de Langfuse observas que los pasos 1 a 3 se ejecutaron dos veces, el checkpoint no está activo: verifica que workflowId sea exactamente el mismo string en ambas llamadas y que el proveedor de persistencia configurado en WorkflowAgent esté disponible durante el reinicio. Si el agente falla silenciosamente sin emitir trazas, confirma que initTelemetry() se llama antes del primer request en app/layout.tsx.
Preguntas frecuentes
Da el siguiente paso
Hablemos
Cuéntanos sobre tu negocio. Respondemos en 24 horas.
Otros canales
Escríbenos por WhatsApp o agenda una llamada de 30 minutos.