使用 OpenAI、Go 和 PostgreSQL (pgvector) 构建语义搜索引擎

使用 openai、go 和 postgresql (pgvector) 构建语义搜索引擎

近年来,向量嵌入技术已成为自然语言处理(NLP)和语义搜索的核心。与传统的关键词搜索不同,向量数据库通过比较文本的向量表示(嵌入)来理解文本的语义含义。本示例展示如何结合OpenAI嵌入、Go语言和PostgreSQL数据库(以及pgvector扩展)构建一个语义搜索引擎。

什么是嵌入?

嵌入是文本(或其他数据)在高维空间中的向量表示。语义相似的文本在该空间中具有相近的向量。将嵌入存储在PostgreSQL(使用pgvector扩展)等数据库中,可以实现快速准确的相似性搜索。

为什么PostgreSQL使用pgvector?

pgvector是一个流行的PostgreSQL扩展,它添加了向量数据类型,支持:

存储嵌入作为向量列执行近似或精确最近邻搜索使用标准SQL进行查询

应用程序概述

调用OpenAI的嵌入API将输入文本转换为向量嵌入。使用pgvector扩展将这些嵌入存储在PostgreSQL数据库中。查询嵌入以查找数据库中最语义相似的条目。

先决条件

Go语言安装(推荐1.19版本)。本地或云端部署并运行的PostgreSQL数据库。在PostgreSQL中安装pgvector扩展。(安装说明请参考pgvector的GitHub页面。)可访问OpenAI API密钥。

Makefile用于本地测试,包含PostgreSQL/pgvector和Docker相关任务:

pgvector:    @docker run -d         --name pgvector         -e postgres_user=admin         -e postgres_password=admin         -e postgres_db=vectordb         -v pgvector_data:/var/lib/postgresql/data         -p 5432:5432         pgvector/pgvector:pg17psql:    @psql -h localhost -u admin -d vectordb

登录后复制

确保已安装pgvector。然后在PostgreSQL数据库中执行:

create extension if not exists vector;

登录后复制

完整代码

package mainimport (    "context"    "fmt"    "log"    "os"    "strings"    "github.com/jackc/pgx/v5/pgxpool"    "github.com/joho/godotenv"    "github.com/sashabaranov/go-openai")func floats32ToString(floats []float32) string {    strVals := make([]string, len(floats))    for i, val := range floats {        strVals[i] = fmt.Sprintf("%f", val)    }    joined := strings.Join(strVals, ", ")    return "[" + joined + "]"}func main() {    err := godotenv.Load()    if err != nil {        log.Fatal("Error loading .env file")    }    dbpool, err := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))    if err != nil {        fmt.Fprintf(os.Stderr, "Unable to create connection pool: %v", err)        os.Exit(1)    }    defer dbpool.Close()    _, err = dbpool.Exec(context.Background(), "CREATE EXTENSION IF NOT EXISTS vector;")    if err != nil {        log.Fatalf("Failed to create extension: %v", err)        os.Exit(1)    }    createTableSQL := `    CREATE TABLE IF NOT EXISTS documents (        id SERIAL PRIMARY KEY,        content TEXT,        embedding vector(1536)    );    `    _, err = dbpool.Exec(context.Background(), createTableSQL)    if err != nil {        log.Fatalf("Failed to create table: %v", err)    }    createIndexSQL := `    CREATE INDEX IF NOT EXISTS documents_embedding_idx    ON documents USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);    `    _, err = dbpool.Exec(context.Background(), createIndexSQL)    if err != nil {        log.Fatalf("Failed to create index: %v", err)    }    apiKey := os.Getenv("OPENAI_API_KEY")    if apiKey == "" {        log.Fatal("OPENAI_API_KEY is not set")    }    openaiClient := openai.NewClient(apiKey)    docs := []string{        "PostgreSQL is an advanced open-source relational database.",        "OpenAI provides GPT-based models to generate text embeddings.",        "pgvector allows storing embeddings in a Postgres database.",    }    for _, doc := range docs {        err = insertDocument(context.Background(), dbpool, openaiClient, doc)        if err != nil {            log.Printf("Failed to insert document '%s': %v", doc, err)        }    }    queryText := "How to store embeddings in Postgres?"    similarDocs, err := searchSimilarDocuments(context.Background(), dbpool, openaiClient, queryText, 5)    if err != nil {        log.Fatalf("Search failed: %v", err)    }    fmt.Println("=== Most Similar Documents ===")    for _, doc := range similarDocs {        fmt.Printf("- %s", doc)    }}func insertDocument(ctx context.Context, dbpool *pgxpool.Pool, client *openai.Client, content string) error {    embedResp, err := client.CreateEmbeddings(ctx, openai.EmbeddingRequest{        Model: openai.AdaEmbeddingV2,        Input: []string{content},    })    if err != nil {        return fmt.Errorf("CreateEmbeddings API call failed: %w", err)    }    embedding := embedResp.Data[0].Embedding    embeddingStr := floats32ToString(embedding)    insertSQL := `        INSERT INTO documents (content, embedding)        VALUES ($1, $2::vector)    `    _, err = dbpool.Exec(ctx, insertSQL, content, embeddingStr)    if err != nil {        return fmt.Errorf("failed to insert document: %w", err)    }    return nil}func searchSimilarDocuments(ctx context.Context, pool *pgxpool.Pool, client *openai.Client, query string, k int) ([]string, error) {    embedResp, err := client.CreateEmbeddings(ctx, openai.EmbeddingRequest{        Model: openai.AdaEmbeddingV2,        Input: []string{query},    })    if err != nil {        return nil, fmt.Errorf("CreateEmbeddings API call failed: %w", err)    }    queryEmbedding := embedResp.Data[0].Embedding    queryEmbeddingStr := floats32ToString(queryEmbedding)    selectSQL := fmt.Sprintf(`        SELECT content        FROM documents        ORDER BY embedding  '%s'::vector        LIMIT %d;    `, queryEmbeddingStr, k)    rows, err := pool.Query(ctx, selectSQL)    if err != nil {        return nil, fmt.Errorf("failed to query documents: %w", err)    }    defer rows.Close()    var contents []string    for rows.Next() {        var content string        if err := rows.Scan(&content); err != nil {            return nil, fmt.Errorf("failed to scan row: %w", err)        }        contents = append(contents, content)    }    if err = rows.Err(); err != nil {        return nil, fmt.Errorf("row iteration error: %w", err)    }    return contents, nil}

