Vibe Coding 一键部署——火山引擎推出 Supabase,驱动 Agent 应用快速上线

点击上方👆蓝字关注我们!

picture.image

Agent 开发者几乎都会遇到这样的场景:脑海中智能交互的蓝图清晰无比,动手时却被繁杂的后端基建卡住——搭建服务架构、设计数据库表结构、配置认证体系、编写海量 API……好不容易让应用上线,新的问题又接踵而至:用户量未起时,闲置服务器空耗成本;用量起来后,流量洪峰又易引发服务崩溃。

在构建 AI Agent 的浪潮中,是否有一种方案,能快速扫清后端障碍,让创意一键落地?

为此,火山引擎推出火山 Supabase ——一个让开发者能专注业务逻辑的**“云上开发工具箱”** ,旨在让开发者摆脱后端基建的束缚,实现高效的 Vibe Coding,并聚焦于 Agent 交互逻辑与体验创新。

Supabase,作为一款广受欢迎的开源 BaaS(Backend as a Service,后端即服务),将数据库、用户认证、文件存储、实时通信等所有通用后端能力,打包成即取即用的标准化服务。

Vercel 创始人,Next.js 之父 Guillermo Rauch 称 Supabase 是 PostgreSQL 的最佳 BaaS 实现,为 Vibe Coding 提供了坚实的“道路”和数据基础 ,是开发者构建应用时不可或缺的后端平台。

火山 Supabase 100% 兼容开源 Supabase 生态,保障开源开发者能向云上无缝迁移,更在成本控制、数据安全、开发效率 三大核心维度实现突破性升级,让开发者摆脱基建内耗,聚焦核心业务创新与价值落地。目前火山 Supabase 已服务于字节内外客户,如豆包、扣子、TRAE 等典型的 AI Agent 平台

助力 Agent 开发提效 80%,资源利用率大幅提升

开源 Supabase 凭借其灵活性和与 PostgreSQL 生态的深度兼容,在 GitHub 上斩获了近 10 万 Star,深受全球开发者青睐。

而火山 Supabase 在保留开源优势的基础上,注入了字节跳动大规模业务场景锤炼出的企业级服务能力,为开发者带来了更极致的性价比与稳定性。

  • 全链路 Serverless 架构,实现弹性与成本双赢。 传统 Serverless 架构通常仅覆盖计算层,数据库仍需固定配置,成本控制依然是核心挑战。火山 Supabase 创新性地将 Serverless 理念贯穿至数据库层,实现了 BaaS 服务层到数据库层的全链路弹性。业务低谷时,资源可自动休眠,费用趋近于零;流量波峰时,系统则能秒级自动扩容,无需人工干预。经实际生产业务验证,与自建开源 Supabase 相比,火山 Supabase 最高可节省 23 倍的成本。
  • 企业级数据安全体系,筑牢业务资产防线。 依托字节跳动海量业务验证的“数据保险箱”方案,火山 Supabase 提供了核心的 Data as Git 能力,让数据管理如代码版本控制般便捷。企业可创建独立的数据分支,用于新功能测试、AI 模型训练,全程不影响生产环境。当遇到误操作导致的数据损坏时,通过时间点回溯功能,可瞬间恢复到任意时间点的数据状态。结合金融级高可用底座与存算分离架构,即便面对电商大促、金融交易等业务规模的激增,也能确保数据不丢失、服务不中断。
  • AI 原生极简交互,开发效率提升 80%+。 火山 Supabase 重构了开发者的交互流程。首先,开发者可以通过自然语言下达指令,例如“创建一个用于测试环境的 Supabase 实例”,系统便会自动完成所有配置,传统开发模式下需要十余个步骤的繁琐操作,如今仅需 2-3 步即可完成。其次,基于火山 Supabase 的 RLS 和 标准化 REST API,开发者无需再开发后端服务,仅需开发前端代码即可完成 AI Agent 等应用的制作,大幅降低了开发、调试、部署的成本。

以扣子编程为例,在每个 Vibe Coding 生成物(网页应用、移动应用、小程序、智能体、技能等)背后,都会同步创建和使用一个 Supabase 实例运行这些产物,并且使用火山 Supabase 的多分支能力(Branching)进行环境隔离。

