springAI + pgvector + zhipu 实现 RAG

 近期由于工作需要,研究了一下使用springAI实现RAG,网上查找资料要么配置不全,要么没有讲到环境搭建的内容,所以写一篇记录一下完整过程,希望能够帮到其他开发者。文末附完整代码。

一、安装 Postgres PGVector数据库

PGVector:PGVector是基于PostgreSQL的向量化的扩展插件。PGVector为PostgreSQL添加了一个新的数据类型:vector用于高纬向量的存储,并支持检索和计算。PGVector特别适用于推荐系统、图像检索、自然语言处理等需要向量相似度计算和向量检索的场景。

1、相关环境及工具:

    操作系统:Windows 10

    PostgreSQL版本:16(pg的安装非常简单,网上一搜一大把,此处略过)

    PGVector版本:vector-0.7.3(安装包从官网下载即可)

    工具:安装PgVector需要用到VScode相关工具(vcvars64.bat、nmake)

    vcvars64.bat位置:D:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat

    nmake.exe位置:D:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64\nmake.exe

2、安装步骤

1、以管理员身份运行cmd.exe(最好使用管理员身份运行,否则可能出现文件夹无权限访问的情况,导致安装失败)。

2、执行命令:call "D:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" 。

3、进入PGVector解压所在目录:cd G:\PostgreSQL\vector-0.7.3 。

4、设置环境变量:set "PGROOT=G:\PostgreSQL\16"(PostgreSQL根目录)

5、执行编译命令:nmake /F Makefile.win;(nmake命令如果执行失败,需要将命令所在位置配置到系统环境变量PATH中;D:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64)

6、执行安装命令:nmake /F Makefile.win install

安装完成后可以看到vector安装包中相关sql脚本已经复制到PG的extension目录下:

图片

7、创建数据库:打开psql客户端创建测试库vcs

图片

 

二、编写测试代码

下面演示使用spingAI框架,将一个markdown文档向量存储到PGVector后,结合文档内容,调用智谱API进行RAG问答的代码示例。springboot版本:3.4.2;Java版本:17。

1、maven依赖配置:

maven依赖配置:
    <properties>
        <java.version>17</java.version>
        <spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version>
    </properties>
        <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-markdown-document-reader</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <repositories>
        <repository>
            <id>spring-snapshot</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>

2、代码示例

application.yml配置:


server:
  port: 8180
spring:
  application:
    name: springAI
  ai:
    zhipuai:
#      https://www.bigmodel.cn/usercenter/proj-mgmt/apikeys   智谱开放平台
      api-key: 填写自己的
      base-url: https://open.bigmodel.cn/api/paas/
    vectorstore:
      pgvector:
        initialize-schema: true
        index-type: hnsw
  datasource:
    username: postgres
    password: 123456
    url: jdbc:postgresql://localhost:5432/vcs

注意:一定要将spring.ai.vectorstore.pgvector.initialize-schema配置为true

这样的话,springAI在启动期间会执行以下命令自动启用vector所需的扩展。通过以下命令可以看到,创建了vector相关的extension、一个名为vector_store的表,并创建了表的索引,向量化后的数据就存储在vector_store表中。

其中embedding的字段类型是vector(1536),就是上面我们提到的新的字段类型。


CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE IF NOT EXISTS vector_store (
  id uuid DEFAULT uuid_generate_v4() PRIMARY KEY,
  content text,
  metadata json,
  embedding vector(1536) // 1536 is the default embedding dimension
);

CREATE INDEX ON vector_store USING HNSW (embedding vector_cosine_ops);

上传markdown文档接口,进行向量化

FileController 文件上传接口


@Autowired
private FileService fileService;

@RequestMapping("/upload/md")
public void uploadMd(MultipartFile file) throws IOException {
    fileService.saveResourceMarkDown(file);
}

FileService 将文档内容存储到vector中


public void saveResourceMarkDown(MultipartFile file) throws IOException {
    String fileName= file.getOriginalFilename();
    Path tempFile = Files.createTempFile("temp-", fileName);
    Files.write(tempFile, file.getBytes());
    Resource fileResource = new FileSystemResource(tempFile.toFile());
    MarkdownDocumentReaderConfig loadConfig = MarkdownDocumentReaderConfig.builder().build();
    MarkdownDocumentReader markdownDocumentReader=new MarkdownDocumentReader(fileResource, loadConfig);
    vectorStore.accept(tokenTextSplitter.apply(markdownDocumentReader.get()));
}

