ホームページ > バックエンド開発 > Golang > OpenAI、Go、PostgreSQL を使用したセマンティック検索エンジンの構築 (pgvector)

OpenAI、Go、PostgreSQL を使用したセマンティック検索エンジンの構築 (pgvector)

Linda Hamilton
リリース: 2025-01-15 11:09:44
オリジナル
771 人が閲覧しました

Building a Semantic Search Engine with OpenAI, Go, and PostgreSQL (pgvector)

近年、ベクトル埋め込みは現代の自然言語処理 (NLP) とセマンティック検索の基礎となっています。ベクトル データベースは、キーワード検索に依存するのではなく、数値表現 (埋め込み) を通じてテキストの「意味」を比較します。この例では、OpenAI 埋め込み、Go、および pgvector 拡張機能を備えた PostgreSQL を使用してセマンティック検索エンジンを作成する方法を示します。

埋め込みとは何ですか?

埋め込みは、高次元空間におけるテキスト (またはその他のデータ) のベクトル表現です。 2 つのテキストが意味的に類似している場合、それらのベクトルはこの空間内で互いに近くなります。 PostgreSQL (pgvector 拡張子付き) のようなデータベースに埋め込みを保存することで、類似性検索を迅速かつ正確に実行できます。

なぜ PostgreSQL と pgvector を選ぶのですか?

pgvector は、PostgreSQL にベクトル データ型を追加する一般的な拡張機能です。これにより次のことが可能になります:

  • エンベディングをベクトル列として保存します
  • 近似または正確な最近傍検索を実行します
  • 標準 SQL を使用してクエリを実行します

アプリの概要

  1. OpenAI の埋め込み API を呼び出して、入力テキストをベクトル埋め込みに変換します。
  2. これらの埋め込みを PostgreSQL に保存するには、pgvector 拡張機能を使用します。
  3. 埋め込みをクエリして、データベース内で意味的に最も類似したエントリを検索します。

前提条件

  • インストールしてください (1.19 を推奨)。
  • PostgreSQL がインストールされ、実行されています (ローカルまたはホスト)。
  • PostgreSQL に pgvector 拡張機能をインストールします。 (インストール手順については、pgvector の GitHub ページを参照してください。)
  • アクセスが埋め込まれた OpenAI API キー。

ローカル テスト用の postgres/pgvector および Docker に関連するタスクを含む Makefile。

<code class="language-makefile">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:pg17
psql:
    @psql -h localhost -U admin -d vectordb</code>
ログイン後にコピー

pgvector がインストールされていることを確認してください。次に、PostgreSQL データベースで次のようにします:

<code class="language-sql">CREATE EXTENSION IF NOT EXISTS vector;</code>
ログイン後にコピー

完全なコード

<code class="language-go">package main

import (
    "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, ", ")

    // pgvector 需要方括号表示法才能输入向量,例如 [0.1, 0.2, 0.3]
    return "[" + joined + "]"
}

func main() {
    // 加载环境变量
    err := godotenv.Load()
    if err != nil {
        log.Fatal("加载 .env 文件出错")
    }

    // 创建连接池
    dbpool, err := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))
    if err != nil {
        fmt.Fprintf(os.Stderr, "无法创建连接池:%v\n", err)
        os.Exit(1)
    }
    defer dbpool.Close()

    // 1. 确保已启用 pgvector 扩展
    _, err = dbpool.Exec(context.Background(), "CREATE EXTENSION IF NOT EXISTS vector;")
    if err != nil {
        log.Fatalf("创建扩展失败:%v\n", err)
        os.Exit(1)
    }

    // 2. 创建表(如果不存在)
    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("创建表失败:%v\n", err)
    }

    // 3. 创建索引(如果不存在)
    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("创建索引失败:%v\n", err)
    }

    // 4. 初始化 OpenAI 客户端
    apiKey := os.Getenv("OPENAI_API_KEY")
    if apiKey == "" {
        log.Fatal("未设置 OPENAI_API_KEY")
    }
    openaiClient := openai.NewClient(apiKey)

    // 5. 插入示例文档
    docs := []string{
        "PostgreSQL 是一个先进的开源关系数据库。",
        "OpenAI 提供基于 GPT 的模型来生成文本嵌入。",
        "pgvector 允许将嵌入存储在 Postgres 数据库中。",
    }

    for _, doc := range docs {
        err = insertDocument(context.Background(), dbpool, openaiClient, doc)
        if err != nil {
            log.Printf("插入文档“%s”失败:%v\n", doc, err)
        }
    }

    // 6. 查询相似性
    queryText := "如何在 Postgres 中存储嵌入?"
    similarDocs, err := searchSimilarDocuments(context.Background(), dbpool, openaiClient, queryText, 5)
    if err != nil {
        log.Fatalf("搜索失败:%v\n", err)
    }

    fmt.Println("=== 最相似的文档 ===")
    for _, doc := range similarDocs {
        fmt.Printf("- %s\n", doc)
    }
}

