语义搜索
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:
create extension if not exists vector with schema extensions;create table embeddings ( id bigint primary key generated always as identity, content text not null, embedding extensions.vector (384));alter table embeddings enable row level security;create 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:
import { withSupabase } from 'npm:@supabase/server@^1'const model = new Supabase.ai.Session('gte-small')// Triggered by a Database Webhook, which authenticates with a secret key.// Deploy with `verify_jwt = false`.export default { fetch: withSupabase({ auth: 'secret' }, async (req, ctx) => { const payload: WebhookPayload = await req.json() const { content, id } = payload.record // Generate embedding. const embedding = await model.run(content, { mean_pool: true, normalize: true, }) // Store in database. const { error } = await ctx.supabaseAdmin .from('embeddings') .update({ embedding: JSON.stringify(embedding) }) .eq('id', id) if (error) console.warn(error.message) return Response.json({ ok: true }) }),}创建一个数据库函数和远程过程调用 #
🌐 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:
-- Matches document sections using vector similarity search on embeddings---- Returns a setof embeddings so that we can use PostgREST resource embeddings (joins with other tables)-- Additional filtering like limits can be chained to this function callcreate or replace function query_embeddings(embedding extensions.vector(384), match_threshold float)returns setof embeddingslanguage plpgsqlas $$begin return query select * from embeddings -- The inner product is negative, so we negate match_threshold where embeddings.embedding <#> embedding < -match_threshold -- Our embeddings are normalized to length 1, so cosine similarity -- and inner product will produce the same query results. -- Using inner product which can be computed faster. -- -- For the different distance functions, see https://github.com/pgvector/pgvector order by embeddings.embedding <#> embedding;end;$$;在 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:
import { withSupabase } from 'npm:@supabase/server@^1'const model = new Supabase.ai.Session('gte-small')export default { fetch: withSupabase({ auth: 'user' }, async (req, ctx) => { const { search } = await req.json() if (!search) return Response.json({ error: 'Please provide a search param!' }, { status: 400 }) // Generate embedding for search term. const embedding = await model.run(search, { mean_pool: true, normalize: true, }) // Query embeddings. const { data: result, error } = await ctx.supabase .rpc('query_embeddings', { embedding, match_threshold: 0.8, }) .select('content') .limit(3) if (error) { return Response.json({ error: error.message }, { status: 500 }) } return Response.json({ search, result }) }),}你现在已经搭建好了由 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!