TuBrief
Subscribed Channels
Videos
Community

Arquitectura para mitigar el Cold Start sin servidor y reducir costes en agentes Vercel Eve

TuBrief Editorial
July 23, 2026
0
Computing/Software

Written with AI assistance from the source video. The video is the authority.

Español한국어English中文हिन्दीDeutschFrançaisРусскийPortuguês日本語Bahasa Indonesiaالعربية

Related Video

Ship 26 NYC - Taller - Construye un Agente con Eve: El Marco de Trabajo de Agentes Abiertos39:50

Ship 26 NYC - Taller - Construye un Agente con Eve: El Marco de Trabajo de Agentes Abiertos

Vercel

More from the community

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

September 13, 2026

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

September 13, 2026

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

September 13, 2026

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

September 13, 2026

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

September 12, 2026

Apple Won the AI Race

September 12, 2026

Comments (0)

Log in to leave a comment

No posts yet

© 2026 . All rights reserved.

TuBrief
Subscribed Channels
Videos
Community
Log in

Arquitectura para mitigar el Cold Start sin servidor y reducir costes en agentes Vercel Eve

Al desplegar agentes basados en Vercel Eve en producción, uno se enfrenta de inmediato a la naturaleza sin estado (Statelessness) propia de los entornos serverless. Cuando la solicitud finaliza, la instancia se cierra y el estado de ejecución se pierde. Sin embargo, consultar una base de datos como PostgreSQL cada vez para restaurar la sesión genera latencias superiores a los 100 ms y dispara los costes de la base de datos.

Para superar las limitaciones de serverless, he resumido tres arquitecturas utilizadas en entornos de producción reales.

1. Reducir la latencia de restauración de sesión por debajo de 50 ms con Upstash Redis

En un entorno serverless, abrir una nueva conexión a la base de datos en cada petición es el camino más rápido para arruinar tanto los costes de infraestructura como la velocidad de respuesta. Situar Upstash Redis, que se comunica mediante HTTP REST API, como capa de caché de sesión permite resolver este problema.

`
[User Request]
│
▼
┌──────────────┐ < 50ms (HTTP REST) ┌────────────────────────┐
│ Vercel Eve │ ────────────────────────> │ Upstash Redis │
│ Agent │ <──────────────────────── │ (Session State Storage)│
└──────────────┘ Session Context Restored└────────────────────────┘
│
│ Compress History (Sliding Window + Summary)
▼
┌──────────────┐
│ LLM Provider │
└──────────────┘

`

Los datos de la sesión se obtienen en menos de 50 ms a través de la REST API. Reemplazar las consultas directas a la RDB por una caché reduce drásticamente el consumo de Read Capacity.

Criterio de evaluación RDB tradicional (PostgreSQL) DynamoDB (On-Demand) Upstash Redis (HTTP REST)
Método de conexión Socket TCP AWS SDK HTTP/REST API
Latencia media de lectura 50ms - 200ms 10ms - 20ms 1ms - 5ms (Edge < 50ms)
Idoneidad Serverless Baja (Exhaustión de conexiones) Media (Existe latencia de conexión) Muy alta (Soporta Scale-to-Zero)
Estructura de costes Facturación por hora de instancia aprovisionada Facturación por unidad de petición RCU/WCU Facturación por unidad de petición de comandos ($0.20/100k)
Propósito principal de uso Transacciones ACID, almacenamiento original Almacenamiento y búsqueda de datos permanentes Caché de sesión, Rate Limit, memoria de agente

El código para guardar y restaurar la sesión se mantiene simple.

`typescript
import { Redis } from "@upstash/redis";

const redis = Redis.fromEnv();

interface AgentSessionContext {
userId: string;
currentStep: string;
intermediateThoughts: Record<string, unknown>[];
lastActiveTimestamp: number;
}

export async function restoreSessionContext(sessionId: string): Promise<AgentSessionContext | null> {
const cacheKey = session:context:${sessionId};
const cachedContext = await redis.get(cacheKey);
return cachedContext ?? null;
}

export async function saveSessionContext(
sessionId: string,
context: AgentSessionContext,
ttlSeconds: number = 3600
): Promise {
const cacheKey = session:context:${sessionId};
await redis.set(cacheKey, JSON.stringify(context), { ex: ttlSeconds });
}

`

A medida que la conversación se alarga, los costes de tokens siguen aumentando. Se utiliza un enfoque en el que solo se conservan como originales los últimos 6 turnos, y la conversación anterior se resume mediante un modelo ligero y se coloca en la parte superior del prompt.