picture.image

在使用火山 Supabase 后,扣子编程整体的交付效率、质量、成本,都得到极大的改善。以交付效率为例,基于火山 Supabase,扣子编程在 Vibe Coding 场景,提供了以下功能:

  • 开箱即用的云端环境 :打开网页即可开始,每个项目自动分配云端运行环境和对应资源,无需安装任何工具。遇到问题也能一键重启、自动恢复。
  • 一键部署和项目构建服务 :一键完成云端打包、构建与服务部署,支持自定义域名。版本可回退,部署记录完善、可追踪。

这极大降低了 Agent 开发的门槛,让开发者一站式完成从构建到落地的完整生产级闭环,让整个开发过程更聚焦于创意和价值创造。

picture.image

站在 PostgreSQL 肩膀上的 Agent 全栈开发平台

火山 Supabase 的强大之处,在于它巧妙地将一系列成熟的开源组件,围绕 PostgreSQL 这颗强大的“心脏”进行整合与封装,为开发者提供了一套连贯、高效的工具集。下面,我们深入其核心组件,探究其技术原理。

核心架构:以 PostgreSQL 为中心的全栈协同

火山 Supabase 的架构并非重新发明轮子,而是“站在巨人的肩膀上”。它以功能强大且极度可靠的 PostgreSQL 数据库为中心,集成了一系列专注于特定领域的服务,每个服务都力求做到小而精,并通过良好定义的接口协同工作。

  • PostgreSQL:不止开源 PostgreSQL,火山团队为更好服务 Agent 场景,全新打造 Serverless PostgreSQL,提供了一系列前所未有的体验——秒级弹性、资源自动休眠、秒级 Branch 管理、Timetravel 灵活查询历史数据等。
  • PostgREST:一个神奇的工具,它能将你的 PostgreSQL 数据库瞬间转换成一个 RESTful API。你无需编写一行后端代码,数据库中的表、视图、函数,都会自动拥有对应的 API 端点。
  • GoTrue:一个基于 JWT (JSON Web Tokens) 的身份验证服务,负责用户注册、登录和管理。它与 PostgreSQL 的行级安全(Row Level Security, RLS)机制深度集成,为数据访问提供了精细的权限控制。
  • Realtime:一个基于 Elixir 构建的高性能 WebSocket 服务。它通过监听 PostgreSQL 的预写日志(Write-Ahead Log, WAL),能够实时地将数据库的变更推送给客户端,是构建实时协作应用的关键。
  • Storage:提供对象存储服务,用于管理图片、视频等大文件。其元数据存储在 Postgres 中,权限管理同样与 GoTrue 和 RLS 集成打通。
  • Edge Functions:分布式 Serverless 函数。开发者可以编写 TypeScript、Python 函数,按需执行,可用于处理 Webhooks、安全地调用第三方 API 或执行自定义的后端逻辑。

数据流视角下的协同工作

1、用户请求:一个前端应用通过 supabase-js 客户端库发起请求。

2、API 网关 (Kong):所有请求首先经过 API 网关,它负责路由、认证和负载均衡。

3、身份验证 (GoTrue):如果是需要认证的请求,网关会携带 JWT 令牌与 GoTrue 通信,验证用户身份。

4、API 服务 (PostgREST):验证通过后,请求被转发到 PostgREST。PostgREST 将 HTTP 请求(如 GET /tasks)解析成 SQL 查询。

5、数据库权限 (PostgreSQL RLS):PostgreSQL 在执行 SQL 前,会检查为当前用户角色(从 JWT 中解析)设定的 RLS 策略。例如,一个 RLS 策略可以规定“用户只能访问 tasks 表中 user\_id 等于自己 ID 的行”。

6、数据响应:数据库返回符合权限的数据,经由 PostgREST 和 API 网关,最终到达客户端。

7、实时更新 (Realtime):如果某个操作(如 INSERTUPDATE)修改了数据库,Realtime 服务会监听到 WAL 的变化,并将变更通过 WebSocket 推送给所有订阅了该数据的客户端,实现界面自动更新。

