1.如何把ChatClient集成向量数据库SimpleVectorStore并实现RAG?
1.引入向量数据库和向量库拦截器依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vector-store</artifactId>
</dependency>
<!--向量模型接入chatClient时引入-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
2.yml配置向量模型
yml
spring:
ai:
ollama:
# 想看请求过程,搜索:OllamaApi,DashScopeApi,然后搜索:restClient.post().uri,找到的方法里包含chat的就是content的请求,包含chat和stream的都是stream请求
base-url: http://localhost:11434
embedding:
options:
# quentinz/bge-base-zh-v1.5:latest(默认维度:768),nomic-embed-text:latest(默认维度:768),bge-m3:latest(默认维度为:1024),mxbai-embed-large:latest(默认维度为:1024)
model: mxbai-embed-large:latest
chat:
options:
model: deepseek-r1:8b
temperature: 0.7
3.配置向量数据库与实现RAG
java
/**
* RAG:SpringAI先调用存储向量,再调用模型进行问答
*/
@SpringBootTest
public class SimpleVectorStoreRagChatClient {
@TestConfiguration
static class TestConfig {
/**
* 1.配置向量库
*
* @param embeddingModel
* @return
*/
@Bean
public VectorStore vectorStore(OllamaEmbeddingModel embeddingModel) {
return SimpleVectorStore.builder(embeddingModel).build();
}
}
/**
* 2.向量库中初始化一些文档数据
*
* @param vectorStore
*/
@BeforeEach
public void init(@Autowired VectorStore vectorStore) {
Document document1 = Document.builder()
.text("""
预订航班:
- 通过我们的网站或移动应用程序预订。
- 预订时需要全额付款。
- 确保个人信息(姓名、ID等)的准确性,因为更正可能会产生25的费用。
""")
.build();
Document document2 = Document.builder()
.text("""
取消预订:
- 最晚在航班起飞前48小时取消。
- 取消费用:经济舱75美元,豪华经济舱50美元,商务舱25美元。
- 退款将在7个工作日内处理。
""")
.build();
//存储向量(内部会自动向量化)
vectorStore.add(List.of(document1, document2));
}
/**
* 3.RAG:SpringAI先调用存储向量,再调用模型进行问答
*
* @param vectorStore
* @param chatModel
*/
@Test
public void testRag(@Autowired VectorStore vectorStore,@Autowired OllamaChatModel chatModel) {
ChatClient chatClient = ChatClient.builder(chatModel).build();
String content = chatClient.prompt()
.system("你是一个智能助手")
.user("退票多少钱")
//配置一个日志拦截器,方便查看Rag查到内容
.advisors(SimpleLoggerAdvisor.builder().build(),
//向量数据库高级查询,一般都只调topK和similarityThreshold的阈值来让文档更精确
QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.topK(5)
.similarityThreshold(0.5)
.build()
).build()
)
.call()
.content();
System.out.println(content);
}
}
4.扩展:向量数据库的配置,新增数据,查询向量
这里主要想记录一下向量的简单查询,还有如何查看向量模型的维度是多少。
java
/**
* 向量数据库的配置,新增数据,查询向量
*/
@SpringBootTest
public class SimpleVectorStoreTest {
/**
* 向量数据库
* 1.SimpleVectorStore:适用于简单的向量数据库场景,例如:本地向量数据库、学习使用。
* 2.MilvusVectorStore:比较流行的向量数据库,支持分布式、高可用、海量数据存储。
* 3.ElasticsearchVectorStore: 如果项目中有es可以考虑使用。
*/
@TestConfiguration
static class TestConfig {
@Bean
public VectorStore vectorStore(OllamaEmbeddingModel embeddingModel) {
return SimpleVectorStore.builder(embeddingModel).build();
}
}
@Test
public void testEmbedding(@Autowired OllamaEmbeddingModel embeddingModel) {
/*
向量模型:
1.quentinz/bge-base-zh-v1.5:latest(默认维度:768),
2.nomic-embed-text:latest(默认维度:768),
3.bge-m3:latest(默认维度为:1024)
4.mxbai-embed-large:latest(默认维度为:1024)
*/
float[] embedding = embeddingModel.embed("hello world");
System.out.println("默认维度为:"+embedding.length);
}
@Test
public void testVectorStore(@Autowired VectorStore vectorStore) {
Document document1 = Document.builder()
.text("""
预订航班:
- 通过我们的网站或移动应用程序预订。
- 预订时需要全额付款。
- 确保个人信息(姓名、ID等)的准确性,因为更正可能会产生25的费用。
""")
.build();
Document document2 = Document.builder()
.text("""
取消预订:
- 最晚在航班起飞前48小时取消。
- 取消费用:经济舱75美元,豪华经济舱50美元,商务舱25美元。
- 退款将在7个工作日内处理。
""")
.build();
//存储向量(内部会自动向量化)
vectorStore.add(List.of(document1, document2));
//简单查询
// List<Document> similaritySearch = vectorStore.similaritySearch("退票");
// for (Document search : similaritySearch) {
// System.out.println(search.getText());
// System.out.println(search.getScore());
// }
//高级查询
SearchRequest searchRequest = SearchRequest.builder()
.query("退票")
.topK(5)
.similarityThreshold(0.5)
.build();
List<Document> documents = vectorStore.similaritySearch(searchRequest);
for (Document document : documents) {
System.out.println(document.getText());
System.out.println(document.getScore());
}
}
}