springai+pgvector+ollama实现rag

        首先在ollama中安装mofanke/dmeta-embedding-zh:latest。执行ollama run mofanke/dmeta-embedding-zh 。实现将文本转化为向量数据

        接着安装pgvector(建议使用pgadmin4作为可视化工具,用navicate会出现表不显示的问题)

        安装好需要的软件后我们开始编码操作。

1:在pom文件中加入:

        <!--用于连接pgsql-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--用于使用pgvector来操作向量数据库-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
        </dependency>
        <!--pdf解析-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pdf-document-reader</artifactId>
        </dependency>
        <!--文档解析l-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-tika-document-reader</artifactId>
        </dependency>

2:在yml中配置:

spring:
  datasource:
    url: jdbc:postgresql://127.0.0.1:5432/postgres
    username: postgres
    password: password
  ai:
    vectorstore:
      pgvector:
        dimensions: 768   #不同的embeddingmodel对应的值
    ollama:
      base-url: http://127.0.0.1:11434
      chat:
        enabled: true
        options:
          model: qwen2:7b
      embedding:
        model: mofanke/dmeta-embedding-zh

3:在controller中加入:

   /**
     * 嵌入文件
     *
     * @param file 待嵌入的文件
     * @return 是否成功
     */
    @SneakyThrows
    @PostMapping("embedding")
    public List<Document> embedding(@RequestParam MultipartFile file) {

        // 从IO流中读取文件
        TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(new InputStreamResource(file.getInputStream()));
        // 将文本内容划分成更小的块
        List<Document> splitDocuments = new TokenTextSplitter()
                .apply(tikaDocumentReader.read());
        // 存入向量数据库,这个过程会自动调用embeddingModel,将文本变成向量再存入。
        vector.add(splitDocuments);
        return splitDocuments;
    }

调用上方的接口可以将文档转为向量数据存入到pgvector中

4:请求聊天,先根据聊天内容通过pgvector获取对应的数据,并将结果丢到qwen2模型中进行数据分析并返回结果

/**
     * 获取prompt
     *
     * @param message 提问内容
     * @param context 上下文
     * @return prompt
     */
    private String getChatPrompt2String(String message, String context) {
        String promptText = """
				请用仅用以下内容回答"%s" ,输出结果仅在以下内容中,输出内容仅以下内容,不需要其他描述词:
				%s
				""";
        return String.format(promptText, message, context);
    }

    @GetMapping("chatToPgVector")
    public String chatToPgVector(String message) {

        // 1. 定义提示词模板,question_answer_context会被替换成向量数据库中查询到的文档。
        String promptWithContext = """
                你是一个代码程序,你需要在文本中获取信息并输出成json格式的数据,下面是上下文信息
                ---------------------
                {question_answer_context}
                ---------------------
                给定的上下文和提供的历史信息,而不是事先的知识,回复用户的意见。如果答案不在上下文中,告诉用户你不能回答这个问题。
                """;
        //查询获取文档信息
        List<Document> documents = vector.similaritySearch(message,"test_store");
        //提取文本内容
        String content = documents.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n"));
        System.out.println(content);
        //封装prompt并调用大模型
        String chatResponse = ollamaChatModel.call(getChatPrompt2String(message, content));
        return chatResponse;
   /*     return ChatClient.create(ollamaChatModel).prompt()
                .user(message)
                // 2. QuestionAnswerAdvisor会在运行时替换模板中的占位符`question_answer_context`,替换成向量数据库中查询到的文档。此时的query=用户的提问+替换完的提示词模板;
                .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults(), promptWithContext))
                .call().content();*/
    }

至此一个简单的rag搜索增强demo就完成了。接下来我们来看看PgVectorStore为我们做了什么

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.ai.vectorstore;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pgvector.PGvector;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.IntStream;
import org.postgresql.util.PGobject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.filter.FilterExpressionConverter;
import org.springframework.ai.vectorstore.filter.converter.PgVectorFilterExpressionConverter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

public class PgVectorStore implements VectorStore, InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(PgVectorStore.class);
    public static final int OPENAI_EMBEDDING_DIMENSION_SIZE = 1536;
    public static final int INVALID_EMBEDDING_DIMENSION = -1;
    public static final String VECTOR_TABLE_NAME = "vector_store";
    public static final String VECTOR_INDEX_NAME = "spring_ai_vector_index";
    public final FilterExpressionConverter filterExpressionConverter;
    private final JdbcTemplate jdbcTemplate;
    private final EmbeddingModel embeddingModel;
    private int dimensions;
    private PgDistanceType distanceType;
    private ObjectMapper objectMapper;
    private boolean removeExistingVectorStoreTable;
    private PgIndexType createIndexMethod;
    private final boolean initializeSchema;

    public PgVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {
        this(jdbcTemplate, embeddingModel, -1, PgVectorStore.PgDistanceType.COSINE_DISTANCE, false, PgVectorStore.PgIndexType.NONE, false);
    }

    public PgVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, int dimensions) {
        this(jdbcTemplate, embeddingModel, dimensions, PgVectorStore.PgDistanceType.COSINE_DISTANCE, false, PgVectorStore.PgIndexType.NONE, false);
    }

    public PgVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel emb