8、服务端逻辑 (Edge Functions):如果需要执行复杂或敏感的操作(如调用外部支付接口),客户端会调用一个 Edge Function。该函数在沙箱环境中运行,可以使用 service\_role\_key 安全地与数据库或其他服务交互,并将结果返回给客户端。


picture.image

火山 Supabase 一站式 AI 基建能力供给

技术实操:将火山 Supabase 集成到 Agent 开发环境中,开启 Vibe Coding

将火山 Supabase 集成到开发者的 Agent 开发环境中,可以极大地加速 Agent 和全栈应用的后端构建。下面我们将通过两个具体的 Agent 开发场景——“任务管理与记忆”、“RAG 功能”,展示如何实现集成。

基于火山的 Supabase 的 Agent 的数据链路示意图如下:

picture.image

1. 环境配置:连接你的 Supabase 项目

在项目环境中,第一步是安全地配置 Supabase 的连接凭证。我们推荐使用环境变量来管理这些敏感信息。

在你的项目设置或 .env 文件中,添加以下两个变量:

  
SUPABASE\_URL="YOUR\_SUPABASE\_PROJECT\_URL"#例如br123123123.supabase.aidap-global.cn-beijing.volces.com:443  
SUPABASE\_ANON\_KEY="YOUR\_SUPABASE\_ANON\_KEY"  

  • SUPABASE\_URLSUPABASE\_ANON\_KEY 可以在火山 Supabase 控制台的“连接”页面中获取到。ANON\_KEY 是公开的匿名密钥,它受到 RLS 策略的保护,可以安全地在前端环境使用它。

2. 初始化 supabase-js 客户端

在项目中(无论是 Node.js 后端还是前端代码),首先需要安装 supabase-js 客户端库:

  
npm install @supabase/supabase-js  

然后,创建一个客户端实例。通常,我们会将这个实例放在一个单独的文件中(例如 lib/supabaseClient.js),以便在整个项目中复用。

  
// lib/supabaseClient.js  
import { createClient } from '@supabase/supabase-js'  
  
const supabaseUrl = process.env.SUPABASE\_URL  
const supabaseAnonKey = process.env.SUPABASE\_ANON\_KEY  
  
export const supabase = createClient(supabaseUrl, supabaseAnonKey)  

3. 示例 1:为 Agent 实现用户任务管理与记忆

假设我们正在构建一个 Agent,它需要记录用户的任务,并能根据历史任务提供建议。

(1) 创建相关表结构 及 RLS策略

首先,在火山 Supabase 的 Dashboard 中 SQL Editor 里创建 tasks 表,并为 Agent 的“记忆”创建一个 memories 表,后者将包含向量数据。

  
-- 启用 pgvector 扩展  
CREATE EXTENSION IF NOT EXISTS vector;  
  
-- 创建任务表  
CREATE TABLE public.tasks (  
  id          uuid primary key default gen\_random\_uuid(),  
  user\_id     uuid references auth.users not null,  
  title       text not null,  
  is\_complete boolean default false,  
  created\_at  timestamptz default now()  
);  
  
-- 为任务表启用行级安全 (RLS)  
ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;  
  
-- 策略:用户只能看到和操作自己的任务  
CREATE POLICY "Users can manage their own tasks."  
ON public.tasks  
FOR ALL  
USING (auth.uid() = user\_id)  
WITH CHECK (auth.uid() = user\_id);  
  
-- 创建记忆表,用于存储向量化后的任务信息  
CREATE TABLE public.memories (  
  id            uuid primary key default gen\_random\_uuid(),  
  task\_id       uuid references public.tasks on delete cascade,  
  user\_id       uuid references auth.users not null,  
  content       text not null, -- 任务标题或描述  
  embedding     vector(384),   -- 假设使用 384 维的 embedding 模型  
  created\_at    timestamptz default now()  
);  
  
-- 为记忆表启用 RLS  
ALTER TABLE public.memories ENABLE ROW LEVEL SECURITY;  
  
-- 策略:用户只能访问自己的记忆  
CREATE POLICY "Users can access their own memories."  
ON public.memories  
FOR ALL  
USING (auth.uid() = user\_id)  
WITH CHECK (auth.uid() = user\_id);  

