【Spring AI】Java实现类似langchain的向量数据库RAG_原理与具体实践

发布于:2024-10-13 ⋅ 阅读:(33) ⋅ 点赞:(0)

介绍一下RAG:

检索增强生成(RAG)是一种技术,它结合了检索模型和生成模型来提高文本生成的质量。通过从企业私有或专有的数据源中检索相关信息,并将这些信息与大型语言模型相结合,RAG能够显著减少模型产生幻觉的情况,同时使回答更加精确、相关。这解决了大模型在缺乏特定上下文或最新数据时提供的答案不够准确的问题。

向量数据库在Rag中的具体作用说明:

在检索增强生成(RAG)过程中,向量数据库起着核心作用。首先,原始文档或数据通过Embedding模型转化为高维向量,随后这些向量被存储于向量数据库中。当有新的查询请求时,系统将该查询也转换为相应的向量,并在向量数据库中查找最相似的向量以获取相关上下文信息。使用向量数据库的原因在于其能高效地处理大规模高维向量的存储与检索问题,支持快速精确匹配,这对于提高RAG系统的响应速度及准确性至关重要。此外,向量数据库还能够很好地支持非结构化数据的管理,使得从大量文本或其他类型的内容中提取有价值的信息成为可能。

Spring AI alibaba 是什么:

Spring AI Alibaba 是基于 Spring Boot 的应用框架,专为 Java 开发者设计,用于集成和利用阿里云的通义大模型等AI服务。其核心优势在于提供了标准化接口,使得开发者能够以一致的方式访问不同AI供应商(如OpenAI、Azure、阿里云)的服务,只需更改配置即可切换不同的AI实现。这极大简化了开发流程,减少了因适配不同API而带来的工作量。此外,Spring AI Alibaba 还支持多种生成式任务,如对话、文生图、语音合成,并且与 Flux 流输出兼容,方便构建复杂的AI应用。通过Spring Boot的自动装配机制,可以轻松地在现有项目中添加强大的AI能力。

详细实践:

检索增强的后端代码编写

为了实现基于检索增强生成 (RAG) 的功能,以读取阿里巴巴财务报表PDF并提供相应的服务,我们首先需要理解如何配置Spring AI Alibaba来支持RAG能力。根据提供的我了解的信息,这涉及到一系列的准备工作和具体的代码实现步骤。

1. 环境准备

  • JDK版本:确保你的开发环境使用的是JDK 17或更高版本。
  • Spring Boot版本:项目需基于Spring Boot 3.3.x及以上版本构建。
  • 阿里云账号与API Key获取
    • 服务开通后,在用户中心创建一个新的API Key,并妥善保存用于后续配置。

2. 配置阿里云通义千问 API Key

在你的应用程序环境中设置AI_DASHSCOPE_API_KEY环境变量:

export AI_DASHSCOPE_API_KEY=您的实际API密钥

并在application.properties文件中添加如下配置项来引用这个环境变量:

spring.ai.dashscope.api-key: ${AI_DASHSCOPE_API_KEY}

3. 添加必要的依赖库

由于Spring AI Alibaba目前处于早期阶段,其Maven仓库尚未正式发布所有相关组件,因此我们需要指定额外的仓库地址以便能够下载所需的库。请将以下仓库配置加入到你的pom.xml文件内:

<repositories>
    <repository>
        <id>sonatype-snapshots</id>

        <url>https://oss.sonatype.org/content/repositories/snapshots</url>

        <snapshots>
            <enabled>true</enabled>

        </snapshots>

    </repository>

    <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>

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>

        <artifactId>spring-ai-alibaba-starter</artifactId>

        <version>1.0.0-M2</version>

    </dependency>

    <!-- 其他必要的依赖 -->
</dependencies>


4. RAG服务实现

接下来是核心的服务逻辑实现,包括向量存储、文档检索等关键组件的定义。我们将创建一个名为RagService的服务类,它负责处理从PDF提取内容、建立索引以及最终基于查询条件进行响应的过程。

创建RagService类
public class RagService {
    // ...省略了构造器和其他成员变量声明...
    
    public String buildIndex() {
        String filePath = "阿里巴巴财报.pdf";
        DocumentReader reader = new DashScopeDocumentCloudReader(filePath, dashscopeApi, null);
        List<Document> documentList = reader.get();
        vectorStore.add(documentList);
        return "索引构建完成";
    }
    
