TuBrief
구독 채널
비디오
커뮤니티

Struktur Mengatasi Serverless Cold Start dan Biaya pada Agen Vercel Eve

TuBrief 편집팀
2026년 7월 23일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

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

관련 영상

Ship 26 NYC - Workshop - Bangun Agen dengan Eve: Kerangka Kerja Agen Terbuka39:50

Ship 26 NYC - Workshop - Bangun Agen dengan Eve: Kerangka Kerja Agen Terbuka

Vercel

커뮤니티의 다른 글

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 13일

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

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Struktur Mengatasi Serverless Cold Start dan Biaya pada Agen Vercel Eve

Ketika menerapkan agen berbasis Vercel Eve ke lingkungan produksi, Anda akan langsung berhadapan dengan sifat stateless khas dari serverless. Begitu permintaan selesai, instans ditutup dan status eksekusi hilang. Namun, memulihkan sesi dengan selalu mengakses DB seperti PostgreSQL setiap saat akan menyebabkan latensi di atas 100ms beserta lonjakan biaya DB yang drastis.

Untuk mengatasi keterbatasan serverless, berikut ringkasan tiga struktur yang digunakan di lingkungan produksi nyata.

1. Menurunkan Latensi Pemulihan Sesi di Bawah 50ms dengan Upstash Redis

Di lingkungan serverless, membuka koneksi DB baru setiap kali adalah cara tercepat yang merusak biaya infrastruktur sekaligus kecepatan respons. Masalah ini dapat diselesaikan dengan menempatkan Upstash Redis, yang berkomunikasi melalui HTTP REST API, sebagai session cache layer.

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

`

Data sesi diambil melalui REST API dalam waktu kurang dari 50ms. Mengganti kueri RDB langsung dengan cache akan mengurangi konsumsi Read Capacity secara signifikan.

Item Evaluasi RDB Tradisional (PostgreSQL) DynamoDB (On-Demand) Upstash Redis (HTTP REST)
Metode Koneksi TCP Socket AWS SDK HTTP/REST API
Latensi Baca Rata-rata 50ms - 200ms 10ms - 20ms 1ms - 5ms (Edge < 50ms)
Kesesuaian Serverless Rendah (Connection Exhaustion) Sedang (Terdapat latensi koneksi) Sangat Tinggi (Mendukung Scale-to-Zero)
Struktur Biaya Penagihan per jam instans ter-provisioning Penagihan per unit permintaan RCU/WCU Penagihan per unit permintaan Command ($0.20/100k)
Tujuan Penggunaan Utama Transaksi ACID, penyimpanan asli Penyimpanan & pencarian data permanen Caching sesi, Rate Limit, memori agen

Kode untuk menyimpan dan memulihkan sesi dibuat tetap sederhana.

`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 });
}

`

Seiring memanjangnya percakapan, biaya token akan terus membengkak. Metode yang digunakan adalah mempertahankan 6 turn terakhir sebagai data asli, sementara percakapan sebelumnya diringkas menggunakan model ringan dan ditempatkan di bagian atas 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: Ringkas fakta kunci dan keputusan dari percakapan berikut dalam waktu kurang dari 200 karakter:\n\n${JSON.stringify(olderMessages)},
});

const compressedHistory: Message[] = [];
if (systemMessage) compressedHistory.push(systemMessage);
compressedHistory.push({
role: "system",
content: [Ringkasan Percakapan Sebelumnya]: ${summaryResponse.text},
});
compressedHistory.push(...recentMessages);

return compressedHistory;
}

`

2. Pola Pertahanan Latensi dan Kegagalan API Eksternal

Jika mengalami kesalahan 429 (Rate Limit) atau 5xx saat memanggil tool eksternal, seluruh proses penalaran agen akan terhenti. Oleh karena itu, perlu dipasang exponential backoff yang dikombinasikan dengan Full Jitter serta circuit breaker.

Rumus exponential backoff menghindari kemacetan dengan menambahkan angka acak alih-alih meningkatkan waktu tunggu secara berbanding lurus.

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));
}

}
}

`

Jika gangguan berlangsung lama, circuit breaker akan langsung memblokir permintaan (Fail-Fast) dan menjalankan logika Fallback.

Status Respons API Eksternal Status Circuit Breaker Mekanisme Kerja Hasil Pemrosesan Agen
HTTP 200 OK Closed Lolos secara normal & menaikkan penghitung keberhasilan Menyuplai data eksternal ke agen secara normal
HTTP 429 / 503 Closed $
ightarrow$ Open Mengeksekusi exponential backoff lalu menjadi Open jika ambang tingkat kegagalan tercapai Sirkuit terbuka setelah penanganan ulang
Status Circuit OPEN Open Memblokir permintaan jaringan API eksternal (Fail-Fast) Menggunakan Tool alternatif atau menampilkan pesan Fallback
Setelah Cooldown Kedaluwarsa Half-Open Memverifikasi pemulihan layanan eksternal dengan permintaan Probing tunggal Sirkuit normal jika berhasil, sirkuit terblokir kembali jika gagal

`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. Integrasi Persetujuan Asinkron Tanpa Timeout (Human-in-the-loop)

Fungsi serverless memiliki batasan waktu eksekusi. Biarkan permintaan tetap terbuka hanya untuk menunggu persetujuan pembayaran atau penghapusan DB akan menyebabkan kesalahan timeout.

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

`

Berikan needsApproval: true pada tool Eve, hentikan eksekusi, dan menyisakan checkpoint saja.

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

export const deleteDatabaseTool = defineTool({
name: "delete_database",
description: "Menghapus rekaman basis data permanen dari penyewa tertentu.",
needsApproval: true,
input: z.object({
tenantId: z.string(),
reason: z.string(),
}),
execute: async ({ tenantId }) => {
return await db.tenant.delete({ where: { id: tenantId } });
},
});

`

Persetujuan manusia diterima melalui webhook callback untuk melanjutkan proses.

`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. Validasi Prompt dan Canary Routing pada Tahap CI/CD

Fenomena halusinasi yang terjadi setelah modifikasi prompt sulit dideteksi melalui pengujian manual. Alur pipa (pipeline) dirancang sedemikian rupa sehingga PR hanya akan digabungkan jika berhasil lolos dari indikator DeepEval.

Indikator Evaluasi Ambang Penerimaan Kriteria Evaluasi
Faithfulness ge0.85ge 0.85ge0.85 Ada/tidaknya distorsi fakta dibandingkan Context yang diberikan
Answer Relevancy ge0.75ge 0.75ge0.75 Tingkat kesesuaian dengan tujuan pertanyaan pengguna
Hallucination Rate le0.10le 0.10le0.10 Rasio terjadinya halusinasi di dalam test set
Tool Calling Accuracy ge0.90ge 0.90ge0.90 Pemilihan tool spesifikasi OpenAPI yang benar dan tingkat kepatuhan tipe

Jalankan Pytest di GitHub Actions untuk memblokir build jika berada di bawah ambang batas.

`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

`

Saat deployment, hubungkan Edge Config dan middleware untuk menerapkan prompt baru hanya pada 10% dari lalu lintas terlebih dahulu.

`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*',
};

`