点击上方👆蓝字关注我们!
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 平台 。
开源 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)进行环境隔离。
在使用火山 Supabase 后,扣子编程整体的交付效率、质量、成本,都得到极大的改善。以交付效率为例,基于火山 Supabase,扣子编程在 Vibe Coding 场景,提供了以下功能:
- 开箱即用的云端环境 :打开网页即可开始,每个项目自动分配云端运行环境和对应资源,无需安装任何工具。遇到问题也能一键重启、自动恢复。
- 一键部署和项目构建服务 :一键完成云端打包、构建与服务部署,支持自定义域名。版本可回退,部署记录完善、可追踪。
这极大降低了 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):如果某个操作(如 INSERT 或 UPDATE)修改了数据库,Realtime 服务会监听到 WAL 的变化,并将变更通过 WebSocket 推送给所有订阅了该数据的客户端,实现界面自动更新。
8、服务端逻辑 (Edge Functions):如果需要执行复杂或敏感的操作(如调用外部支付接口),客户端会调用一个 Edge Function。该函数在沙箱环境中运行,可以使用 service\_role\_key 安全地与数据库或其他服务交互,并将结果返回给客户端。
火山 Supabase 一站式 AI 基建能力供给
将火山 Supabase 集成到开发者的 Agent 开发环境中,可以极大地加速 Agent 和全栈应用的后端构建。下面我们将通过两个具体的 Agent 开发场景——“任务管理与记忆”、“RAG 功能”,展示如何实现集成。
基于火山的 Supabase 的 Agent 的数据链路示意图如下:
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\_URL和SUPABASE\_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 完全独立。整套机制原理如下:
- 开发分支开发:在 Dev Branch 上调整 Schema、调试 RLS、验证 Agent 逻辑,并用 Realtime、pgvector 等能力跑通端到端链路。
- 预发分支验证:将 Dev 上稳定的 Schema 迁移到 Staging Branch,使用部分真实数据或脱敏数据进行灰度验证。
- 生产分支发布:仅将 Schema 变更从 Staging 合入 Prod Branch,不带任何测试数据,把风险锁在前两级环境。
- 开发环境 PITR 恢复:当某次尝试在开发环境“玩砸”时,可以利用火山 Supabase 的 PITR 恢复能力,将开发分支回滚到指定时间。
在 Branch 机制之上,前文提到的全链路 Serverless 弹性、数据库即 API(PostgREST)、RLS 多租户隔离、Realtime 实时推送与 pgvector 向量检索 会以“整套能力”挂载在每一个分支之上。
这意味着:你可以在 Dev / Staging 分支上放心压测 Realtime 推送、调参数、改 Schema,再把稳定的变更以“仅同步 Schema”的方式迁移到 Prod 分支,为 Agent、全栈应用和多人协作场景提供一个安全可控的迭代闭环。
通过这些步骤,我们为 Agent 构建了一个功能完备、安全且实时的后端,而这一切几乎没有编写传统的后端服务代码。那么,准备好在你的下一个 Agent 或全栈项目中使用火山 Supabase 了吗?
这里有一份快速上手清单:
- 访问火山引擎官网 :搜索“火山 Supabase”并创建一个新项目。
- 获取 API 凭证 :在控制台的“连接”页面中,找到你的
Project URL和anon\_key。 - 安装
supabase-js:在你的前端或 Node.js 项目中运行npm install @supabase/supabase-js。 - 初始化客户端 :使用获取的凭证创建 Supabase 客户端实例。
- 设计数据表 :使用仪表盘的 Table Editor 或 SQL Editor 创建你的表结构。
- 开启 RLS :为你需要保护的每一张表启用行级安全,并至少为授权用户添加一条
SELECT策略,否则 API 将无法读取任何数据。 - 开始编码 :利用
select,insert,rpc等方法,尽情享受无需后端编码的全栈开发体验吧!
技术的终极价值在于降低创新的门槛,火山引擎 Supabase 融合了开源的灵活性与字节跳动的企业级技术可靠性,将复杂的后端能力转化为普惠的工具,助力开发者在诸如扣子、豆包、TRAE 这样高效的 Vibe Coding 中,跳出基建内耗的循环。无论是初创团队快速验证 MVP,还是大型企业构建稳定业务,都能以更低的成本、更高的效率和更强的安全保障,让每一个好想法,一键变为现实。
目前,火山 Supabase 已正式上线火山引擎官网。点击“阅读原文”欢迎试用产品或查阅技术文档,开启你的高效 Vibe Coding 之旅。