(2) 创建向量检索 Function

创建一个 PG Function 来进行向量相似度检索。

  
-- 创建一个函数来匹配相似的记忆  
CREATE OR REPLACE FUNCTION match\_memories(  
  query\_embedding vector(384),  
  match\_threshold float,  
  match\_count int  
)  
RETURNS TABLE (id uuid, content text, similarity float)  
LANGUAGE sql STABLE AS $$  
  SELECT  
    memories.id,  
    memories.content,  
    1 - (memories.embedding <=> query\_embedding) as similarity  
  FROM memories  
  WHERE auth.uid() = memories.user\_id -- 确保只在当前用户记忆中搜索  
    AND 1 - (memories.embedding <=> query\_embedding) > match\_threshold  
  ORDER BY similarity DESC  
  LIMIT match\_count;  
$$;  

(3) 实现基本的 CRUD 操作

现在,在代码中,我们可以使用 supabase-js 与这些表进行交互。

  
import { supabase } from './lib/supabaseClient.js';  
  
// 获取当前用户的任务列表  
async functiongetTasks() {  
  const { data: tasks, error } = await supabase  
    .from('tasks')  
    .select('*')  
    .order('created\_at', { ascending: false });  
  
if (error) {  
    console.error('Error fetching tasks:', error);  
    return;  
  }  
  console.log('Tasks:', tasks);  
}  
  
// 创建一个新任务  
async function createTask(title) {  
  const { data: { user } } = await supabase.auth.getUser(); // 获取当前登录用户  
if (!user) {  
    console.error('User not logged in');  
    return;  
  }  
  
  const { data: newTask, error } = await supabase  
    .from('tasks')  
    .insert({ title: title, user\_id: user.id })  
    .select()  
    .single();  
  
if (error) {  
    console.error('Error creating task:', error);  
  } else {  
    console.log('New task created:', newTask);  
    // 异步生成并存储记忆向量 (具体实现在 Edge Function 的 generate-memory 函数中,见下节)  
    await supabase.functions.invoke('generate-memory', {  
      body: { task\_id: newTask.id, content: newTask.title },  
    });  
  }  
}  

(4) 使用 Edge Function 实现记忆信息的 Embedding 并入库

使用 Edge Function 进行记忆信息的 Embedding 并入库,即上节提及的“generate-memory”的具体实现。

相比于直接在客户端执行 Embedding 并入库,在 Edge Function 中会更安全,能避免暴露实现细节。

  
// supabase/functions/generate-memory/index.ts  
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';  
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.6.0';  
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';  
  
serve(async (req) => {  
  const { task\_id, content } = await req.json();  
  
  // 使用 service\_role key 安全地初始化 Admin 客户端  
  const supabaseAdmin = createClient(  
    Deno.env.get('SUPABASE\_URL') ,  
    Deno.env.get('SUPABASE\_SERVICE\_ROLE\_KEY')   
  );  
  
  // 1. 生成 Embedding  
  const generateEmbedding = await pipeline('feature-extraction', 'Supabase/gte-small');  
  const output = await generateEmbedding(content, { pooling: 'mean', normalize: true });  
  const embedding = Array.from(output.data);  
  
  // 2. 获取任务信息,以拿到 user\_id  
  const { data: task, error: taskError } = await supabaseAdmin  
    .from('tasks')  
    .select('user\_id')  
    .eq('id', task\_id)  
    .single();  
  
if (taskError) {  
    return new Response(JSON.stringify({ error: taskError.message }), { status: 500 });  
  }  
  
  // 3. 将向量存入 memories 表  
  const { error: insertError } = await supabaseAdmin.from('memories').insert({  
    task\_id,  
    user\_id: task.user\_id,  
    content,  
    embedding,  
  });  
  
if (insertError) {  
    return new Response(JSON.stringify({ error: insertError.message }), { status: 500 });  
  }  
  
return new Response(JSON.stringify({ message: 'Memory created' }), { status: 200 });  
});  

(5) 在 APP 前端 或 Edge Function 中检索记忆信息