`typescript
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";

interface Message {
role: "user" | "assistant" | "system";
content: string;
}

export async function compressConversationHistory(
messages: Message[],
recentWindowSize: number = 6
): Promise<Message[]> {
if (messages.length <= recentWindowSize) return messages;

const systemMessage = messages.find((m) => m.role === "system");
const nonSystemMessages = messages.filter((m) => m.role !== "system");

const olderMessages = nonSystemMessages.slice(0, nonSystemMessages.length - recentWindowSize);
const recentMessages = nonSystemMessages.slice(nonSystemMessages.length - recentWindowSize);

const summaryResponse = await generateText({
model: openai("gpt-4o-mini"),
prompt: 다음 대화의 핵심 사실과 결정 사항만 200자 이내로 요약하세요:\n\n${JSON.stringify(olderMessages)},
});

const compressedHistory: Message[] = [];
if (systemMessage) compressedHistory.push(systemMessage);
compressedHistory.push({
role: "system",
content: [이전 대화 요약]: ${summaryResponse.text},
});
compressedHistory.push(...recentMessages);

return compressedHistory;
}

`

2. Patrones de protección frente a latencias y fallos en API externas

Si se encuentra un error 429 (Rate Limit) o 5xx al llamar a herramientas externas, toda la inferencia del agente se interrumpe. Se debe implementar una estrategia de backoff exponencial combinada con Full Jitter y un Circuit Breaker.

La fórmula de backoff exponencial evita los cuellos de botella al incorporar números aleatorios en lugar de incrementar el tiempo de espera de forma estrictamente proporcional.

Textdelay=minleft(Textmax,Textbaseimes2extattemptight)imesleft(0.5+extrandom(0,1.0)ight)T_{ ext{delay}} = minleft(T_{ ext{max}}, T_{ ext{base}} imes 2^{ ext{attempt}} ight) imes left(0.5 + ext{random}(0, 1.0) ight)Textdelay​=minleft(Textmax​,Textbase​imes2extattemptight)imesleft(0.5+extrandom(0,1.0)ight)

`typescript
export interface RetryConfig {
maxRetries: number;
baseDelayMs: number;
maxDelayMs: number;
}

export async function executeWithExponentialBackoff(
fn: () => Promise,
config: RetryConfig = { maxRetries: 3, baseDelayMs: 200, maxDelayMs: 8000 }
): Promise {
let attempt = 0;

while (true) {
try {
return await fn();
} catch (error: any) {
attempt++;
const statusCode = error?.status || error?.response?.status;
const isUnretryable = statusCode && statusCode >= 400 && statusCode < 500 && statusCode !== 429;

  if (attempt > config.maxRetries || isUnretryable) throw error;

  const calculatedDelay = Math.min(
    config.maxDelayMs,
    config.baseDelayMs * Math.pow(2, attempt)
  );
  const jitteredDelay = calculatedDelay * (0.5 + Math.random());

  await new Promise((resolve) => setTimeout(resolve, jitteredDelay));
}

}
}

`

Si el fallo se prolonga, el Circuit Breaker bloquea las peticiones inmediatamente (Fail-Fast) y ejecuta la lógica de Fallback.

Estado de respuesta de API externa Estado del Circuit Breaker Mecanismo de funcionamiento Resultado de procesamiento del agente
HTTP 200 OK Closed Paso normal e incremento del contador de éxitos Suministra datos externos al agente con normalidad
HTTP 429 / 503 Closed $
ightarrow$ Open Ejecución de backoff exponencial y cambio a Open al alcanzar el umbral de tasa de fallos Apertura del circuito tras los reintentos
Estado Circuit OPEN Open Bloqueo de peticiones de red a la API externa (Fail-Fast) Uso de herramienta alternativa o emisión de mensaje de Fallback
Tras expirar Cooldown Half-Open Verificación de recuperación del servicio externo con una única petición de sondeo (Probing) Normalización del circuito si tiene éxito; rebloqueo si falla

`typescript
export class CircuitBreaker {
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
private failureCount = 0;
private lastStateChange = Date.now();

constructor(
private failureThreshold: number = 5,
private cooldownPeriodMs: number = 30000
) {}

async execute(requestFn: () => Promise, fallbackFn: () => Promise): Promise {
const now = Date.now();

if (this.state === 'OPEN') {
  if (now - this.lastStateChange > this.cooldownPeriodMs) {
    this.state = 'HALF_OPEN';
    this.lastStateChange = now;
  } else {
    return await fallbackFn();
  }
}

try {
  const result = await requestFn();
  if (this.state === 'HALF_OPEN') {
    this.state = 'CLOSED';
    this.failureCount = 0;
    this.lastStateChange = now;
  }
  return result;
} catch (error) {
  this.failureCount++;
  if (this.failureCount >= this.failureThreshold || this.state === 'HALF_OPEN') {
    this.state = 'OPEN';
    this.lastStateChange = now;
  }
  return await fallbackFn();
}

}
}

`

