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

admin 2026-03-10 01:36:39 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 火山引擎推出火山Supabase,一款兼容开源生态的BaaS产品,旨在解决AIAgent开发中的后端基建难题。该产品通过全链路Serverless架构实现弹性伸缩与成本节约,提供企业级数据安全体系与AI原生交互,显著提升开发效率。文档阐述了其基于PostgreSQL的核心架构与组件,并利用代码实例演示了在Agent任务管理及RAG功能中的集成应用。 综合评分: 70 文章分类: 产品介绍,应用安全,云安全,解决方案


cover_image

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

火山引擎数据库 火山引擎数据库

字节跳动技术团队

2026年3月9日 18:05 北京

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)进行环境隔离。

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

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

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

站在 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):如果某个操作(如 INSERT 或 UPDATE)修改了数据库,Realtime 服务会监听到 WAL 的变化,并将变更通过 WebSocket 推送给所有订阅了该数据的客户端,实现界面自动更新。
  8. 服务端逻辑 (Edge Functions):如果需要执行复杂或敏感的操作(如调用外部支付接口),客户端会调用一个 Edge Function。该函数在沙箱环境中运行,可以使用 service_role_key 安全地与数据库或其他服务交互,并将结果返回给客户端。

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

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

将火山 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:443SUPABASE_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.jsimport { createClient } from'@supabase/supabase-js'
const supabaseUrl = process.env.SUPABASE_URLconst supabaseAnonKey = process.env.SUPABASE_ANON_KEY
exportconst 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 defaultfalse,  created_at  timestamptz default now());
-- 为任务表启用行级安全 (RLS)ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;
-- 策略:用户只能看到和操作自己的任务CREATE POLICY "Users can manage their own tasks."ON public.tasksFOR ALLUSING(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());
-- 为记忆表启用 RLSALTER TABLE public.memories ENABLE ROW LEVEL SECURITY;
-- 策略:用户只能访问自己的记忆CREATE POLICY "Users can access their own memories."ON public.memoriesFOR ALLUSING(auth.uid() = user_id)WITH CHECK(auth.uid() = user_id);

(2) 创建向量检索Function

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

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

(3) 实现基本的 CRUD 操作

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

上下滑动查看完整内容

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

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

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

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

上下滑动查看完整内容

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

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

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

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

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

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

上下滑动查看完整内容

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

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

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

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

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

上下滑动查看完整内容

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

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

上下滑动查看完整内容