在 APP 前端 或 Edge Function 中,调用以下函数来检索进行记忆信息。

  
async function findSimilarMemories(text) {  
  // 1. 将当前文本生成 embedding (此处省略)  
  const queryEmbedding = await generateEmbeddingFor(text);   
  
  // 2. 调用 RPC 函数进行搜索  
  const { data: memories, error } = await supabase.rpc('match\_memories', {  
    query\_embedding: queryEmbedding,  
    match\_threshold: 0.7, // 相似度阈值  
    match\_count: 5,       // 返回最多 5 个结果  
  });  
  
if (error) {  
    console.error('Error searching memories:', error);  
    return [];  
  }  
  
  console.log('Found similar memories:', memories);  
return memories;  
}  

(6) 使用 Edge Function 中访问 大模型 (或 MCP、外部工具等)

Edge Functions 可以用于承载 AI Agent 的核心逻辑,如调用 LLM、MCP Tools、执行工具)的执行。

  
// supabase/functions/agent-core/index.ts  
import { serve } from "https://deno.land/std@0.192.0/http/server.ts";  
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";  
  
serve(async (req) => {  
  const supabase = createClient(  
    Deno.env.get("SUPABASE\_URL")!,  
    Deno.env.get("SUPABASE\_ANON\_KEY")!  
  );  
  
  // 1. 解析用户请求(用户输入、Agent ID)  
  const { user\_id, query, agent\_id } = await req.json();  
  
  // 2. 检索长期记忆(结构化+非结构化)  
  const user\_prefs = await supabase  
    .from("user\_profiles")  
    .select("*")  
    .eq("user\_id", user\_id)  
    .single();  
  
  // 3. 调用 LLM(比如 OpenAI)生成 Agent 响应  
  const llmResponse = await fetch("https://api.openai.com/v1/chat/completions", {  
    method: "POST",  
    headers: {  
      "Content-Type": "application/json",  
      "Authorization": `Bearer ${Deno.env.get("OPENAI\_API\_KEY")}`  
    },  
    body: JSON.stringify({  
      model: "gpt-3.5-turbo",  
      messages: [  
        { role: "system", content: `你是一个基于 Supabase 的 AI Agent,用户偏好:${JSON.stringify(user\_prefs.data)}` },  
        { role: "user", content: query }  
      ]  
    })  
  });  
  
  const llmData = await llmResponse.json();  
  const agent\_answer = llmData.choices[0].message.content;  
  
  // 4. 保存本次交互到长期记忆  
  await supabase.table("agent\_sessions").upsert({  
    user\_id,  
    agent\_id,  
    query: query,  
    response: agent\_answer,  
    updated\_at: new Date()  
  });  
  
  // 5. 返回 Agent 响应  
return new Response(JSON.stringify({ answer: agent\_answer }), {  
    headers: { "Content-Type": "application/json" }  
  });  
});  

部署该函数后,客户端调用 supabase.functions.invoke('generate-memory', ...) 即可触发服务端的向量生成与存储,整个过程对前端透明且安全。

4. 示例 2:为 Agent 实现 RAG 功能

(1) 创建相关表结构及 RLS 策略

创建两个表:一个存储原始文档分块,一个存储向量(也可合并,分开更易管理):

  
-- 启用 pgvector 扩展  
CREATE EXTENSION IF NOT EXISTS vector;  
  
-- 文档元数据表:存储原始文档信息  
CREATE TABLE IF NOT EXISTS rag\_documents (  
    id UUID PRIMARY KEY DEFAULT uuid\_generate\_v4(),  
    document\_name TEXT NOT NULL,  -- 文档名称  
    document\_type TEXT,           -- 文档类型(pdf/txt/md 等)  
    user\_id UUID,                 -- 所属用户(关联 auth.users)  
    created\_at TIMESTAMP DEFAULT NOW()  
);  
  
