0


Spring AI使用向量数据库实现检索AI对话

Spring AI - 使用向量数据库实现检索式AI对话

  1. Spring AI 并不仅限于针对大语言模型对话API进行了统一封装,它还可以通过简单的方式实现LangChain的一些功能。本篇将带领读者实现一个简单的检索式AI对话接口。 LangChain相关知识

1.需求背景

在一些场景下,我们想让AI

根据我们提供的数据

进行

回复

。因为对话有最大Token的限制,因此很多场景下我们是无法直接将所有的数据发给AI的,一方面在数据量很大的情况下,会突破Token的限制,另一方面,在不突破Token限制的情况下也会有不必要的对话费用开销。因此我们如何在花费最少费用的同时又能让A

I更好的根据我们提供的数据

进行回复是一个非常关键的问题。针对这一问题,我们可以采用

数据向量化

的方式来解决。

2.实现原理

将我们

个人数据

存储到

向量数据库

中。然后,在用户想AI发起对话之前,首先从向量数据库中

检索

一组

相似的文档

。然后,将这些

文档

作为

用户问题的上下文

,并与用户的对话一起发送到 AI 模型,从而实现

精确性的回复

。这种方式称为检索增强生成**(RAG)**。

2.1 数据向量化

我们有很多种方式将数据向量化,最简单的就是通过调用第三方API来实现。以OpenAI的API为例,它提供了

https://api.openai.com/v1/embeddings

接口,通过请求该接口可以获取某段文本的向量化的数据。具体可参考官方API介绍:Create embeddings 。在Spring AI中,我们不必调用该接口手动进行向量化处理,在存储到向量数据库的时候,Spring AI会自动调用的。

2.2 向量存储以及检索

Spring AI

中有一个

VectorStore

抽象接口,该接口定义了

Spring AI

向量数据库

的交互操作,我们只需通过

简单的向量数据库

配置

即可使用该接口对向量数据库进行操作。

publicinterfaceVectorStore{voidadd(List<Document> documents);Optional<Boolean>delete(List<String> idList);List<Document>similaritySearch(String query);List<Document>similaritySearch(SearchRequest request);}
2.3 向量数据库的介绍
向量数据库(Vector Database)

是一种特殊类型的数据库,在人工智能应用中发挥着重要作用。在向量数据库中,查询操作与传统的关系数据库不同。它们是执行

相似性搜索

