在2026年使用Next.js、Supabase和Cron构建Agent驱动的应用程序

在过去的半年里,我沉浸在为客户构建三个agent驱动的应用程序中。其中两个已经完全上线,每天自主处理数千个任务。那么,是什么在推动这些项目呢?是Next.js部署在Vercel上、Supabase用于持久化和实时需求,以及良好的基于cron的编排来保持agent对齐并防止过度API费用的强大组合。这不仅仅是理论——我会详细说明什么有效,陷阱是什么,以及我在深入之前希望拥有的所有见解。

Building Agent-Powered Apps with Next.js, Supabase & Cron in 2026

2026年Agent驱动的应用程序实际上是什么样子

当我谈论"agent驱动的应用程序"时,我不仅仅是在讨论一个重复系统提示的聊天机器人。想象一下自主处理多步任务、做出实时数据驱动决策并按计划运行的软件,所有这些都无需人工推动。

这是在生产环境中运行的内容的一个示例:

  • 内容管道扫描RSS源、生成摘要、起草社交帖子并将其排队等待审查。这个周期每30分钟重复一次,就像钟表一样。
  • 数据协调agent每晚连接到多个API,嗅出不一致之处,尝试自动更正修复,并上报它们无法解决的问题。
  • 客户入职agent由新注册webhook触发。这些agent配置资源、发送自定义序列,并根据他们学到的用户行为进行适应。

共同的线索是什么?他们需要状态持久化、计划执行、工具访问和人类查看和干预的方式。这是我们深入探讨的堆栈的最佳位置。

为什么这个堆栈有效

我已经尝试过其他设置。有LangChain配对Python后端,Temporal用于编排,以及自定义AWS队列系统。虽然它们都能完成工作,但它们带来了大多数团队到2026年就无法处理的大量操作复杂性。

这是我的直接比较:

堆栈 设置时间 操作开销 每天10K任务的成本 最适合
Next.js + Supabase + Vercel Cron 1-2天 ~$85-150/月 大多数agent应用
Python + Celery + Redis + AWS 3-5天 ~$200-400/月 重型计算任务
Temporal + Custom Workers 5-10天 非常高 ~$300-600/月 复杂工作流
Inngest + Next.js 1-2天 低-中 ~$100-200/月 事件驱动agent

Next.js组合在速度方面绝对胜出。您可以获得无服务器函数、仪表板UI、数据库、身份验证、实时更新和定时执行,而无需为基础设施担忧。对于我们中绝大多数构建agent应用的人来说,那已经足够了。

架构概述

让我们绘制一幅在各个项目中都大受欢迎的架构图:

┌─────────────────┐     ┌──────────────────┐
│   Vercel Cron    │────▶│  Next.js API Route │
│  (Scheduler)     │     │  (Agent Runner)    │
└─────────────────┘     └────────┬───────────┘
                                 │
                    ┌────────────┼────────────┐
                    ▼            ▼             ▼
              ┌──────────┐ ┌─────────┐ ┌───────────┐
              │ Supabase  │ │ LLM API │ │ External  │
              │ (State +  │ │(OpenAI/ │ │ Tools &   │
              │  Queue)   │ │ Claude) │ │ APIs      │
              └──────────┘ └─────────┘ └───────────┘
                    │
                    ▼
              ┌──────────┐
              │ Supabase  │
              │ Realtime  │──▶ Dashboard UI
              └──────────┘

cron作业到达Next.js API路由,然后从Supabase拉取任务,执行agent逻辑,将结果写回,并完成。同时,您的仪表板通过Supabase Realtime提供实时更新来关注行动。

是的,足够简单,但魔鬼在于细节。

Building Agent-Powered Apps with Next.js, Supabase & Cron in 2026 - architecture

使用Next.js和Vercel建立基础

当您在2026年构建agent应用程序时,Next.js 15与App Router是必须的。由于其Server Actions和Route Handlers,它为您提供最干净的执行模式。在Social Animal,我们已经完善了这个Next.js技能,让我告诉您,App Router对于后端密集型项目来说已经成熟得非常好。

启动项目很直接:

pnpx create-next-app@latest agent-app --typescript --tailwind --app
cd agent-app
pnpm add @supabase/supabase-js ai @ai-sdk/openai

