feat: RAG

This commit is contained in:
zouzhiwen 2026-04-01 19:04:38 +08:00
parent f5b0e16c94
commit bf47df6ac6
5 changed files with 128 additions and 17 deletions

19
pom.xml
View File

@ -77,12 +77,25 @@
</dependency> </dependency>
<!--mysql驱动--> <!-- PostgreSQL聊天记忆 JDBC + pgvector 共用同一数据源 -->
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>org.postgresql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>postgresql</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
<version>1.1.2</version>
</dependency>
<!-- 向量数据库 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,14 +1,19 @@
package com.baoshi.conf; package com.baoshi.conf;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.spec.DashScopeModel;
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository; import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.model.ollama.autoconfigure.OllamaChatProperties; import org.springframework.ai.model.ollama.autoconfigure.OllamaChatProperties;
import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration @Configuration
public class AiConfig { public class AiConfig {
@ -16,7 +21,7 @@ public class AiConfig {
@Bean @Bean
public ChatMemory chatMemory(JdbcChatMemoryRepository chatMemoryRepository) { public ChatMemory chatMemory(JdbcChatMemoryRepository chatMemoryRepository) {
return MessageWindowChatMemory.builder() return MessageWindowChatMemory.builder()
.maxMessages(1) .maxMessages(3)
.chatMemoryRepository(chatMemoryRepository) .chatMemoryRepository(chatMemoryRepository)
.build(); .build();
} }
@ -37,4 +42,10 @@ public class AiConfig {
.defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build()) .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build())
.build(); .build();
} }
@Primary
@Bean
public EmbeddingModel embeddingModel(@Autowired @Qualifier("ollamaEmbeddingModel") EmbeddingModel embeddingModel) {
return embeddingModel;
}
} }

View File

@ -5,6 +5,8 @@ spring:
base-url: http://100.121.13.117:11434 base-url: http://100.121.13.117:11434
chat: chat:
model: qwen2.5:7b model: qwen2.5:7b
embedding:
model: bge-m3
dashscope: dashscope:
api-key: sk-36a57382b9ae4db696ecd07eb7150a88 api-key: sk-36a57382b9ae4db696ecd07eb7150a88
chat: chat:
@ -14,11 +16,18 @@ spring:
repository: repository:
jdbc: jdbc:
initialize-schema: always initialize-schema: always
vectorstore:
pgvector:
initialize-schema: true
index-type: HNSW
distance-type: COSINE_DISTANCE
datasource: datasource:
username: root username: postgres
password: 123456 password: postgres
url: jdbc:mysql://localhost:3306/springai?characterEncoding=utf8&useSSL=false&serverTimezone=UTC& url: jdbc:postgresql://localhost:5432/springai
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: org.postgresql.Driver
server: server:
port: 8080 port: 8080

View File

@ -6,6 +6,7 @@ import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository; import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -23,14 +24,12 @@ class JDBCMemoryTest {
private static final String CONVERSATION_ID = "1"; private static final String CONVERSATION_ID = "1";
@Autowired @Autowired
private ChatClient ollama; @Qualifier("ollama")
private ChatClient chatClient;
@Autowired @Autowired
private ChatMemory chatMemory; private ChatMemory chatMemory;
@Autowired
private JdbcChatMemoryRepository chatMemoryRepository;
@Autowired @Autowired
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
@ -41,20 +40,23 @@ class JDBCMemoryTest {
@Test @Test
void shouldPersistMessagesIntoJdbcRepositoryByChatModelConversation() { void shouldPersistMessagesIntoJdbcRepositoryByChatModelConversation() {
ollama.prompt() chatClient.prompt()
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, CONVERSATION_ID)) .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, CONVERSATION_ID))
.user("我叫邹志文") .user("我叫邹志文")
.call() .call()
.content(); .content();
chatClient.prompt()
String secondResponse = ollama.prompt() .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, CONVERSATION_ID))
.user("她叫冬马和纱")
.call()
.content();
String secondResponse = chatClient.prompt()
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, CONVERSATION_ID)) .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, CONVERSATION_ID))
.user("我叫什么") .user("我叫什么")
.call() .call()
.content(); .content();
assertThat(secondResponse).isNotBlank(); System.out.println(secondResponse);
assertThat(chatMemoryRepository.findByConversationId(CONVERSATION_ID)).isNotEmpty();
Integer rowCount = jdbcTemplate.queryForObject( Integer rowCount = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ?", "SELECT COUNT(*) FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ?",
Integer.class, Integer.class,

View File

@ -0,0 +1,76 @@
package com.baoshi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
@SpringBootTest
public class SimpleVectorStoreTest {
@BeforeEach
public void init( @Autowired
VectorStore vectorStore) {
// 1. 声明内容文档
Document doc = Document.builder()
.text("""
预订航班:
- 通过我们的网站或移动应用程序预订
- 预订时需要全额付款
- 确保个人信息姓名ID 的准确性因为更正可能会产生 25 的费用
""")
.build();
Document doc2 = Document.builder()
.text("""
取消预订:
- 最晚在航班起飞前 48 小时取消
- 取消费用经济舱 75 美元豪华经济舱 50 美元商务舱 25 美元
- 退款将在 7 个工作日内处理
""")
.build();
// 2. 将文本进行向量化并且存入向量数据库无需再手动向量化
vectorStore.add(Arrays.asList(doc,doc2));
}
@Test
void chatRagTest(
@Autowired
VectorStore vectorStore,
@Autowired
@Qualifier("ollama")
ChatClient chatClient
) {
String message="退费需要多少费用?";
String content = chatClient.prompt().user(message)
.advisors(
new SimpleLoggerAdvisor(),
QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(
SearchRequest
.builder().query(message)
.topK(5)
.similarityThreshold(0.3)
.build())
.build()
).call().content();
System.out.println(content);
}
}