-- 文档分块&向量表:存储分块文本和对应的 Embedding 向量  
CREATE TABLE IF NOT EXISTS rag\_document\_chunks (  
    id UUID PRIMARY KEY DEFAULT uuid\_generate\_v4(),  
    document\_id UUID REFERENCES rag\_documents(id) ON DELETE CASCADE,  -- 关联文档  
    chunk\_text TEXT NOT NULL,     -- 文档分块文本  
    chunk\_index INT,              -- 分块序号(保持上下文顺序)  
    embedding vector(1536),       -- OpenAI Embedding 向量(维度 1536,可根据模型调整)  
    created\_at TIMESTAMP DEFAULT NOW()  
);  
  
-- 创建向量索引:加速相似度检索(核心!否则大数据量检索慢)  
CREATE INDEX IF NOT EXISTS idx\_rag\_embeddings   
ON rag\_document\_chunks   
USING ivfflat (embedding vector\_cosine\_ops)  -- 余弦相似度  
WITH (lists = 100);  -- lists 数值:数据量小设 10-100,大数据量设 1000+  
  
-- (可选)启用 RLS 规则,确保用户只能访问自己的文档  
ALTER TABLE rag\_documents ENABLE ROW LEVEL SECURITY;  
ALTER TABLE rag\_document\_chunks ENABLE ROW LEVEL SECURITY;  
  
CREATE POLICY "Users can only access their own documents"  
ON rag\_documents FOR ALL USING (auth.uid() = user\_id);  
  
CREATE POLICY "Users can only access their own chunks"  
ON rag\_document\_chunks FOR ALL   
USING (  
    EXISTS (  
        SELECT 1 FROM rag\_documents   
        WHERE rag\_documents.id = rag\_document\_chunks.document\_id   
        AND rag\_documents.user\_id = auth.uid()  
    )  
);  

(2) 预处理文档:Embedding 并入库

  
import os  
import uuid  
from dotenv import load\_dotenv  
from supabase import create\_client, Client  
from openai import OpenAI  
from PyPDF2 import PdfReader  
  
# 加载环境变量  
load\_dotenv()  
SUPABASE\_URL = os.getenv("SUPABASE\_URL")  
SUPABASE\_KEY = os.getenv("SUPABASE\_ANON\_KEY")  
OPENAI\_API\_KEY = os.getenv("OPENAI\_API\_KEY")  
  
# 初始化客户端  
supabase: Client = create\_client(SUPABASE\_URL, SUPABASE\_KEY)  
openai\_client = OpenAI(api\_key=OPENAI\_API\_KEY)  
  
# 1. 文档分块工具(通用:支持文本/PDF)  
def split\_document(file\_path, chunk\_size=500, chunk\_overlap=50):  
    """  
    拆分文档为小分块,避免向量长度超限,同时保留上下文重叠  
    """  
    chunks = []  
    # 处理 PDF  
    if file\_path.endswith(".pdf"):  
        reader = PdfReader(file\_path)  
        text = ""  
        for page in reader.pages:  
            text += page.extract\_text() or ""  
    # 处理纯文本  
    else:  
        with open(file\_path, "r", encoding="utf-8") as f:  
            text = f.read()  
      
    # 分块逻辑  
    start = 0  
    while start < len(text):  
        end = start + chunk\_size  
        chunk = text[start:end].strip()  
        if chunk:  
            chunks.append(chunk)  
        start = end - chunk\_overlap  # 重叠部分,保持上下文连贯  
    return chunks  
  
# 2. 生成 Embedding 向量  
def get\_embedding(text):  
    """调用 OpenAI Embedding 模型生成向量"""  
    response = openai\_client.embeddings.create(  
        input=text,  
        model="text-embedding-3-small"# 轻量、低成本,维度 1536  
    )  
    return response.data[0].embedding  
  
# 3. 文档入库主函数  
def ingest\_document(file\_path, user\_id):  
    # 步骤 1:拆分文档  
    chunks = split\_document(file\_path)  
    if not chunks:  
        raise ValueError("文档内容为空")  
      
    # 步骤 2:创建文档元数据  
    document\_name = os.path.basename(file\_path)  
    document\_type = file\_path.split(".")[-1]  
    doc\_response = supabase.table("rag\_documents").insert({  
        "document\_name": document\_name,  
        "document\_type": document\_type,  
        "user\_id": user\_id  
    }).execute()  
    document\_id = doc\_response.data[0]["id"]  
      
    # 步骤 3:生成向量并入库  
    chunk\_records = []  
    for idx, chunk in enumerate(chunks):  
        embedding = get\_embedding(chunk)  
        chunk\_records.append({  
            "document\_id": document\_id,  
            "chunk\_text": chunk,  
            "chunk\_index": idx,  
            "embedding": embedding  
        })  
      
    # 批量插入(提升效率)  
    supabase.table("rag\_document\_chunks").insert(chunk\_records).execute()  
    print(f"文档 {document\_name} 入库完成,共拆分 {len(chunks)} 个分块")  
    return document\_id  
  
