“ 上篇文章我们了解了springAI+zhipu+pgvector实现RAG,从环境搭建到代码编写再到效果演示的完整过程。考虑到近期DeepSeek大火,很多开发者需要对接DeepSeek,所以,这篇文章,将带大家使用springAI+DeepSeek+zhipuEmbedding+pgvector实现RAG,希望能够帮到有需要的开发者。”
如需获取完整代码,移步公众号【智界跬步】,给他发消息:DEEPSEEKRAG。
RAG的实现过程
RAG:检索增强生成的缩写,结合了检索和生成两种技术,用于提升生成模型的效果,特别适用于需要外部知识的场景。
为什么需要RAG:
1、减少幻觉:依赖检索结果生成,降低模型编造信息的风险。
2、动态知识库:支持实时更新知识库,无需重新训练模型。
3、领域适配:通过定制知识库快速适配垂直场景。
RAG的实现过程:
1、文档分割:可按页、段落、token等方式进行分割。
2、文档向量化:使用嵌入模型将文本转为向量表示。
3、向量存储:将文档的向量表示存储到向量数据库中。
4、问题向量化:用户提出问题,嵌入模型生成问题对应的向量。
5、向量检索:通过向量数据库对问题进行相似性搜索,找到相关文档。
6、增强提示:将检索到的相关上下文信息添加到用户的问题提示中。
7、生成答案:基础模型基于增强后的提示生成最终回答。
代码实现
环境说明:
操作系统:Windows 10
PostgreSQL版本:PostgreSQL16
PGVector版本:
vector-0.7.3(如需安装过程,请看上篇文章)
嵌入模型:智谱Embedding(https://open.bigmodel.cn/api/paas)
基础模型:deepseek-ai/DeepSeek-R1-Distill-Qwen-14B(硅基流动:https://api.siliconflow.cn)
springAI版本:1.0.0-SNAPSHOT
springboot版本:3.4.2
Java版本:17
模型说明:
访问spring官网可以看到spring框架是通过OpenAI API的方式与DeepSeek集成。
在Chat Models下可以找到对DeepSeek的支持,但在Embedding Modes下是没有DeepSeek的,所以本文中对文档的向量化处理还是使用智谱的嵌入模型。
maven依赖配置:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
代码示例
application.yml配置:
server:
port: 8180
spring:
application:
name: springAI-DeepSeek
ai:
openai:
api-key: 填自己的
base-url: https://api.siliconflow.cn
chat:
options:
model: DeepSeek-R1-Distill-Qwen-14B
vectorstore:
pgvector:
index-type:
initialize-schema: true
zhipuai:
api-key: 填自己的
datasource:
username: postgres
password: 123456
url: jdbc:postgresql://localhost:5432/vcs
DsConfiguration.java:
定义embeddingModel、chatModel,并指定主bean,需要手动指定OpenAiApi和ZhiPuAiApi的baseUrl和apiKey,本文中使用的模型是硅基流动官网的DeepSeek-R1-Distill-Qwen-14B模型。此外,还需定义向量存储相关bean。
@Bean
@Primary
public EmbeddingModel embeddingModel() {
return new ZhiPuAiEmbeddingModel(new ZhiPuAiApi("填写自己的key"));
}
@Bean
@Primary
public ChatModel chatModel() {
OpenAiApi openAiApi = new OpenAiApi("https://api.siliconflow.cn", "填写自己的key");
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("deepseek-ai/DeepSeek-R1-Distill-Qwen-14B")
.build();
return new OpenAiChatModel(openAiApi, options);
}
FileController.java:
提供上传文档接口,并负责将文档转为向量表示,存储到向量数据库中。
@RequestMapping("/upload/pdf")
public void uploadPdf(MultipartFile file) {
fileService.saveSource(file);
}
@RequestMapping("/upload/md")
public void uploadMd(MultipartFile file) throws IOException {
fileService.saveResourceMarkDown(file);
}
ChatController.java: 提供RAG文档对话接口。
@RequestMapping("/rag/chat")
public String ragChat(String message) {
return ragService.chatByVectorStore(message);
}
RagService.java: 实现向量检索、系统提示词设置、基础模型调用。
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();
}
运行效果
首先,调用FileController接口,上传一个介绍交通规则的markdown文档。
然后,调用ChatController接口,进行文档内容相关提问。
问题1:什么是交通规则。可以看到从文档中找到了答案,并给出了DeepSeek的思考过程。
问题2:什么是公积金规则。由于我们知识库中没有公积金相关的文档,所以可以看到DeepSeek无法回答,并可以看到它的思考过程。