3. Integración de aprobación asíncrona (Human-in-the-loop) sin Timeouts

Las funciones serverless tienen un límite de tiempo de ejecución. Mantener la petición abierta a la espera de la aprobación de un pago o de la eliminación de una base de datos provocará un error de timeout.

`
[Agent Action] ──> Eve Tool (needsApproval: true)
│
▼
[Checkpoint Saved & Instance Terminated]
│
├─> Slack Notification (Interactive Card)
│
[Human Approve] ───────>│ (Webhook POST Callback)
│
▼
[Resume Agent & Proceed Transaction]

`

Se asigna needsApproval: true a la herramienta de Eve, se detiene la ejecución y únicamente se guarda un punto de control (checkpoint).

`typescript
import { defineTool } from "@vercel/eve";
import { z } from "zod";

export const deleteDatabaseTool = defineTool({
name: "delete_database",
description: "특정 테넌트의 영구 데이터베이스 레코드를 삭제합니다.",
needsApproval: true,
input: z.object({
tenantId: z.string(),
reason: z.string(),
}),
execute: async ({ tenantId }) => {
return await db.tenant.delete({ where: { id: tenantId } });
},
});

`

La aprobación humana se recibe mediante un callback de webhook para reanudar el proceso.

`typescript
import { createWebhook } from "@vercel/workflows";

export async function handleApprovalWorkflow(event: { approvalId: string; payload: any }) {
const webhook = createWebhook();

await sendSlackApprovalCard({
approvalId: event.approvalId,
callbackUrl: webhook.url,
payload: event.payload,
});

try {
const { approved, userReason } = await webhook.timeout("12h");

if (!approved) {
  await rollbackPreviousSteps(event.payload);
  return { status: "REJECTED", reason: userReason };
}

return await proceedAction(event.payload);

} catch (error) {
await rollbackPreviousSteps(event.payload);
return { status: "TIMEOUT_CANCELLED" };
}
}

`

4. Validación de prompts y enrutamiento Canary en la etapa de CI/CD

Las alucinaciones que ocurren tras modificar un prompt son difíciles de detectar mediante pruebas manuales. Se diseña el pipeline para que los PR solo se fusionen si superan las métricas de DeepEval.

Métrica de evaluación Umbral de aceptación Criterio de evaluación
Faithfulness ge0.85ge 0.85ge0.85 Presencia o ausencia de distorsión de hechos con respecto al contexto proporcionado
Answer Relevancy ge0.75ge 0.75ge0.75 Grado de coincidencia con el propósito de la pregunta del usuario
Hallucination Rate le0.10le 0.10le0.10 Proporción de apariciones de alucinaciones dentro del conjunto de pruebas
Tool Calling Accuracy ge0.90ge 0.90ge0.90 Selección correcta de herramientas con especificación OpenAPI y cumplimiento de tipos

Se ejecuta Pytest en GitHub Actions para bloquear la compilación si no se alcanza el umbral.

`yaml
name: Eve Agent Prompt Evaluation Pipeline

on:
pull_request:
branches: [ main ]

jobs:
evaluate-agent:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

  - name: Set up Python
    uses: actions/setup-python@v5
    with:
      python-version: '3.11'

  - name: Install Evaluation Dependencies
    run: |
      pip install deepeval pytest

  - name: Run DeepEval Regression Suite
    env:
      OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    run: |
      pytest test_agent_evals.py --deepeval-metric-threshold=0.85

`

Durante el despliegue, se integran Edge Config y un Middleware para aplicar el nuevo prompt inicialmente solo al 10% del tráfico.

`typescript
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { get } from '@vercel/edge-config';

export async function middleware(req: NextRequest) {
const res = NextResponse.next();
let variant = req.cookies.get('agent_canary_variant')?.value;

if (!variant) {
const canaryRate = (await get('canary_traffic_rate')) || 0.10;
variant = Math.random() < canaryRate ? 'canary' : 'control';
res.cookies.set('agent_canary_variant', variant, { path: '/', httpOnly: true });
}

res.headers.set('x-agent-prompt-version', variant === 'canary' ? 'v2-canary' : 'v1-stable');
return res;
}

export const config = {
matcher: '/api/agent/:path*',
};

`