# 测试:上传一个 PDF 文档  
if \_\_name\_\_ == "\_\_main\_\_":  
    # 替换为你的文件路径和用户 ID(Supabase Auth 的 user\_id)  
    ingest\_document("test\_document.pdf", "your-user-uuid-here")  

(3) 使用 Edge Function 实现 RAG 功能

  
// supabase/functions/rag-retrieve/index.ts  
import { serve } from "https://deno.land/std@0.192.0/http/server.ts";  
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";  
import OpenAI from "https://esm.sh/openai@4";  
  
serve(async (req) => {  
  // 解析请求参数  
  const { query, user\_id } = await req.json();  
if (!query || !user\_id) {  
    return new Response(JSON.stringify({ error: "缺少参数" }), { status: 400 });  
  }  
  
  // 初始化客户端  
  const supabase = createClient(  
    Deno.env.get("SUPABASE\_URL")!,  
    Deno.env.get("SUPABASE\_ANON\_KEY")!  
  );  
  const openai = new OpenAI({ apiKey: Deno.env.get("OPENAI\_API\_KEY")! });  
  
  // 1. 生成问题向量  
  const embeddingResponse = await openai.embeddings.create({  
    input: query,  
    model: "text-embedding-3-small",  
  });  
  const queryEmbedding = embeddingResponse.data[0].embedding;  
  
  // 2. 向量检索  
  const { data: chunks } = await supabase.raw(`  
    SELECT rc.chunk\_text, 1 - (rc.embedding <=> $1) as similarity  
    FROM rag\_document\_chunks rc  
    JOIN rag\_documents rd ON rc.document\_id = rd.id  
    WHERE rd.user\_id = $2  
    ORDER BY similarity DESC  
    LIMIT 5;  
  `, [queryEmbedding, user\_id]);  
  
  // 3. 生成回答  
  const context = chunks.map((c: any) => c.chunk\_text).join("\n\n");  
  const llmResponse = await openai.chat.completions.create({  
    model: "gpt-3.5-turbo",  
    messages: [  
      { role: "system", content: "基于上下文回答问题,不要编造内容" },  
      { role: "user", content: `上下文:${context}\n问题:${query}` }  
    ]  
  });  
  
return new Response(JSON.stringify({  
    answer: llmResponse.choices[0].message.content,  
    context: context  
  }), {  
    headers: { "Content-Type": "application/json" }  
  });  
});  

5. 示例 3:利用 Realtime 实现实时状态同步

Agent 需要实时交互(比如多用户协作 Agent、实时任务进度更新),Realtime 可以实现实时主动地推送实时消息。

在 APP 前端代码(例如 React 组件)中,可以这样订阅 tasks 表的变更:

  
import { useEffect, useState } from 'react';  
import { supabase } from './lib/supabaseClient';  
  
functionRealtimeTasks() {  
  const [tasks, setTasks] = useState([]);  
  
  useEffect(() => {  
    // 初始加载任务  
    const fetchInitialTasks = async () => {  
      const { data } = await supabase.from('tasks').select('*');  
      setTasks(data || []);  
    };  
    fetchInitialTasks();  
  
    // 订阅变更  
    const channel = supabase  
      .channel('public:tasks')  
      .on(  
        'postgres\_changes',  
        { event: '*', schema: 'public', table: 'tasks' },  
        (payload) => {  
          console.log('Change received!', payload);  
          // 简单地重新拉取数据以更新 UI  
          // 更优化的方式是根据 payload.eventType 来增量更新 state  
          fetchInitialTasks();   
        }  
      )  
      .subscribe();  
  
    // 清理订阅  
    return () => {  
      supabase.removeChannel(channel);  
    };  
  }, []);  
  
return (  
    - {task.title}  
  );  
}  