// insertDocument 使用 OpenAI API 为 `content` 生成嵌入,并将其插入 documents 表中。
func insertDocument(ctx context.Context, dbpool *pgxpool.Pool, client *openai.Client, content string) error {
    // 1) 从 OpenAI 获取嵌入
    embedResp, err := client.CreateEmbeddings(ctx, openai.EmbeddingRequest{
        Model: openai.AdaEmbeddingV2, // "text-embedding-ada-002"
        Input: []string{content},
    })
    if err != nil {
        return fmt.Errorf("CreateEmbeddings API 调用失败:%w", err)
    }

    // 2) 将嵌入转换为 pgvector 的方括号字符串
    embedding := embedResp.Data[0].Embedding // []float32
    embeddingStr := floats32ToString(embedding)

    // 3) 插入 PostgreSQL
    insertSQL := `
        INSERT INTO documents (content, embedding)
        VALUES (, ::vector)
    `
    _, err = dbpool.Exec(ctx, insertSQL, content, embeddingStr)
    if err != nil {
        return fmt.Errorf("插入文档失败:%w", err)
    }

    return nil
}

// searchSimilarDocuments 获取用户查询的嵌入,并根据向量相似性返回前 k 个相似的文档。
func searchSimilarDocuments(ctx context.Context, pool *pgxpool.Pool, client *openai.Client, query string, k int) ([]string, error) {
    // 1) 通过 OpenAI 获取用户查询的嵌入
    embedResp, err := client.CreateEmbeddings(ctx, openai.EmbeddingRequest{
        Model: openai.AdaEmbeddingV2, // "text-embedding-ada-002"
        Input: []string{query},
    })
    if err != nil {
        return nil, fmt.Errorf("CreateEmbeddings API 调用失败:%w", err)
    }

    // 2) 将 OpenAI 嵌入转换为 pgvector 的方括号字符串格式
    queryEmbedding := embedResp.Data[0].Embedding // []float32
    queryEmbeddingStr := floats32ToString(queryEmbedding)
    // 例如 "[0.123456, 0.789012, ...]"

    // 3) 构建按向量相似性排序的 SELECT 语句
    selectSQL := fmt.Sprintf(`
        SELECT content
        FROM documents
        ORDER BY embedding <-> '%s'::vector
        LIMIT %d;
    `, queryEmbeddingStr, k)

    // 4) 运行查询
    rows, err := pool.Query(ctx, selectSQL)
    if err != nil {
        return nil, fmt.Errorf("查询文档失败:%w", err)
    }
    defer rows.Close()

    // 5) 读取匹配的文档
    var contents []string
    for rows.Next() {
        var content string
        if err := rows.Scan(&content); err != nil {
            return nil, fmt.Errorf("扫描行失败:%w", err)
        }
        contents = append(contents, content)
    }
    if err = rows.Err(); err != nil {
        return nil, fmt.Errorf("行迭代错误:%w", err)
    }

    return contents, nil
}</code>
ログイン後にコピー

結論

PostgreSQL、Go、pgvector の OpenAI 埋め込みは、セマンティック検索アプリケーションを構築するための簡単なソリューションを提供します。テキストをベクトルとして表現し、データベース インデックスの力を活用することで、従来のキーワードベースの検索からコンテキストと意味による検索に移行します。

この改訂された出力では、元の言語スタイルが維持され、オリジナリティーを高めるために文が言い換えられ、画像の形式と位置が維持されます。また、主な変更点には、より説明的な変数名とコメントが含まれています。

以上がOpenAI、Go、PostgreSQL を使用したセマンティック検索エンジンの構築 (pgvector)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート