检索增强生成(RAG)是一种有助于克服大型语言模型局限性的技术,这些局限性主要体现在处理长篇内容、确保事实准确性以及保持上下文感知能力方面。
Spring AI 使用AdvisorAPI为常见的 RAG 流程提供开箱即用的支持。
一、简单理解流程
1、获取用户输入
2、将用户输入调用模型转成向量
3、根据向量去向量数据库查询相似的内容
4、一起把内容发送到大模型
5、返回结果
二、简单例子
向量数据库就用上节用的内存数据库
1、依赖
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-vector-store-advisor</artifactId></dependency>
2、文档获取工具类
用来获取某个目录下面的文档
public class TxtDocumentLoader {/*** 静态工具方法,线程安全,无任何静态成员变量* @param dirPath 文件夹路径* @param chunkSize 分片字符数* @param chunkOverlap 重叠字符数* @return List<Document>*/public static List<Document> loadFromDirectory(String dirPath, int chunkSize, int chunkOverlap) {if (chunkOverlap >= chunkSize) {throw new IllegalArgumentException("重叠长度不能大于等于分片长度");}List<Document> documentList = new ArrayList<>();File dir = new File(dirPath);if (!dir.exists() || !dir.isDirectory()) {return documentList;}File[] files = dir.listFiles((f, name) -> name.toLowerCase().endsWith(".txt"));if (files == null) {return documentList;}for (File file : files) {documentList.addAll(loadSingleTxt(file, chunkSize, chunkOverlap));}return documentList;}public static List<Document> loadSingleTxt(File txtFile, int chunkSize, int chunkOverlap) {List<Document> chunks = new ArrayList<>();String content;try {content = Files.readString(txtFile.toPath(), StandardCharsets.UTF_8);} catch (IOException e) {throw new RuntimeException("读取文件失败:" + txtFile.getAbsolutePath(), e);}if (!StringUtils.hasText(content)) {return chunks;}List<String> sliceTexts = splitText(content, chunkSize, chunkOverlap);for (int i = 0; i < sliceTexts.size(); i++) {Document doc = new Document(sliceTexts.get(i), Map.of("file_name", txtFile.getName(),"file_path", txtFile.getAbsolutePath(),"chunk_index", String.valueOf(i)));chunks.add(doc);}return chunks;}private static List<String> splitText(String text, int chunkSize, int chunkOverlap) {List<String> result = new ArrayList<>();int start = 0;int len = text.length();while (start < len) {int end = Math.min(start + chunkSize, len);String chunk = text.substring(start, end);result.add(chunk);start = start + chunkSize - chunkOverlap;}return result;}// 重载:给默认值,方便快速调用public static List<Document> loadFromDirectory(String dirPath) {return loadFromDirectory(dirPath, 800, 150);}}
2、简单使用
//先将内容加入到向量数据库中@GetMapping("/addV")public String addV(){//加入向量数据库中List<Document> documents = TxtDocumentLoader.loadFromDirectory("C:\\Users\\86135\\Desktop\\text");vectorStore.doAdd(documents);return "ok";}@GetMapping("/getV")public List<Document> getV(String topic){//相似度查询List<Document> documents = vectorStore.similaritySearch(SearchRequest.builder().query(topic)//要查询的结果.topK(2)//最多返回两个.build());return documents;}@GetMapping("/rag")public String rag(String topic){QuestionAnswerAdvisor questionAnswerAdvisor= QuestionAnswerAdvisor.builder(vectorStore).searchRequest(SearchRequest.builder().similarityThreshold(0.4d).//相似度阈值,越大越严格,太大要求太高了不一定能查到内容topK(5).build()).build();String result = openaiChatClient.prompt().user(u -> u.text("{topic}").param("topic",topic)).advisors(questionAnswerAdvisor).call().content();return result;}
先addV在/rag查询结果,如果没有查询到目标,有可能 similarityThreshold(0.4d)阈值设置的太高了。