登录后复制

结论

OpenAI嵌入、Go语言和PostgreSQL数据库中的pgvector扩展提供了一种简便的语义搜索引擎构建方案。通过将文本表示为向量并利用数据库索引的优势,我们实现了从传统的基于关键词的搜索到基于语义理解的搜索的转变。

以上就是使用 OpenAI、Go 和 PostgreSQL (pgvector) 构建语义搜索引擎的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2477107.html

(0)
上一篇 2025年3月4日 19:19:23
下一篇 2025年2月22日 23:28:47

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 微服务中的事务:SAGA 模式概述部分

    构建强大的分布式系统是一项极具挑战性的任务,尤其是在保证多个服务数据一致性方面。在微服务架构中,传统的数据库事务往往力不从心。这时,分布式事务便成为关键解决方案。 分布式事务能够协调多个服务间的操作,并优雅地处理各种故障。而SAGA模式是实…

    2025年3月4日
    200
  • Docker 卷

    容器化应用的关键在于数据持久化。docker容器默认情况下,删除后其内部所有数据都会丢失。解决方法是使用docker卷。它们允许数据在容器生命周期中持续存在,为任何应用提供隔离和可扩展性。 为何使用Docker卷? 持久性:创建或链接卷到容…

    2025年3月4日
    200
  • 如何使用Golang,Gin和Postgresql构建CRUD应用程序

    本教程演示如何使用Go语言、Gin框架和PostgreSQL数据库构建一个简单的CRUD (创建、读取、更新、删除) 应用。最终,您将得到一个可以管理PostgreSQL数据库中数据的基本应用。 目录 简介前提条件项目结构项目设置创建数据库…

    2025年3月4日
    200
  • 实时批处理

    高效批处理:实时数据处理的优雅方案 批处理是优化数据库操作的常用技术,广泛应用于数据库、Redis和各种批量API中。其优势在于速度更快、成本更低且速度限制更低,但代价是代码复杂度略有提升。本文探讨如何优雅地处理实时到达的数据批处理问题。 …

    2025年3月4日
    200
  • Golang文本/模板中的SQL查询

    在使用Go的text/template包动态生成SQL查询构建后端API时,提高开发效率的同时,务必注意SQL注入的风险。本文将演示如何避免这种风险。 text/template的SQL注入漏洞示例 以下代码片段展示了如何通过字符串插值构建…

    2025年3月4日
    200
  • GO中的数据处理管道(Golang)

    Go语言数据处理管道详解 Go语言中的数据处理管道是一种将数据处理流程分解成一系列阶段或步骤的模式。每个阶段对数据执行特定操作,前一阶段的输出作为下一阶段的输入。这种模式广泛应用于ETL(提取、转换、加载)、流处理和批处理等场景。Go语言利…

    2025年3月4日
    200
  • 不要浪费时间 – 立即消除Golang!

    还在犹豫是否学习Golang?作为开发者,您可能已经错过了很多机会!近年来,行业对更快、更简洁、更可靠代码的需求日益增长。虽然JavaScript、Java和Python各有优势,但Golang凭借其速度、简洁性和并发性脱颖而出,让您在竞争…

    2025年3月4日
    200
  • Go HTTP文件下载文件名错乱:如何正确设置HTTP Header实现文件下载?

    go http文件下载文件名混乱:正确设置http header实现精准文件下载 使用Go语言的http包提供本地文件下载服务时,下载后的文件名经常与原始文件名不符,导致文件下载失败。本文分析了这个问题,并提供了解决方案。 问题根源 问题在…

    2025年3月4日
    200
  • Go HTTP文件下载:文件名错误如何解决?

    Go语言HTTP文件下载文件名错误的解决方法 在使用Go语言的http包提供本地文件下载服务时,经常会遇到下载文件名与实际文件名不符的问题,例如下载文件名显示为”response.html”。本文将分析这个问题的原因…

    2025年3月4日
    200
  • Go语言HTTP下载文件文件名错误如何修复?

    Go语言HTTP文件下载:文件名修复指南 问题: 使用Go语言的http包提供文件下载服务时,下载文件名可能与源文件名不一致。例如,源文件名为file.docx,但下载后文件名却变成了response.html。即使设置了header也无法…

    2025年3月4日
    200

发表回复

登录后才能评论