,而不是精确匹配。当给定向量作为查询时,向量数据库返回与查询向量“相似”的向量。通过这种方式,我们就能将个人的数据与AI模型进行集成。`

常见的向量数据库有:Chroma、Milvus、Pgvector、Redis、Neo4j等。

3.代码实现

3.1 Pgvector的安装

Pgvector将使用Docker安装。docker-compose.yml文件如下:

version:'3.7'
services:
  postgres:
    image: ankane/pgvector:v0.5.0
    restart: always
    environment:-POSTGRES_USER=postgres
      -POSTGRES_PASSWORD=postgres
      -POSTGRES_DB=vector_store
      -PGPASSWORD=postgres
    logging:
      options:
        max-size:10m
        max-file:"3"
    ports:- '5432:5432'
    healthcheck:
      test:"pg_isready -U postgres -d vector_store"
      interval:2s
      timeout:20s
      retries:10
3.2创建Spring项目

1.pom.xml的配置:

<properties><java.version>17</java.version><!--  Spring AI的版本信息  --><spring-ai.version>0.8.0-SNAPSHOT</spring-ai.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- 使用OpenAI --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId><version>${spring-ai.version}</version></dependency><!-- 使用PGVector作为向量数据库 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId><version>${spring-ai.version}</version></dependency><!-- 引入PDF解析器 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-pdf-document-reader</artifactId><version>${spring-ai.version}</version></dependency></dependencies><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository></repositories>

2.配置API,key,Pgvector:
spring ai支持的大模型比较多,也可以用ollama里面的模型

server:port:8801spring:ai:openai:base-url: https://api.example.com
      api-key: sk-aec103e6cfxxxxxxxxxxxxxxxxxxxxxxx71da57a

  datasource:username: postgres
    password: postgres
    url: jdbc:postgresql://localhost/vector_store
3.3 创建VectorStore和文本分割器TokenTextSplitter
packagecom.ningning0111.vectordatabasedemo.config;importorg.springframework.ai.embedding.EmbeddingClient;importorg.springframework.ai.transformer.splitter.TokenTextSplitter;importorg.springframework.ai.vectorstore.PgVectorStore;importorg.springframework.ai.vectorstore.VectorStore;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.jdbc.core.JdbcTemplate;@ConfigurationpublicclassApplicationConfig{/**
     * 向量数据库进行检索操作
     * @param embeddingClient
     * @param jdbcTemplate
     * @return
     */@BeanpublicVectorStorevectorStore(EmbeddingClient embeddingClient,JdbcTemplate jdbcTemplate){returnnewPgVectorStore(jdbcTemplate,embeddingClient);}/**
     * 文本分割器
     * @return
     */@BeanpublicTokenTextSplittertokenTextSplitter(){returnnewTokenTextSplitter();}}
3.4 构建PDF存储服务层

service层

下创建一个名为

PdfStoreService

的类,用于将

PDF文件

存储到

向量数据库

中。

packagecom.ningning0111.vectordatabasedemo.service;importlombok.RequiredArgsConstructor;importorg.springframework.ai.reader.ExtractedTextFormatter;importorg.springframework.ai.reader.pdf.PagePdfDocumentReader;importorg.springframework.ai.reader.pdf.ParagraphPdfDocumentReader;importorg.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;importorg.springframework.ai.transformer.splitter.TokenTextSplitter;importorg.springframework.ai.vectorstore.VectorStore;importorg.springframework.core.io.DefaultResourceLoader;importorg.springframework.core.io.FileSystemResource;importorg.springframework.core.io.Resource;importorg.springframework.stereotype.Service;importorg.springframework.web.multipart.MultipartFile;importjava.io.IOException;importjava.nio.file.Files;importjava.nio.file.Path;@Service@RequiredArgsConstructorpublicclassPdfStoreService{privatefinalDefaultResourceLoader resourceLoader;privatefinalVectorStore vectorStore;privatefinalTokenTextSplitter tokenTextSplitter;/**
     * 根据PDF的页数进行分割
     * @param url
     */publicvoidsaveSourceByPage(String url){// 加载资源,需要本地路径的信息Resource resource = resourceLoader.getResource(url);// 加载PDF文件时的配置对象PdfDocumentReaderConfig loadConfig =PdfDocumentReaderConfig.builder().withPageExtractedTextFormatter(newExtractedTextFormatter
                                .Builder().withNumberOfBottomTextLinesToDelete(3).withNumberOfTopPagesToSkipBeforeDelete(1).build()).withPagesPerDocument(1).build();PagePdfDocumentReader pagePdfDocumentReader =newPagePdfDocumentReader(resource, loadConfig);// 存储到向量数据库中
        vectorStore.accept(tokenTextSplitter.apply(pagePdfDocumentReader.get()));}/**
     * 根据PDF的目录(段落)进行划分
     * @param url
     */publicvoidsaveSourceByParagraph(String url){Resource resource = resourceLoader.getResource(url);PdfDocumentReaderConfig loadConfig =PdfDocumentReaderConfig.builder().withPageExtractedTextFormatter(newExtractedTextFormatter
                                .Builder().withNumberOfBottomTextLinesToDelete(3).withNumberOfTopPagesToSkipBeforeDelete(1).build()).withPagesPerDocument(1).build();ParagraphPdfDocumentReader pdfReader =newParagraphPdfDocumentReader(
                resource,
                loadConfig
        );
        vectorStore.accept(tokenTextSplitter.apply(pdfReader.get()));}/**
     * MultipartFile对象存储,采用PagePdfDocumentReader
     * @param file
     */publicvoidsaveSource(MultipartFile file){try{// 获取文件名String fileName = file.getOriginalFilename();// 获取文件内容类型String contentType = file.getContentType();// 获取文件字节数组byte[] bytes = file.getBytes();// 创建一个临时文件Path tempFile =Files.createTempFile("temp-", fileName);// 将文件字节数组保存到临时文件Files.write(tempFile, bytes);// 创建一个 FileSystemResource 对象Resource fileResource =newFileSystemResource(tempFile.toFile());PdfDocumentReaderConfig loadConfig =PdfDocumentReaderConfig.builder().withPageExtractedTextFormatter(newExtractedTextFormatter
                                    .Builder().withNumberOfBottomTextLinesToDelete(3).withNumberOfTopPagesToSkipBeforeDelete(1).build()).withPagesPerDocument(1).build();PagePdfDocumentReader pagePdfDocumentReader =newPagePdfDocumentReader(fileResource, loadConfig);
            vectorStore.accept(tokenTextSplitter.apply(pagePdfDocumentReader.get()));}catch(IOException e){
            e.printStackTrace();}}}
3.5 构建对话服务

创建

ChatService

类,该类提供了

两种对话方式

不进行检索

的普通对话模式和

对向量数据库进行检索

的对话模式

packagecom.ningning0111.vectordatabasedemo.service;importlombok.RequiredArgsConstructor;importorg.springframework.ai.chat.ChatClient;importorg.springframework.ai.chat.ChatResponse;importorg.springframework.ai.chat.messages.Message;importorg.springframework.ai.chat.messages.UserMessage;importorg.springframework.ai.chat.prompt.Prompt;importorg.springframework.ai.chat.prompt.SystemPromptTemplate;importorg.springframework.ai.document.Document;importorg.springframework.ai.vectorstore.VectorStore;importorg.springframework.stereotype.Service;importjava.util.List;importjava.util.Map;importjava.util.stream.Collectors;@Service@RequiredArgsConstructorpublicclassChatService{// 系统提示词privatefinalstaticStringSYSTEM_PROMPT="""
            你需要使用文档内容对用户提出的问题进行回复,同时你需要表现得天生就知道这些内容,
            不能在回复中体现出你是根据给出的文档内容进行回复的,这点非常重要。
            
            当用户提出的问题无法根据文档内容进行回复或者你也不清楚时,回复不知道即可。
                    
            文档内容如下:
            {documents}
            
            """;privatefinalChatClient chatClient;privatefinalVectorStore vectorStore;// 简单的对话,不对向量数据库进行检索publicStringsimpleChat(String userMessage){return chatClient.call(userMessage);}// 通过向量数据库进行检索publicStringchatByVectorStore(String message){// 根据问题文本进行相似性搜索List<Document> listOfSimilarDocuments = vectorStore.similaritySearch(message);// 将Document列表中每个元素的content内容进行拼接获得documentsString documents = listOfSimilarDocuments.stream().map(Document::getContent).collect(Collectors.joining());// 使用Spring AI 提供的模板方式构建SystemMessage对象Message systemMessage =newSystemPromptTemplate(SYSTEM_PROMPT).createMessage(Map.of("documents", documents));// 构建UserMessage对象UserMessage userMessage =newUserMessage(message);// 将Message列表一并发送给ChatGPTChatResponse rsp = chatClient.call(newPrompt(List.of(systemMessage, userMessage)));return rsp.getResult().getOutput().getContent();}}
3.6 构建ChatController提供对话接口:
packagecom.ningning0111.vectordatabasedemo.controller;importcom.ningning0111.vectordatabasedemo.service.ChatService;importlombok.RequiredArgsConstructor;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;@RestController@RequiredArgsConstructor@RequestMapping("/api/v1/chat")publicclassChatController{privatefinalChatService chatService;@GetMapping("/simple")publicStringsimpleChat(@RequestParamString message
    ){return chatService.simpleChat(message);}@GetMapping("/")publicStringchat(@RequestParamString message
    ){return chatService.chatByVectorStore(message);}}
3.7 构建PdfUploadController提供了上传文件并保存到向量数据库中接口
packagecom.ningning0111.vectordatabasedemo.controller;importcom.ningning0111.vectordatabasedemo.service.PdfStoreService;importlombok.RequiredArgsConstructor;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.multipart.MultipartFile;@Controller@RequestMapping("/api/v1/pdf")@RequiredArgsConstructorpublicclassPdfUploadController{privatefinalPdfStoreService pdfStoreService;@PostMapping("/upload")publicvoidupload(@RequestParamMultipartFile file
    ){
        pdfStoreService.saveSource(file);}}

https://github.com/NingNing0111/vector-database-demo


本文转载自: https://blog.csdn.net/weixin_57128596/article/details/137361085
版权归原作者 Fairy要carry 所有, 如有侵权,请联系我们删除。

“Spring AI使用向量数据库实现检索AI对话”的评论:

还没有评论