import&nbsp;osimport&nbsp;uuidfrom&nbsp;dotenv&nbsp;import&nbsp;load_dotenvfrom&nbsp;supabase&nbsp;import&nbsp;create_client, Clientfrom&nbsp;openai&nbsp;import&nbsp;OpenAIfrom&nbsp;PyPDF2&nbsp;import&nbsp;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&nbsp;split_document(file_path, chunk_size=500, chunk_overlap=50):&nbsp; &nbsp;&nbsp;"""&nbsp; &nbsp; 拆分文档为小分块,避免向量长度超限,同时保留上下文重叠&nbsp; &nbsp; """&nbsp; &nbsp; chunks = []&nbsp; &nbsp;&nbsp;# 处理 PDF&nbsp; &nbsp;&nbsp;if&nbsp;file_path.endswith(".pdf"):&nbsp; &nbsp; &nbsp; &nbsp; reader = PdfReader(file_path)&nbsp; &nbsp; &nbsp; &nbsp; text =&nbsp;""&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;page&nbsp;in&nbsp;reader.pages:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; text += page.extract_text()&nbsp;or""&nbsp; &nbsp;&nbsp;# 处理纯文本&nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;with&nbsp;open(file_path,&nbsp;"r", encoding="utf-8")&nbsp;as&nbsp;f:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; text = f.read()&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;# 分块逻辑&nbsp; &nbsp; start =&nbsp;0&nbsp; &nbsp;&nbsp;while&nbsp;start <&nbsp;len(text):&nbsp; &nbsp; &nbsp; &nbsp; end = start + chunk_size&nbsp; &nbsp; &nbsp; &nbsp; chunk = text[start:end].strip()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;chunk:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; chunks.append(chunk)&nbsp; &nbsp; &nbsp; &nbsp; start = end - chunk_overlap &nbsp;# 重叠部分,保持上下文连贯&nbsp; &nbsp;&nbsp;return&nbsp;chunks
# 2. 生成 Embedding 向量def&nbsp;get_embedding(text):&nbsp; &nbsp;&nbsp;"""调用 OpenAI Embedding 模型生成向量"""&nbsp; &nbsp; response = openai_client.embeddings.create(&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;input=text,&nbsp; &nbsp; &nbsp; &nbsp; model="text-embedding-3-small"&nbsp;&nbsp;# 轻量、低成本,维度 1536&nbsp; &nbsp; )&nbsp; &nbsp;&nbsp;return&nbsp;response.data[0].embedding
# 3. 文档入库主函数def&nbsp;ingest_document(file_path, user_id):&nbsp; &nbsp;&nbsp;# 步骤 1:拆分文档&nbsp; &nbsp; chunks = split_document(file_path)&nbsp; &nbsp; ifnot chunks:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;ValueError("文档内容为空")&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;# 步骤 2:创建文档元数据&nbsp; &nbsp; document_name = os.path.basename(file_path)&nbsp; &nbsp; document_type = file_path.split(".")[-1]&nbsp; &nbsp; doc_response = supabase.table("rag_documents").insert({&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"document_name": document_name,&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"document_type": document_type,&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"user_id": user_id&nbsp; &nbsp; }).execute()&nbsp; &nbsp; document_id = doc_response.data[0]["id"]&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;# 步骤 3:生成向量并入库&nbsp; &nbsp; chunk_records = []&nbsp; &nbsp;&nbsp;for&nbsp;idx, chunk&nbsp;in&nbsp;enumerate(chunks):&nbsp; &nbsp; &nbsp; &nbsp; embedding = get_embedding(chunk)&nbsp; &nbsp; &nbsp; &nbsp; chunk_records.append({&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"document_id": document_id,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"chunk_text": chunk,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"chunk_index": idx,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"embedding": embedding&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;# 批量插入(提升效率)&nbsp; &nbsp; supabase.table("rag_document_chunks").insert(chunk_records).execute()&nbsp; &nbsp;&nbsp;print(f"文档&nbsp;{document_name}&nbsp;入库完成,共拆分&nbsp;{len(chunks)}&nbsp;个分块")&nbsp; &nbsp;&nbsp;return&nbsp;document_id
# 测试:上传一个 PDF 文档if&nbsp;__name__ ==&nbsp;"__main__":&nbsp; &nbsp;&nbsp;# 替换为你的文件路径和用户 ID(Supabase Auth 的 user_id)&nbsp; &nbsp; ingest_document("test_document.pdf",&nbsp;"your-user-uuid-here")

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

上下滑动查看完整内容

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

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

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

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

上下滑动查看完整内容

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

这样,任何用户创建、更新或删除任务,所有打开该页面的客户端都会几乎瞬间收到通知并更新 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 URL 和 anon_key
  3. 安装 supabase-js在你的前端或 Node.js 项目中运行 npm install @supabase/supabase-js
  4. 初始化客户端:使用获取的凭证创建 Supabase 客户端实例。
  5. 设计数据表:使用仪表盘的 Table Editor 或 SQL Editor 创建你的表结构。
  6. 开启 RLS:为你需要保护的每一张表启用行级安全,并至少为授权用户添加一条 SELECT 策略,否则 API 将无法读取任何数据。
  7. 开始编码:利用 selectinsertrpc 等方法,尽情享受无需后端编码的全栈开发体验吧!

结语:火山 Supabase,释放 Vibe Coding 的高效创造力

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

已关注

关注

重播 分享 赞

关闭

观看更多

更多

退出全屏

切换到竖屏全屏退出全屏

字节跳动技术团队已关注

分享视频

,时长01:40

0/0

00:00/01:40

切换到横屏模式

继续播放

[ ]

进度条,百分之0

播放

00:00

/

01:40

01:40

倍速

全屏

倍速播放中

0.5倍 0.75倍 1.0倍 1.5倍 2.0倍

超清 流畅

 您的浏览器不支持 video 标签

继续观看

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

观看更多

转载

,

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

字节跳动技术团队已关注

分享点赞在看

已同步到看一看写下你的评论

视频详情

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


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:字节跳动技术团队 火山引擎数据库 火山引擎数据库《Vibe Coding 一键部署——火山引擎推出 Supabase, 驱动Agent 应用快速上线》

评论:0   参与:  0