RAG问答示例

ZhipuRagController 



@Autowired
private RagService ragService;

@RequestMapping("/chat")
public String chat(String message) {
    return ragService.chatByVectorStore(message);
}

RagService

private final static String SYSTEM_PROMPT = """
        你需要使用文档内容对用户提出的问题进行回复,同时你需要表现得天生就知道这些内容,
        不能在回复中体现出你是根据给出的文档内容进行回复的,这点非常重要。

        当用户提出的问题无法根据文档内容进行回复或者你也不清楚时,回复不知道即可。

        文档内容如下:
        {documents}

        """;

@Autowired
private ChatModel chatModel;
@Autowired
private VectorStore vectorStore;

public String chatByVectorStore(String message) {
    List<Document> listOfSimilarDocuments = vectorStore.similaritySearch(message);
    assert listOfSimilarDocuments != null;
    String documents = listOfSimilarDocuments.stream().map(Document::getText).collect(Collectors.joining());
    Message systemMessage = new SystemPromptTemplate(SYSTEM_PROMPT).createMessage(Map.of("documents", documents));
    UserMessage userMessage = new UserMessage(message);
    ChatResponse rsp = chatModel.call(new Prompt(List.of(systemMessage, userMessage)));
    return rsp.getResult().getOutput().getText();
}

三、运行效果

项目启动:启动后去数据库中查询表,可以看到vcs库下有一张vector_store的表,且表结构如前面提到的创建命令一致。

图片

图片

文件上传:

调用文件上传接口:http://localhost:8180/file/upload/md

文件上传完成后,可以看到vector_store表中已经有数据了

图片

图片

问答验证:

调用问答接口:http://localhost:8180/file/upload/md

当我们问有哪些交通规则时,可以看到从文档中找到了答案。

图片

 

 

当我们问公积金有哪些规则时,可以看到没有找到答案。

图片

四、结语

到此,从环境搭建到效果演示全部完成,如需获取完整代码,关注公众号【智界跬步】,回复消息:RAGDEMO。

 

### SpringAI 集成智谱 API 方法 为了使应用程序能够集成并调用智谱提供的大模型API,可以通过引入相应的SDK依赖项以及编写必要的配置文件和服务类来完成这一过程。对于Spring AI而言,由于其设计之初就考虑到了多个人工智能服务商的支持,因此在实际操作过程中只需要遵循官方文档指导即可轻松实现与智谱平台之间的对接。 #### 添加 Maven 依赖 首先,在项目的`pom.xml`文件中加入智谱开放平台所提供的Java SDK作为项目依赖: ```xml <dependency> <groupId>cn.zhipu.openapi</groupId> <artifactId>zhipu-oapi-java-sdk</artifactId> <version>latest-version</version> </dependency> ``` 请注意替换上述代码中的`latest-version`为最新版本号[^5]。 #### 创建配置类 接着创建一个新的Java类用于加载来自环境变量或application.properties/yml的应用程序属性,并初始化ZhiPuClient实例以便后续发起请求时使用: ```java @Configuration public class ZhiPuConfig { @Value("${zhipu.api.key}") private String apiKey; @Bean public ZhiPuClient zhiPuClient() { return new ZhiPuClient(apiKey); } } ``` 这里假设已经在外部资源文件里定义好了名为`zhipu.api.key`的关键字对应着有效的API密钥字符串[^2]。 #### 编写服务层逻辑 最后一步就是构建具体业务场景下的处理流程了。比如当需要向用户提供自动回复功能的时候就可以这样做: ```java @Service public class ChatService { private final ZhiPuClient client; @Autowired public ChatService(ZhiPuClient client){ this.client = client; } public String getResponse(String message) throws Exception{ // 构建请求体对象 Map<String, Object> body = new HashMap<>(); body.put("prompt", message); // 发送POST请求给智谱服务器获取响应数据 Response response = client.post("/v1/chat/completions", body); // 解析返回的结果集提取有用的信息部分 JSONObject jsonResult = (JSONObject)new JSONParser().parse(response.body()); return jsonResult.get("choices").toString(); } } ``` 这段简单的例子展示了怎样借助于之前准备好的客户端工具去发送消息至远程主机等待答复再将其展示出来[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值