我正在使用Vercel AI SDK,感谢它在不同LLM提供商中的统一接口。到2026年,ai包的版本4.x原生支持结构化输出、工具调用和流——LangChain你好吗?

现在您的vercel.json需要看起来像这样的cron配置:

{
  "crons": [
    {
      "path": "/api/agents/run",
      "schedule": "*/15 * * * *"
    },
    {
      "path": "/api/agents/cleanup",
      "schedule": "0 2 * * *"
    }
  ]
}

第一个cron每15分钟运行一次agent。第二个每晚清理。请记住,Vercel Pro($20/月)支持最高1分钟频率的cron作业。但是如果您在Hobby计划中,您只能限于每日cron,这对大多数agent驱动的需求来说是不够的。

Vercel函数配置

Agent任务可能比夏日大片还长(好吧,不完全是),但默认情况下需要超过10秒执行。像这样配置您的路由:

// app/api/agents/run/route.ts
export const maxDuration = 300; // Pro计划上5分钟
export const dynamic = 'force-dynamic';

export async function GET(request: Request) {
  // 验证cron密钥以防止未授权访问
  const authHeader = request.headers.get('authorization');
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Agent逻辑放在这里
  return Response.json({ success: true });
}

那个maxDuration?这是至关重要的。Pro上最多300秒,如果您在Enterprise水域中游泳则900秒。任何超过5分钟的常规任务?是时候分段或重新考虑您的执行模型了。

Supabase作为您的Agent的大脑

Supabase——一个强大的工具。它充当您的任务队列、状态存储、审计日志和实时更新的通知程序。这是我随着时间推移精细调整的模式:

-- Agent任务队列
CREATE TABLE agent_tasks (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  agent_type TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending'
    CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
  priority INTEGER DEFAULT 0,
  payload JSONB NOT NULL DEFAULT '{}',
  result JSONB,
  error TEXT,
  attempts INTEGER DEFAULT 0,
  max_attempts INTEGER DEFAULT 3,
  locked_until TIMESTAMPTZ,
  scheduled_for TIMESTAMPTZ DEFAULT NOW(),
  started_at TIMESTAMPTZ,
  completed_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 队列轮询查询的索引
CREATE INDEX idx_agent_tasks_queue ON agent_tasks (
  agent_type, status, priority DESC, scheduled_for
) WHERE status = 'pending';

-- Agent执行日志以用于可观测性
CREATE TABLE agent_logs (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  task_id UUID REFERENCES agent_tasks(id),
  level TEXT NOT NULL DEFAULT 'info',
  message TEXT NOT NULL,
  metadata JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Agent状态持久化(对于多步agent)
CREATE TABLE agent_state (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  agent_type TEXT NOT NULL,
  agent_instance_id TEXT NOT NULL,
  state JSONB NOT NULL DEFAULT '{}',
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(agent_type, agent_instance_id)
);

那个locked_until列?这是关键。它模拟了一个临时的分布式锁。这意味着当cron作业选择一个任务时,它将locked_until向前设置5分钟。如果进程崩溃,锁过期,另一个周期可以再试一次。

任务拾取查询

这是真正的妙处。这是您无需重新处理就能抓取任务的黄金机会:

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

async function claimTasks(agentType: string, limit: number = 5) {
  const { data: tasks, error } = await supabase
    .rpc('claim_agent_tasks', {
      p_agent_type: agentType,
      p_limit: limit,
      p_lock_duration: '5 minutes'
    });

  if (error) throw error;
  return tasks;
}

以及匹配的Postgres函数:

CREATE OR REPLACE FUNCTION claim_agent_tasks(
  p_agent_type TEXT,
  p_limit INTEGER,
  p_lock_duration INTERVAL
)
RETURNS SETOF agent_tasks AS $$
BEGIN
  RETURN QUERY
  UPDATE agent_tasks
  SET
    status = 'running',
    locked_until = NOW() + p_lock_duration,
    started_at = NOW(),
    attempts = attempts + 1,
    updated_at = NOW()
  WHERE id IN (
    SELECT id FROM agent_tasks
    WHERE agent_type = p_agent_type
      AND status = 'pending'
      AND scheduled_for <= NOW()
      AND (locked_until IS NULL OR locked_until < NOW())
      AND attempts < max_attempts
    ORDER BY priority DESC, scheduled_for ASC
    LIMIT p_limit
    FOR UPDATE SKIP LOCKED
  )
  RETURNING *;
END;
$$ LANGUAGE plpgsql;

FOR UPDATE SKIP LOCKED是这里的无名英雄。它确保没有两个并发调用互相践踏。相信我,发现和修复重复处理不是一件愉快的事。

基于Cron的Agent编排

Vercel Cron是整个操作的稳定鼓声。每15分钟,您的agent运行程序春风吹拂,抓取任务,处理它们,然后在下一次调用之前打盹。

以下是完整的agent运行程序路由的样子:

// app/api/agents/run/route.ts
import { claimTasks, completeTask, failTask, logAgent } from '@/lib/agents';
import { runContentAgent } from '@/lib/agents/content';
import { runReconciliationAgent } from '@/lib/agents/reconciliation';

export const maxDuration = 300;

const AGENT_RUNNERS: Record<string, (task: AgentTask) => Promise<any>> = {
  'content-pipeline': runContentAgent,
  'data-reconciliation': runReconciliationAgent,
};

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization');
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response('Unauthorized', { status: 401 });
  }

  const results = { processed: 0, failed: 0, skipped: 0 };

  for (const [agentType, runner] of Object.entries(AGENT_RUNNERS)) {
    const tasks = await claimTasks(agentType, 5);

    for (const task of tasks) {
      try {
        await logAgent(task.id, 'info', `Starting ${agentType} task`);
        const result = await runner(task);
        await completeTask(task.id, result);
        results.processed++;
      } catch (error) {
        const message = error instanceof Error ? error.message : 'Unknown error';
        await failTask(task.id, message);
        await logAgent(task.id, 'error', message);
        results.failed++;
      }
    }
  }

  return Response.json(results);
}