这样,任何用户创建、更新或删除任务,所有打开该页面的客户端都会几乎瞬间收到通知并更新 UI,无需手动刷新。

6. 高级用法示例:Branch 多分支/多环境管理

在前文我们提到火山 Supabase 的 Data as Git 能力。站在工程视角,Branch 就是一条独立的数据时间线:你可以为每个环境(Dev / Staging / Prod),甚至为重要客户或一次数据库大改,创建各自的分支,在分支上做结构调整、压测与演练,不会污染生产主线。火山 Supabase 为整套 BaaS 服务,包括 Database、Auth、Storage、Edge Functions 都提供了分支能力,并且创建分支时可以选择“最新数据”或者“空”等不同策略。

在项目中,其开发和生产环境使用同一个 Supabase Workspace,但 Branch 完全独立。整套机制原理如下:

  1. 开发分支开发:在 Dev Branch 上调整 Schema、调试 RLS、验证 Agent 逻辑,并用 Realtime、pgvector 等能力跑通端到端链路。
  2. 预发分支验证:将 Dev 上稳定的 Schema 迁移到 Staging Branch,使用部分真实数据或脱敏数据进行灰度验证。
  3. 生产分支发布:仅将 Schema 变更从 Staging 合入 Prod Branch,不带任何测试数据,把风险锁在前两级环境。
  4. 开发环境 PITR 恢复:当某次尝试在开发环境“玩砸”时,可以利用火山 Supabase 的 PITR 恢复能力,将开发分支回滚到指定时间。

在 Branch 机制之上,前文提到的全链路 Serverless 弹性、数据库即 API(PostgREST)、RLS 多租户隔离、Realtime 实时推送与 pgvector 向量检索 会以“整套能力”挂载在每一个分支之上。

这意味着:你可以在 Dev / Staging 分支上放心压测 Realtime 推送、调参数、改 Schema,再把稳定的变更以“仅同步 Schema”的方式迁移到 Prod 分支,为 Agent、全栈应用和多人协作场景提供一个安全可控的迭代闭环。


给 Agent 开发者的快速起步清单

通过这些步骤,我们为 Agent 构建了一个功能完备、安全且实时的后端,而这一切几乎没有编写传统的后端服务代码。那么,准备好在你的下一个 Agent 或全栈项目中使用火山 Supabase 了吗?

这里有一份快速上手清单:

  1. 访问火山引擎官网 :搜索“火山 Supabase”并创建一个新项目。
  2. 获取 API 凭证 :在控制台的“连接”页面中,找到你的 Project URLanon\_key
  3. 安装 supabase-js:在你的前端或 Node.js 项目中运行 npm install @supabase/supabase-js
  4. 初始化客户端 :使用获取的凭证创建 Supabase 客户端实例。
  5. 设计数据表 :使用仪表盘的 Table Editor 或 SQL Editor 创建你的表结构。
  6. 开启 RLS :为你需要保护的每一张表启用行级安全,并至少为授权用户添加一条 SELECT 策略,否则 API 将无法读取任何数据。
  7. 开始编码 :利用 select, insert, rpc 等方法,尽情享受无需后端编码的全栈开发体验吧!
结语:火山 Supabase,释放 Vibe Coding 的高效创造力

技术的终极价值在于降低创新的门槛,火山引擎 Supabase 融合了开源的灵活性与字节跳动的企业级技术可靠性,将复杂的后端能力转化为普惠的工具,助力开发者在诸如扣子、豆包、TRAE 这样高效的 Vibe Coding 中,跳出基建内耗的循环。无论是初创团队快速验证 MVP,还是大型企业构建稳定业务,都能以更低的成本、更高的效率和更强的安全保障,让每一个好想法,一键变为现实。

目前,火山 Supabase 已正式上线火山引擎官网。点击“阅读原文”欢迎试用产品或查阅技术文档,开启你的高效 Vibe Coding 之旅。

0
0
0
0
评论
未登录
暂无评论