大模型 RAG:基於 PgSql 的向量檢索

一 RAG 與向量檢索

1.1 RAG 概念

檢索增強生成(Retrieval-augmented Generation),簡稱 RAG。

RAG 通常包括兩個階段:1、檢索上下文相關信息;2、使用檢索到的知識指導生成過程。簡單來說,就像開卷考試,我們可以攜帶參考材料用來查找相關信息來回答問題。

1.2 RAG 意義

目前對大模型的使用通常存在兩個主要挑戰:1、由於生成模型依賴於內在知識(權重),對於未覆蓋到的知識領域可能會產生大量的幻覺,也就是 “一本正經的胡說八道”;其次,由於大模型參數量巨大,重新訓練或微調方法成本過高。

1.3 RAG 應用框架

RAG 應用框架如下圖所示,包含文本向量化、檢索向量數據庫、獲取上下文(相關知識 / 文檔)、prompt 構造、調用大模型執行文本生成等主要環節。

當然這裏只是描述了一個主流程,實際應用還有對多輪 / 歷史會話處理、多輪問題改寫,意圖識別與分發,結果聚合等等。

二 向量數據庫

    由於介紹 RAG 的文章已經很多,本篇主要打算介紹向量數據庫部分,所以不再對 RAG 進行贅述。

2.1 向量數據庫方案

目前已經有十幾種可選的向量數據庫技術方案,包括 Milvus、MongoDB Atlas、Chroma、Weaviate 等。從分類的角度說,包括:原生向量數據庫(Chroma、LanceDB、Mivus 等)、支持向量的全文檢索數據庫(Elastic、Lucene、OpenSearch 和 Solr)、支持向量的 NoSQL 數據庫(Cassandra、Rockset、Azure Cosmos DB 和 MongoDB)、和支持向量的關係數據庫(PostgreSQL、Clickhouse、SingleStoreDB 等)。

主流向量數據庫的部分指標對比如下:

通常大模型應用場景,Milvus 等原生向量數據庫是最推薦的。但根據實際使用場景和習慣,在知識庫數量並不是很大的場景(億級以下),考慮到使用習慣和學習複雜度,也可以選擇 PgSQL 等關係型數據庫。

2.2 PgSql 與 PgVector

Postgres 通過_ pg_vector 和 pg_embdding_ 兩個插件來實現向量數據庫,讓 PG 數據庫支持向量索引檢索的能力。其索引算法使用的是基於 Faiss 的 IVF Flat 索引,提供了優異的召回率。

三 基於 PgSql 的向量檢索示例

3.1 建立向量庫

如下建表語句所示,向量庫主要文檔內容和 embedding(文檔向量化結果)。考慮到在實際應用場景可能涉及文檔權限控制,或展示參考資料,我們還設計了文檔名、文檔 id 字段。

-- vector.knowledge_doc_vector definition
-- Drop table
-- DROP TABLE vector.knowledge_doc_vector;
CREATE TABLE vector.knowledge_doc_vector (
  id bigserial NOT NULL,
  doc_id bigserial NOT NULL,
  embedding public.vector NULL,
  doc_content text NULL,
  doc_name varchar NULL,
  doc_page varchar NULL,
  CONSTRAINT knowledge_doc_vector_pkey PRIMARY KEY (id)
);

示例數據如下:

3.2 Mybatis 連接 PgSql

除了 mybatis-plus 之外,引入 pgsql 和 pgvector 兩個依賴:

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>
     <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.pgvector</groupId>
            <artifactId>pgvector</artifactId>
            <version>0.1.6</version>
        </dependency>

3.3 編寫 mapper

用於查找相似向量,按照餘弦相似度計算

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fawvw.pms.vector.domain.dto.KnowledgeDocVectorDTO;
import com.fawvw.pms.vector.domain.entity.KnowledgeDocVector;
import com.google.gson.Gson;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.SelectProvider;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Mapper
public interface VectorMapper extends BaseMapper<KnowledgeDocVector> {
    @SelectProvider(type = SqlProvider.class, method = "findUsersWithSimilarVectors")
    List<KnowledgeDocVectorDTO> findUsersWithSimilarVectors(double[] targetVector);
    static class SqlProvider {
        public String findUsersWithSimilarVectors(Map<String, Object> param) {
            System.out.println("param keys:" + new Gson().toJson(param.keySet()));
            double[] targetVector = (double[]) param.get("array");
            String sql = "SELECT id, doc_id, embedding, doc_content, " +
                    "cosine_distance(embedding, '" + Arrays.toString(targetVector) + "') AS similarity " +
                    "FROM knowledge_doc_vector " +
                    "ORDER BY similarity DESC";
            return sql;
        }
    }
}

3.4 方法中調用,查詢相似向量

下面示例代碼使用的是阿里的靈積服務計算 embedding,也可以採用其他方式計算。

public List findSimilarVector(String query) {
        // query轉爲向量
        TextEmbeddingResult embeddingResult = dashCodeHttpApi.textEmbedding(query);
        List<Double> embeddings = embeddingResult.getOutput().getEmbeddings().get(0).getEmbedding();
        double[] vector = new double[embeddings.size()];
        for (int i = 0; i < embeddings.size(); i++) {
            Double embedding = embeddings.get(i);
            vector[i] = embedding;
        }
        log.info("vector:{}", vector);
        List<KnowledgeDocVectorDTO> vectorDTOList = vectorMapper.findUsersWithSimilarVectors(vector);
        log.info("vectorDTOList:{}", vectorDTOList);
        // 過濾掉相似度低的向量檢索結果 閾值:0.7
        vectorDTOList = vectorDTOList.stream()
                .filter(vectorDTO -> vectorDTO.getSimilarity() <= 0.7)
                .collect(Collectors.toList());
        return vectorDTOList;
    }

拿到與問題相關的文檔知識之後,我們就可以封裝 prompt,並調用大模型 API 獲取生成式回答了。具體方法留在下一篇中給出。

四 小結

    本篇介紹了 RAG 的一些基礎知識,以及向量庫在其中的作用及選型。並基於 PgSQL 給出了一個向量庫的使用示例。接下來的文章中,我們將深入探索如何實現可用的 RAG 應用。歡迎留言一起探討。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/SIkuvKUWW_NmZRPGbgCIkA