注意每个agent类型的顺序处理,每次运行最多5个任务。这有助于保持函数执行时间在控制范围内。有50个待处理任务的堆积?在几个cron周期中分散它们。没有急事,没问题。

当Cron不够时

有时生活会快速袭来,cron就不够用了。在这些情况下,我将cron与Supabase数据库Webhook或Edge Functions集成——它们由数据库更改触发:

-- 当创建高优先级任务时触发edge函数
CREATE OR REPLACE FUNCTION notify_urgent_task()
RETURNS TRIGGER AS $$
BEGIN
  IF NEW.priority > 5 THEN
    PERFORM net.http_post(
      url := 'https://your-app.vercel.app/api/agents/urgent',
      headers := jsonb_build_object(
        'Authorization', 'Bearer ' || current_setting('app.webhook_secret')
      ),
      body := jsonb_build_object('task_id', NEW.id)
    );
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

这个设置让您混合可预测的基于cron的处理与紧急任务的快速响应。

构建Agent循环

这是魔法发生的地方。使用Vercel AI SDK工具调用的示例agent实现:

// lib/agents/content.ts
import { generateText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
import { supabase } from '@/lib/supabase';

export async function runContentAgent(task: AgentTask) {
  const { sourceUrl, targetPlatform } = task.payload;

  const { text, toolCalls, usage } = await generateText({
    model: openai('gpt-4o-mini'),
    system: `You are a content processing agent. Fetch the source content,
    analyze it, and generate appropriate social media content for ${targetPlatform}.
    Use the provided tools to complete your task.`,
    prompt: `Process this source: ${sourceUrl}`,
    tools: {
      fetchContent: tool({
        description: 'Fetch content from a URL',
        parameters: z.object({ url: z.string().url() }),
        execute: async ({ url }) => {
          const res = await fetch(url);
          const html = await res.text();
          // Strip HTML, extract main content
          return extractMainContent(html);
        },
      }),
      saveContent: tool({
        description: 'Save generated content for review',
        parameters: z.object({
          title: z.string(),
          body: z.string(),
          platform: z.string(),
          suggestedPostTime: z.string(),
        }),
        execute: async (content) => {
          const { data, error } = await supabase
            .from('generated_content')
            .insert({
              ...content,
              task_id: task.id,
              status: 'pending_review',
            })
            .select()
            .single();

          if (error) throw error;
          return { saved: true, id: data.id };
        },
      }),
    },
    maxSteps: 5, // 允许最多5个工具调用轮次
  });

  return {
    summary: text,
    toolCallCount: toolCalls.length,
    tokensUsed: usage.totalTokens,
  };
}

设置maxSteps为5让LLM调用工具、评估结果并相应地处理——就像一个小工蜂在做它的事情。大多数内容任务在3–4个这样的调用中完成。

使用Supabase Realtime进行实时Agent监控

自主agent很好,但监控它们是最重要的。Supabase Realtime为仪表板提供动力,该仪表板在agent完成工作时实时更新:

// components/AgentDashboard.tsx
'use client';

import { useEffect, useState } from 'react';
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

export function AgentDashboard() {
  const [tasks, setTasks] = useState<AgentTask[]>([]);
  const [logs, setLogs] = useState<AgentLog[]>([]);

  useEffect(() => {
    // 订阅任务更改
    const taskChannel = supabase
      .channel('agent-tasks')
      .on(
        'postgres_changes',
        { event: '*', schema: 'public', table: 'agent_tasks' },
        (payload) => {
          setTasks(prev => {
            const updated = [...prev];
            const idx = updated.findIndex(t => t.id === payload.new.id);
            if (idx >= 0) updated[idx] = payload.new as AgentTask;
            else updated.unshift(payload.new as AgentTask);
            return updated.slice(0, 50);
          });
        }
      )
      .subscribe();

    // 订阅日志流
    const logChannel = supabase
      .channel('agent-logs')
      .on(
        'postgres_changes',
        { event: 'INSERT', schema: 'public', table: 'agent_logs' },
        (payload) => {
          setLogs(prev => [payload.new as AgentLog, ...prev].slice(0, 100));
        }
      )
      .subscribe();

    return () => {
      supabase.removeChannel(taskChannel);
      supabase.removeChannel(logChannel);
    };
  }, []);

  // 呈现您的仪表板UI...
}

这提供了agent活动的实时流。通常,我会添加指标、任务计数、错误率视觉效果,甚至一个"立即运行"按钮用于测试。Supabase Realtime包含在免费层中——无需额外付费。

处理故障和边界情况

为此做好准备——某些东西会爆炸。API消失不见,速率限制迎面击中您,或者服务只是发送回无意义的数据。这是我关于构建存活系统所学到的。

带指数退避的重试

我们整合的attemptsmax_attempts列处理重试。如果任务失败,它停留在"失败"状态,但稍后可以手动或自动设置为重试:

async function failTask(taskId: string, error: string) {
  const { data } = await supabase
    .from('agent_tasks')
    .select('attempts, max_attempts')
    .eq('id', taskId)
    .single();

  const shouldRetry = data && data.attempts < data.max_attempts;

  await supabase
    .from('agent_tasks')
    .update({
      status: shouldRetry ? 'pending' : 'failed',
      error,
      locked_until: null,
      // 指数退避:1分钟、4分钟、9分钟...
      scheduled_for: shouldRetry
        ? new Date(Date.now() + Math.pow(data.attempts, 2) * 60000).toISOString()
        : undefined,
      updated_at: new Date().toISOString(),
    })
    .eq('id', taskId);
}

外部API的断路器

当外部API摇摇欲坠时,立即重试快速消耗重试。在Supabase中跟踪故障率,如果情况看起来严峻,您可以停止agent:

async function checkCircuitBreaker(service: string): Promise<boolean> {
  const fiveMinutesAgo = new Date(Date.now() - 300000).toISOString();

  const { count } = await supabase
    .from('agent_logs')
    .select('*', { count: 'exact', head: true })
    .eq('level', 'error')
    .ilike('message', `%${service}%`)
    .gte('created_at', fiveMinutesAgo);

  return (count ?? 0) < 10; // 如果5分钟内超过10个错误则断开
}

成本分析和优化

这是我们谈论实际情况的地方。在2026年运行生产agent应用程序需要多少成本?

组件 免费层 典型生产 重型使用
Vercel Pro - $20/月 $20/月 + 使用量
Supabase Pro - $25/月 $25/月 + 使用量
OpenAI (GPT-4o-mini) - $30-80/月 $200+/月
Anthropic (Claude 3.5 Haiku) - $20-50/月 $150+/月
总计 ~$0 (开发) $95-175/月 $395+/月

那些LLM费用?他们掌管全局。以下是如何控制它们:

  1. 在可能的地方使用较小的模型。 GPT-4o-mini和Claude 3.5 Haiku以其较大同类产品的一小部分成本处理大多数任务。
  2. 在达到API之前缓存。 Supabase可以存储LLM响应,减少新API调用。
  3. 批处理、批处理、批处理。 而不是每个任务一个API调用,批处理相似的任务并将它们一起发送。
  4. 令牌限制?是的,请。 在您的AI SDK调用期间始终设置maxTokens——防止肆无忌惮的生成。

生产部署模式

一旦您准备好部署,有几件事是至关重要的:

环境分离。 利用Supabase分支(需要Pro)为暂存和生产维护离散数据库。将Vercel预览部署与Supabase分支配对以完全隔离。

监控。 Vercel的可观测性提供cron执行历史和函数日志。将其与Supabase健康检查配对:

SELECT
  agent_type,
  status,
  COUNT(*) as count,
  AVG(EXTRACT(EPOCH FROM (completed_at - started_at))) as avg_duration_seconds
FROM agent_tasks
WHERE created_at > NOW() - INTERVAL '24 hours'
GROUP BY agent_type, status
ORDER BY agent_type, status;

秘密管理。 使用Vercel环境变量保护您的API密钥,永远不要对其进行硬编码。仅在服务器端使用Supabase的服务角色密钥,在客户端保持匿名密钥。

如果您需要帮助设置像这样的系统,我们的团队使用完全相同的堆栈交付了多个agent驱动的应用程序。查看我们的headless CMS开发Next.js开发功能,或联系我们讨论您的项目。

常见问题

Vercel Cron能处理高频agent任务吗?
Vercel Cron可以管理Pro计划上的最短1分钟间隔。大多数agent任务适合5-15分钟周期。需要更快?考虑Supabase数据库Webhook或切换到Inngest用于事件驱动操作。

我如何防止使用cron重复任务处理?
这是PostgreSQL的FOR UPDATE SKIP LOCKED在您的任务声明查询中大放光彩的地方。即使cron计时重叠,它也是您的安全网,确保每个任务都是唯一声明的。locked_until如果执行中灾难发生,增加一层安全。

Supabase对生产agent工作负载可靠吗?
绝对的。Supabase Pro在专用Postgres实例上运行,具有自动备份和99.9% SLA的正常运行时间。实际上,对于这个任务队列系统,Postgres是完全合格的——能够轻松处理并发访问和复杂查询。

在生产环境中运行AI agent需要花费多少?
通常,每天处理5,000-10,000个任务将花费$95-175/月(Vercel Pro + Supabase Pro)加上$30-80/月用于LLM API调用,使用GPT-4o-mini等模型。变量层是您的LLM成本——取决于任务复杂性和令牌使用。

我应该对agent逻辑使用LangChain还是Vercel AI SDK?
到2026年,Vercel的AI SDK(v4.x)原生涵盖大多数agent需求——工具使用、多步推理、结构化输出和流。LangChain在您深入高级思维链模式、RAG设置或专门工具集成时变得有用。对于这个堆栈,AI SDK的更少复杂性统治至尊。

我可以使用Astro而不是Next.js来实现这个模式吗?
Astro可以处理cron触发的API路由,但您会错过Vercel的优化,包括cron配置和更长的超时。如果您的仪表板喜欢内容密集、低交互性,Astro适合前端。对于您的agent后端坚持Next.js。

我如何监控agent性能和故障?
混合三个层:Vercel的日志用于执行级洞察,Supabase中的agent_logs表用于应用程序日志跟踪,Supabase Realtime用于动态仪表板监控。为故障、执行时间和队列长度建立健康检查。Vercel无缝与PagerDuty和Slack等警报服务同步。

当我的agent任务超过Vercel的函数超时时会发生什么?
在Vercel Pro上,您有300秒的上限。如果任务经常超过这个,考虑在cron周期中将它们分解成小任务。例如,将内容过程分为"获取"、"分析"和"生成"任务。或者,探索Supabase Edge Functions以进行扩展执行——它们有150秒的限制,可以根据需要触发。