语义搜索
Semantic Search with pgvector and Supabase Edge Functions
语义搜索 解释用户查询背后的含义,而不是仅仅关注关键词。它使用机器学习来捕捉查询背后的意图和上下文,处理语言细微差别,比如同义词、表述变化和词语关系。
自从 Supabase Edge Runtime v1.36.0 起,你可以在 Supabase Edge Functions 中原生运行 gte-small 模型,无需任何外部依赖!这样你就可以生成文本嵌入,而无需调用任何外部 API!
🌐 Since Supabase Edge Runtime v1.36.0 you can run the gte-small model natively within Supabase Edge Functions without any external dependencies! This allows you to generate text embeddings without calling any external APIs!
在本教程中,你将实现三个部分:
🌐 In this tutorial you're implementing three parts:
- 一个
generate-embedding数据库 webhook 边缘函数,当在public.embeddings表中添加(或更新)内容行时生成嵌入。 - 一个
query_embeddingsPostgres 函数,它允许我们通过 远程过程调用 (RPC) 从 Edge Function 执行相似性搜索。 - 一个
search边缘函数,它用于生成搜索词的嵌入,通过 RPC 函数调用执行相似度搜索,并返回结果。
你可以在 GitHub 找到完整的示例代码
🌐 You can find the complete example code on GitHub
创建数据库表和网络钩子 #
🌐 Create the database table and webhook
给定下列表定义:
🌐 Given the following table definition:
1create extension if not exists vector with schema extensions;23create table embeddings (4 id bigint primary key generated always as identity,5 content text not null,6 embedding extensions.vector (384)7);8alter table embeddings enable row level security;910create index on embeddings using hnsw (embedding vector_ip_ops);你可以将以下边缘函数部署为数据库 webhook,以生成插入表中的任何文本内容的嵌入:
🌐 You can deploy the following edge function as a database webhook to generate the embeddings for any text content inserted into the table:
1import { withSupabase } from 'npm:@supabase/server@^1'23const model = new Supabase.ai.Session('gte-small')45// Triggered by a Database Webhook, which authenticates with a secret key.6// Deploy with `verify_jwt = false`.7export default {8 fetch: withSupabase({ auth: 'secret' }, async (req, ctx) => {9 const payload: WebhookPayload = await req.json()10 const { content, id } = payload.record1112 // Generate embedding.13 const embedding = await model.run(content, {14 mean_pool: true,15 normalize: true,16 })1718 // Store in database.19 const { error } = await ctx.supabaseAdmin20 .from('embeddings')21 .update({ embedding: JSON.stringify(embedding) })22 .eq('id', id)23 if (error) console.warn(error.message)2425 return Response.json({ ok: true })26 }),27}创建一个数据库函数和远程过程调用 #
🌐 Create a Database Function and RPC
现在你的嵌入已经存储在 Postgres 数据库表中,你可以通过 Supabase Edge Functions 使用 远程过程调用 (RPC) 来查询它们。
🌐 With the embeddings now stored in your Postgres database table, you can query them from Supabase Edge Functions by using Remote Procedure Calls (RPC).
🌐 Given the following Postgres Function:
1-- Matches document sections using vector similarity search on embeddings2--3-- Returns a setof embeddings so that we can use PostgREST resource embeddings (joins with other tables)4-- Additional filtering like limits can be chained to this function call5create or replace function query_embeddings(embedding extensions.vector(384), match_threshold float)6returns setof embeddings7language plpgsql8as $$9begin10 return query11 select *12 from embeddings1314 -- The inner product is negative, so we negate match_threshold15 where embeddings.embedding <#> embedding < -match_threshold1617 -- Our embeddings are normalized to length 1, so cosine similarity18 -- and inner product will produce the same query results.19 -- Using inner product which can be computed faster.20 --21 -- For the different distance functions, see https://github.com/pgvector/pgvector22 order by embeddings.embedding <#> embedding;23end;24$$;在 Supabase Edge Functions 中查询向量 #
🌐 Query vectors in Supabase Edge Functions
你可以先用 supabase-js 生成搜索词的嵌入,然后调用 Postgres 函数从你存储的嵌入中找到相关结果,直接在你的 Supabase Edge Function 中操作:
🌐 You can use supabase-js to first generate the embedding for the search term and then invoke the Postgres function to find the relevant results from your stored embeddings, right from your Supabase Edge Function:
1import { withSupabase } from 'npm:@supabase/server@^1'23const model = new Supabase.ai.Session('gte-small')45export default {6 fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {7 const { search } = await req.json()8 if (!search) return Response.json({ error: 'Please provide a search param!' }, { status: 400 })9 // Generate embedding for search term.10 const embedding = await model.run(search, {11 mean_pool: true,12 normalize: true,13 })1415 // Query embeddings.16 const { data: result, error } = await ctx.supabase17 .rpc('query_embeddings', {18 embedding,19 match_threshold: 0.8,20 })21 .select('content')22 .limit(3)23 if (error) {24 return Response.json({ error: error.message }, { status: 500 })25 }2627 return Response.json({ search, result })28 }),29}你现在已经搭建好了由 AI 驱动的语义搜索,而且完全不依赖外部服务!你只需要你自己、pgvector 和 Supabase Edge Functions 就行了!
🌐 You now have AI powered semantic search set up without any external dependencies! All you need: you, pgvector, and Supabase Edge Functions!