    public StreamResponseSpec queryWithDocumentRetrieval(String message) {
        StreamResponseSpec response = chatClient.prompt().user(message)
                .advisors(new DocumentRetrievalAdvisor(retriever, DEFAULT_USER_TEXT_ADVISE)).stream();
        return response;
    }
}
提供REST接口

最后一步是在Controller层暴露两个端点,一个是用来初始化索引的/buildIndex,另一个则是实际对外提供查询服务的/ragChat

@RestController
@RequestMapping("/ai")
public class RagController {
    @Autowired
    private RagService ragService;

    @GetMapping("/buildIndex")
    public String buildIndex() {
        return ragService.buildIndex();
    }

    @GetMapping("/ragChat")
    public Flux<String> generate(@RequestParam("input") String input, HttpServletResponse httpResponse) {
        StreamResponseSpec chatResponse = ragService.queryWithDocumentRetrieval(input);
        httpResponse.setCharacterEncoding("UTF-8");
        return chatResponse.content();
    }
}

以上就是通过检索增强技术实现读取PDF文件并提供问答服务的整体流程。此方案利用了Spring AI Alibaba提供的RAG框架,并结合阿里云的大规模语言模型能力,实现了高效的知识检索与自然语言处理。在实际部署前,请确保所有依赖均已被正确添加至项目,并且已按照要求设置了正确的环境变量及属性配置。

检索增强的前端代码编写

检索增强的前端代码编写

根据我了解的信息中提供的关于基于React构建支持流输出的前端项目的内容,我们可以按照类似的步骤来构建一个能够与后端http://localhost:8080/ai/ragChat?input=…接口交互,并处理返回的flux<String>数据流的前端应用。下面将具体阐述如何搭建这样的项目。

构建项目并填写代码

首先,我们需要创建一个新的React应用,并确保安装所有必需的依赖项。这可以通过运行以下命令实现:

npx create-react-app ragchat-frontend
cd ragchat-frontend
npm install

接下来,我们将依次设置或修改几个关键文件以满足项目需求。

public/index.html

无需特别改动此文件,保持默认内容即可。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>RAG Chat App</title>

</head>

<body>
  <div id="root"></div>

</body>

</html>
src/index.js

同样地,这个文件基本可以保持原样,仅用于渲染应用入口组件App

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
src/App.js

这里是主应用程序组件,我们将其简单指向我们的聊天组件RAGChatComponent

import React from 'react';
import RAGChatComponent from './components/RAGChatComponent';

function App() {
  return (
    <div className="App">
      <RAGChatComponent />
    </div>

  );
}

export default App;
src/components/RAGChatComponent.js

这是核心功能所在的地方。我们需要在此处添加逻辑来发送请求到指定的URL,并且处理从服务器接收到的数据流。

import React, { useState } from 'react';

function RAGChatComponent() {
  const [input, setInput] = useState('');
  const [messages, setMessages] = useState('');

  const handleInputChange = (event) => {
    setInput(event.target.value);
  };

  const handleSendMessage = async () => {
    try {
      const response = await fetch(`http://localhost:8080/ai/ragChat?input=${input}`);
      if (!response.ok) throw new Error("Network response was not ok");
      
      const reader = response.body.getReader();
      const decoder = new TextDecoder('utf-8');
      let done = false;

      while (!done) {
        const { value, done: readerDone } = await reader.read();
        done = readerDone;
        const chunk = decoder.decode(value, { stream: true });
        setMessages((prevMessages) => prevMessages + chunk);
      }

      // 请求完成后添加换行符
      setMessages((prevMessages) => prevMessages + '\n\n=============================\n\n');
    } catch (error) {
      console.error('Failed to fetch', error);
    }
  };

  const handleClearMessages = () => {
    setMessages('');
  };

  return (
    <div>
      <input
        type="text"
        value={input}
        onChange={handleInputChange}
        placeholder="Enter your message"
      />
      <button onClick={handleSendMessage}>Send</button>

      <button onClick={handleClearMessages}>Clear</button>

      <div>
        <h3>Messages:</h3>

        <pre>{messages}</pre>

      </div>

    </div>

  );
}

export default RAGChatComponent;

通过以上步骤,你已经成功配置了一个简单的React应用,该应用能够向给定的后端服务发送请求,并实时显示返回的文本流。请确保你的后端服务正在本地运行于http://localhost:8080,并且允许来自前端的跨域请求(CORS)。如果需要进一步定制样式或其他功能,可以根据实际需求调整上述代码。