参考完成RAG技术
This commit is contained in:
@@ -16,7 +16,8 @@ import reactor.core.publisher.Flux;
|
||||
chatModel = "openAiChatModel",
|
||||
streamingChatModel = "openAiStreamingChatModel", //配置流式 输出模型
|
||||
// chatMemory = "chatMemory", // 配置聊天记忆对象 ,基于内存
|
||||
chatMemoryProvider = "chatMemoryProvider"
|
||||
chatMemoryProvider = "chatMemoryProvider",//配置会话记忆 提供者对象 , 基于Redis
|
||||
contentRetriever = "contentRetriever" // 配置向量数据库检索对象
|
||||
)
|
||||
public interface CstAIStreamingService {
|
||||
|
||||
@@ -25,6 +26,10 @@ public interface CstAIStreamingService {
|
||||
|
||||
@SystemMessage(fromResource = "system.txt")
|
||||
public Flux<String> chatMemoryId(@MemoryId String memoryId, @UserMessage String message);
|
||||
|
||||
|
||||
// @SystemMessage(fromResource = "system.txt")
|
||||
public Flux<String> chatMemoryIdRAG(@MemoryId String memoryId, @UserMessage String message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.cst.langchain4jheima.config;
|
||||
|
||||
import com.cst.langchain4jheima.aiservice.CstAIService;
|
||||
import com.cst.langchain4jheima.repository.RedisChatMemoryStore;
|
||||
import dev.langchain4j.community.store.embedding.redis.RedisEmbeddingStore;
|
||||
import dev.langchain4j.memory.ChatMemory;
|
||||
import dev.langchain4j.memory.chat.ChatMemoryProvider;
|
||||
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
|
||||
import dev.langchain4j.model.openai.OpenAiChatModel;
|
||||
import dev.langchain4j.service.AiServices;
|
||||
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -38,6 +41,12 @@ public class CommonConfig {
|
||||
return build;
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
RedisChatMemoryStore redisChatMemoryStore;
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
public ChatMemoryProvider chatMemoryProvider() {
|
||||
|
||||
@@ -48,6 +57,7 @@ public class CommonConfig {
|
||||
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
|
||||
.id(memoryId)
|
||||
.maxMessages(10)
|
||||
.chatMemoryStore(redisChatMemoryStore)
|
||||
.build();
|
||||
return chatMemory;
|
||||
}
|
||||
|
||||
@@ -65,4 +65,23 @@ public class ChatController
|
||||
|
||||
return chat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过 memoryId ,实现会话隔离
|
||||
* 会话信息保存在Redis中
|
||||
* 加载的本地文档保存在向量数据库
|
||||
* 基于向量数据库进行信息检索
|
||||
*
|
||||
* @param memoryId
|
||||
* @param question
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/chatByStreamingByMemoryIdByRAG" )
|
||||
public Flux<String> chatByStreamingByMemoryIdByRAG(String memoryId,String question)
|
||||
{
|
||||
Flux<String> chat = streamingModel.chatMemoryIdRAG(memoryId,question);
|
||||
|
||||
return chat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.cst.langchain4jheima.embedding;
|
||||
|
||||
import dev.langchain4j.community.store.embedding.redis.RedisEmbeddingStore;
|
||||
import dev.langchain4j.data.document.Document;
|
||||
import dev.langchain4j.data.document.DocumentSplitter;
|
||||
import dev.langchain4j.data.document.loader.ClassPathDocumentLoader;
|
||||
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
|
||||
import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
|
||||
import dev.langchain4j.data.document.splitter.DocumentSplitters;
|
||||
import dev.langchain4j.data.segment.TextSegment;
|
||||
import dev.langchain4j.model.embedding.EmbeddingModel;
|
||||
import dev.langchain4j.rag.content.retriever.ContentRetriever;
|
||||
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
|
||||
import dev.langchain4j.store.embedding.EmbeddingStore;
|
||||
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
|
||||
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
|
||||
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.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Author: 李中华 wx: spllzh email(qq): 28668817@qq.com
|
||||
* Date: 2025/8/4 18:21
|
||||
**/
|
||||
@Configuration
|
||||
public class EnbedingModelConfig {
|
||||
|
||||
|
||||
@Autowired
|
||||
private EmbeddingModel embeddingModel;
|
||||
|
||||
@Autowired
|
||||
RedisEmbeddingStore redisEmbeddingStore;
|
||||
/**
|
||||
* 创建向量数据库操作对象
|
||||
* @return
|
||||
*/
|
||||
@Bean("myEmbeddingStoreInMemory")
|
||||
// @Primary
|
||||
public EmbeddingStore embeddingStoreInMemory() {
|
||||
//1 , 加载知识库文档进 内存
|
||||
List<Document> documents = ClassPathDocumentLoader.loadDocuments("knowledge");
|
||||
//2 , 构建向量数据库操作对象
|
||||
InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
|
||||
//3, 构建EmbeddingStoreIngestor , 完成文本数据的切割, 向量化, 存储
|
||||
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
|
||||
.embeddingStore(embeddingStore)
|
||||
.build();
|
||||
ingestor.ingest( documents);
|
||||
return embeddingStore;
|
||||
}
|
||||
|
||||
@Bean("myEmbeddingStoreInMemory2")
|
||||
// @Primary
|
||||
public EmbeddingStore embeddingStoreInMemory2() throws IOException {
|
||||
//1 , 加载知识库文档进 内存
|
||||
List<Document> documents1 = ClassPathDocumentLoader.loadDocuments("knowledge\\pdf",new ApachePdfBoxDocumentParser());
|
||||
// List<Document> documents2 = ClassPathDocumentLoader.loadDocuments("knowledge");
|
||||
|
||||
//2 , 构建向量数据库操作对象
|
||||
InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
|
||||
|
||||
//构建文档 分割器对象
|
||||
DocumentSplitter recursiveSplitter = DocumentSplitters.recursive(300, 50);
|
||||
|
||||
//3, 构建EmbeddingStoreIngestor , 完成文本数据的切割, 向量化, 存储
|
||||
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
|
||||
.embeddingStore(embeddingStore)
|
||||
.documentSplitter(recursiveSplitter) //设置文档分割器
|
||||
.embeddingModel(embeddingModel)
|
||||
.build();
|
||||
ingestor.ingest( documents1);
|
||||
return embeddingStore;
|
||||
}
|
||||
|
||||
|
||||
@Bean("myEmbeddingStoreInRedis")
|
||||
// @Primary
|
||||
public EmbeddingStore embeddingStoreInRedis() throws IOException {
|
||||
//1 , 加载知识库文档进 内存
|
||||
List<Document> documents1 = ClassPathDocumentLoader.loadDocuments("knowledge\\pdf",new ApachePdfBoxDocumentParser());
|
||||
// List<Document> documents2 = ClassPathDocumentLoader.loadDocuments("knowledge");
|
||||
|
||||
//构建文档 分割器对象
|
||||
DocumentSplitter recursiveSplitter = DocumentSplitters.recursive(300, 50);
|
||||
|
||||
//3, 构建EmbeddingStoreIngestor , 完成文本数据的切割, 向量化, 存储
|
||||
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
|
||||
.embeddingStore(redisEmbeddingStore)
|
||||
.documentSplitter(recursiveSplitter) //设置文档分割器
|
||||
.embeddingModel(embeddingModel)
|
||||
.build();
|
||||
ingestor.ingest( documents1);
|
||||
return redisEmbeddingStore;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ContentRetriever contentRetriever(@Qualifier("myEmbeddingStoreInMemory2")EmbeddingStore store){
|
||||
EmbeddingStoreContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
|
||||
.embeddingStore(redisEmbeddingStore)
|
||||
.embeddingModel(embeddingModel)
|
||||
.minScore(0.5)//相似度
|
||||
.maxResults(3) //最多返回3条
|
||||
.build();
|
||||
return contentRetriever;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.cst.langchain4jheima.repository;
|
||||
|
||||
import dev.langchain4j.data.message.ChatMessage;
|
||||
import dev.langchain4j.data.message.ChatMessageDeserializer;
|
||||
import dev.langchain4j.data.message.ChatMessageSerializer;
|
||||
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Author: 李中华 wx: spllzh email(qq): 28668817@qq.com
|
||||
* Date: 2025/8/4 17:39
|
||||
**/
|
||||
@Repository
|
||||
public class RedisChatMemoryStore implements ChatMemoryStore {
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
@Override
|
||||
public List<ChatMessage> getMessages(Object memoryId) {
|
||||
//获取当前会话id的 聊天列表,是json串
|
||||
String chatMessageListJson = redisTemplate.opsForValue().get(memoryId.toString());
|
||||
|
||||
// 把JSON转换成会话列表
|
||||
List<ChatMessage> chatMessages = ChatMessageDeserializer.messagesFromJson(chatMessageListJson);
|
||||
|
||||
return chatMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessages(Object memoryId, List<ChatMessage> list) {
|
||||
|
||||
//把会话列表转换成JSON
|
||||
String chatMessageListJson = ChatMessageSerializer.messagesToJson(list);
|
||||
|
||||
//更新当前会话id的值,保存到Redis里
|
||||
redisTemplate.opsForValue().set(memoryId.toString(), chatMessageListJson, Duration.ofDays(1));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMessages(Object memoryId) {
|
||||
// 删除当前会话id的值
|
||||
redisTemplate.delete(memoryId.toString());
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,17 @@ langchain4j:
|
||||
model-name: qwen-plus
|
||||
log-requests: true
|
||||
log-responses: true
|
||||
embedding-model:
|
||||
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
api-key: ${DASHSCOPE_API_KEY}
|
||||
model-name: text-embedding-v3
|
||||
log-requests: true
|
||||
log-responses: true
|
||||
max-segments-per-batch: 10
|
||||
community:
|
||||
redis:
|
||||
host: 101.43.230.106
|
||||
port: 6388
|
||||
|
||||
logging:
|
||||
level:
|
||||
@@ -21,5 +32,7 @@ logging:
|
||||
spring:
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
port: 6389
|
||||
host: 124.221.59.58
|
||||
password: qwe123
|
||||
database: 0
|
||||
BIN
src/main/resources/knowledge/pdf/医院信息.pdf
Normal file
BIN
src/main/resources/knowledge/pdf/医院信息.pdf
Normal file
Binary file not shown.
BIN
src/main/resources/knowledge/pdf/科室信息.pdf
Normal file
BIN
src/main/resources/knowledge/pdf/科室信息.pdf
Normal file
Binary file not shown.
37
src/main/resources/knowledge/医院信息.txt
Normal file
37
src/main/resources/knowledge/医院信息.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
医院名称:
|
||||
北京协和医院
|
||||
|
||||
医院地址:
|
||||
(东单院区)北京市东城区帅府园一号,邮编:100730
|
||||
(西单院区)北京市西城区大木仓胡同41号,邮编:100032
|
||||
|
||||
门诊开放时间:
|
||||
工作日:8:00 - 17:30
|
||||
急诊:24小时开放
|
||||
|
||||
服务热线:
|
||||
(东单院区)010-69151188
|
||||
(西单院区)010-69158100
|
||||
|
||||
医院简介:
|
||||
北京协和医院是集医疗、教学、科研于一体的现代化综合三级甲等医院,是国家卫生健康委指定的全国疑难重症诊治指导中心,最早承担外宾医疗任务的医院之一,也是高等医学教育和住院医师规范化培训国家级示范基地,临床医学研究和技术创新的国家级核心基地。以学科齐全、技术力量雄厚、特色专科突出、多学科综合优势强大享誉海内外。在国家三级公立医院绩效考核中排名第一。
|
||||
|
||||
东单院区乘车路线:
|
||||
1. 106,108,110,111,116,到东单路口北
|
||||
2. 41,104,快到东单路口南
|
||||
3. 1,52,728,802,到东单路口西
|
||||
4. 10,20,25,37,39,到东单路口东
|
||||
5. 103,104,420,803,到新东安市场
|
||||
6. 地铁1号线或5号线到东单站,A或B出口向北
|
||||
|
||||
西单院区乘车路线:
|
||||
1. 68到辟才胡同东口
|
||||
2. 22,46,102,105,109,603,604,626,690,808,826,到西单商场
|
||||
3. 1,10,37,52,70,728,802,到西单路口东
|
||||
4.地铁1号线或4号线到西单站,F1或G出口向北
|
||||
|
||||
东单院区预约号取号地点:
|
||||
东院区老门诊楼一层大厅挂号窗口或新门诊楼各楼层挂号/收费窗口取号
|
||||
|
||||
西单院区预约号取号地点:
|
||||
西院区门诊楼一层大厅挂号窗口取号
|
||||
Reference in New Issue
Block a user