Spring AI (第四章)Rag知识库向量检索

Spring AI (第四章)Rag知识库向量检索 Spring AI (第四章)Rag知识库向量检索文章目录Spring AI (第四章)Rag知识库向量检索一、章节目的二、RAG2.1 什么是 RAGRetrieval-Augmented Generation2.2 RAG 系统核心流程阶段一知识获取Knowledge Ingestion阶段二知识检索Retrieval阶段三生成增强Augmented Generation2.3 支持的文档加载格式2.4 支持的向量数据库类型三、编写知识库文件四、Rag示例代码4.1 加载markdown文档代码4.2 向量数据库配置代码4.3 构建AI大模型聊天对象代码五、测试代码一、章节目的利用知识库打造个人或企业的专属智能问答系统实现具备私有知识理解与生成能力的 AI 模型。理解从知识文档编写→向量化处理→存储入库→检索与生成的完整流程掌握各阶段的关键技术要点。学习知识库文档的编写规范与支持的文件格式掌握文档切片策略Chunking、向量存储方法、语义检索机制 以及检索结果排序策略从而实现高质量、上下文相关的智能问答能力。文将以最简单的方式贯通整个流程中间涉及的可选技术栈可由读者自行探索。关于企业级应用的技术栈内容将在后续文章中进一步介绍。二、RAG2.1 什么是 RAGRetrieval-Augmented GenerationRAG检索增强生成 是一种结合了“信息检索Retrieval”与“大语言模型生成Generation”的混合式AI架构。传统大语言模型如 GPT、LLaMA 等在推理和语言生成上非常强大但它们存在天然的知识局限性知识截止问题模型的知识被固定在训练数据中无法获取新信息如2024年之后的事件。幻觉问题Hallucination当模型缺乏相关事实时容易“编造”看似合理但错误的信息。私有数据不可访问LLM无法直接访问企业内部文档、数据库或API数据。可解释性与可溯源性差模型回答的来源不透明难以验证正确性。RAG成为了解决这些问题的关键方案、通过引入检索模块LLM可以在生成回答前先从知识库、数据库或文档集合中获取最新、最相关的内容使输出更准确、可靠、可追溯。典型应用场景个人/企业问答、文档检索、代码助手等2.2 RAG 系统核心流程阶段一知识获取Knowledge Ingestion1.数据收集Data SourceRAG 系统支持从多种来源导入文本数据本地文件如.txt,.md,.pdf,.docx等数据库表字段通过 SQL 查询获取业务数据外部接口API 抓取动态内容网页内容通过爬虫或链接加载器2.文本切分Chunking将长文档拆分成适合向量化的小片段。常见策略按句子、段落或固定字符数切分目的提高召回精度避免超出模型的输入限制如2048 tokens工具Spring AI 内置DocumentSplitter或自定义分块规则3.向量化Embedding每个文本片段通过嵌入模型如DashScope、OpenAI、Ollama等生成向量表示Vector Embedding。向量是文本的语义映射用于计算相似度Spring AI 提供多种模型封装例如DashScopeEmbeddingModel、OpenAiEmbeddingModel向量会被存入向量数据库中如SimpleVectorStore、PGVector、Milvus等阶段二知识检索Retrieval当用户输入问题时系统会将该问题同样进行向量化并与知识库中的文档向量进行相似度匹配。检索算法余弦相似度 / 向量距离cosine、L2、dot-product返回结果最相关的 Top-K 文本片段通常 3~5 条这些片段会作为“上下文提示”Context传递给大模型帮助其生成基于知识的回答。阶段三生成增强Augmented Generation最后大语言模型LLM接收用户问题 检索出的上下文信息执行生成任务。模型结合外部知识生成更准确、可信的回答Spring AI 提供了ChatClient或ChatModel来完成对话生成┌───────────────────────────────┐ │ 数据源文件/API/DB │ └────────────┬─────────────────┘ │ ▼[文本分块 Chunking]│ ▼[向量化 Embedding]│ ▼[向量存储 VectorStore(如 PGVector)]│ ▼ 用户问题 → 向量化 │ ▼[相似度检索 Top-K 文本]│ ▼[LLM 结合上下文生成最终回答]2.3 支持的文档加载格式Spring AI 提供了若干 DocumentReader 实现用于将各类文档转为 Document 对象包含文本内容 元数据以供切片向量化。下面是主要支持的格式和读取器TextReader用于加载 纯文本 (.txt, .md 等) 文档。 JsonReader用于加载结构化的 JSON 文档将 JSON 中的字段抽取为 Document。 JsoupDocumentReader用于 HTML 文档基于 Jsoup 提取指定 CSS 选择器中的内容。 MarkdownDocumentReader用于 Markdown (.md) 文档支持将标题、代码块、引用块等拆分为多个 Document。 TikaDocumentReader基于 Apache Tika支持多种格式如 PDF、DOC/DOCX、PPT/PPTX、HTML 等 → 可用来处理较为通用的“二进制文档格式”。Spring AI 支持加载以下类型的文档纯文本 (.txt/.md)、JSON、HTML、Markdown、以及通过 Apache Tika 兼容的 PDFDOCXPPTX 等通用格式。2.4 支持的向量数据库类型在向量检索Vector Store层面Spring AI 提供了统一接口 VectorStore并已集成多个主流向量数据库/引擎。以下为可参考的支持列表类型类名 / 模块特点与适用场景内存型存储SimpleVectorStore轻量级、易于使用适合开发测试或小型项目。PostgreSQL (PGVector)PgVectorStore基于 PostgreSQL 扩展 PGVector支持持久化与 SQL 查询整合。PineconePineconeVectorStore云端向量数据库具备高性能、自动伸缩能力适合生产级部署。WeaviateWeaviateVectorStore支持多模态文本、图像向量检索与 Graph 语义关系。QdrantQdrantVectorStore开源、高性能支持向量过滤与 payload 查询。MilvusMilvusVectorStore面向大规模向量存储与检索的分布式数据库。ChromaChromaVectorStore轻量化、易部署适合原型与小型应用。RedisVector Similarity SearchRedisVectorStore利用 Redis Stack 的向量扩展功能兼具缓存与检索能力。MongoDB Atlas Vector SearchMongoDBVectorStore适合已有 MongoDB 项目的语义检索集成。在知识库的向量存储层Spring AI 支持常见的向量数据库与向量搜索引擎包括 Pinecone、Weaviate、Qdrant、Milvus、Chroma 等也支持利用传统数据库如 PostgreSQLPGVector、MongoDB、Neo4j、Oracle 等进行向量存储与语义检索从而提供更广泛的部署选项。三、编写知识库文件推荐使用markdown语法编写知识库文件、可以在知识库文件层面提高检索精度知识库文件要按内容划分个人的知识库文件可以按照个人背景、专业技能、兴趣、爱好去细致的划分、比如兴趣爱好可以划分娱乐、娱乐划分游戏、运动、看书、吃、喝、游戏再划分具体游戏、游戏内容(等级、人物、成就)企业的知识库文件可以按照企业背景、部门、产品、业务、自媒体去划分、比如按部门划分、部门有哪些岗位、岗位有哪些人在负责、具体做什么事(做了多少年、有哪些成绩)、自媒体包括短视频、推文、推文列表按时间划分、不同时间段企业的重大事件知识库文件命名、概括文件内容比如个人喜欢看那些书籍、文件命名要义书籍关键词命名划分、兴趣_书籍_科幻类.md、企业_部门_市场部_岗位职责.md、项目_无人机控制系统_设计文档.md每一段内容上方都要有标题、标题最好是带序号的、分大类标题、子标题、会提高模型的检索精度目标实现方法提高检索精度使用 Markdown 分层标题结构提高内容可管理性按主题、层级划分文件提高可读性简洁命名、清晰结构支持扩展性按需增补文件不破坏原有层级如果企业资料格式比较乱无法有效切割、可以通过不同的文件加载器切片汇总、通过粗排、精排提高检索精度四、Rag示例代码4.1 加载markdown文档代码src/main/resources/static/document/EXI-小洲个人介绍.md# EXI-小洲 个人介绍知识库# 一、EXI-小洲 个人形象信息- 身高1.82(米)- 体重140(斤)- 发型微粉碎盖、黑色、距离秃头还差1314根 - 性别男# 二、EXI-小洲 兴趣爱好- 运动跑步、减肥、乒乓球 - 爱吃美食、喜欢自己研发菜品# 三、EXI-小洲 娱乐## 3.1 游戏-王者荣耀- 射手擅长马可波罗、后裔 - 打野擅长韩信、李白、孙策 - 法师擅长安琪拉 - 战士擅长狂铁夏侯惇 - 最高段位王者20星 - 连胜13局 - 喜欢和队友开黑玩、阵容1搭配她玩谣我玩马可波罗阵容2搭配她玩妲己我玩安琪拉 - 从2018年开始玩的## 3.2 游戏-天天酷跑- 最擅长的模式极速模式 - 最长距离160000米(极速模式)- 从2015年开始玩、读初中的时候 - 最熟悉的装备是超能少年(角色)小单车(座骑)宙斯(宠物)小丑熊(精灵)- 目前账号装备情况钻石28000个、金币999999999个、黄金奖券580张、七彩石650颗pom依赖!-- 加载makeDown文档依赖 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-markdown-document-reader/artifactIdversion1.0.0-M6/version/dependencyimportlombok.extern.slf4j.Slf4j;importorg.springframework.ai.document.Document;importorg.springframework.ai.reader.markdown.MarkdownDocumentReader;importorg.springframework.ai.reader.markdown.config.MarkdownDocumentReaderConfig;importorg.springframework.core.io.Resource;importorg.springframework.core.io.support.ResourcePatternResolver;importorg.springframework.stereotype.Component;importjava.util.ArrayList;importjava.util.List;/** * 加载markdown文档 */Slf4jComponentpublicclassDocumentLoader{// 支持加载多个md文档privatefinalResourcePatternResolverresourcePatternResolver;// 通过构造器注入 ResourcePatternResolverpublicDocumentLoader(ResourcePatternResolverresourcePatternResolver){this.resourcePatternResolverresourcePatternResolver;}/** * 加载多个md文档的方法 * return */publicListDocumentloaderMarkDowns(){ListDocumentallDocumentsnewArrayList();try{// 获取resource下面document目录的所有md文档对象Resource[]resourcesresourcePatternResolver.getResources(classpath:static/document/*.md);for(Resourceresource:resources){StringfileNameresource.getFilename();if(!EXI-小洲个人介绍.md.equals(fileName)){continue;// 内存向量数据库长度有限、只加载一个文档、后续使用持久化向量数据库可以解决这个问题}// 构建文档切片对象、定义知识库文件切片方式MarkdownDocumentReaderConfigconfigMarkdownDocumentReaderConfig.builder().withHorizontalRuleCreateDocument(true)// 根据下划线切割.withIncludeCodeBlock(false)// 是否包含代码.withIncludeBlockquote(false)// 是否根据引用切割.withAdditionalMetadata(filename,fileName)// 设置切片元素的元数据(添加属性).build();// 通过markdown读取对象、加载切片对象以及md文件资源对象MarkdownDocumentReaderreadernewMarkdownDocumentReader(resource,config);allDocuments.addAll(reader.get());// 添加多个对象}}catch(Exceptione){log.error(markdown文档加载失败{},e.getMessage());}returnallDocuments;}}4.2 向量数据库配置代码功能演示我这边使用轻量内置的向量数据库、持久化的向量数据库请关注后续文章importjakarta.annotation.Resource;importorg.springframework.ai.document.Document;importorg.springframework.ai.embedding.EmbeddingModel;importorg.springframework.ai.vectorstore.SimpleVectorStore;importorg.springframework.ai.vectorstore.VectorStore;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.List;/** * 配置向量数据库、定义成Bean对象 */ConfigurationpublicclassVectorStoreConfig{/** * 根据bean的名称注入 文档加载服务对象 */ResourceprivateDocumentLoaderdocumentLoader;/** * 定义一个 Bean 对象并且该对象可以在 Spring 容器中管理、bean的名称就是方法名memoryVectorStore * * param dashscopeEmbeddingModel * return */BeanVectorStorememoryVectorStore(EmbeddingModeldashscopeEmbeddingModel){// 根据阿里的向量模型名称注入、EmbeddingModel 是一个接口表示“可以将文本转换为向量表示”的模型// 定义一个基于内存读写的向量数据库对象SimpleVectorStoresimpleVectorStoreSimpleVectorStore.builder(dashscopeEmbeddingModel).build();// 获取md文档列表ListDocumentdocumentsdocumentLoader.loaderMarkDowns();// 把md文档列表添加至向量数据库中simpleVectorStore.add(documents);// 把向量数据库对象返回至大模型进行调用returnsimpleVectorStore;}}4.3 构建AI大模型聊天对象代码importcom.alibaba.cloud.ai.advisor.RetrievalRerankAdvisor;importcom.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankModel;importlombok.extern.slf4j.Slf4j;importorg.exi.exiaiagent.advisor.MyLoggerAdvisor;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;importorg.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.ai.chat.memory.InMemoryChatMemory;importorg.springframework.ai.chat.model.ChatModel;importorg.springframework.ai.chat.model.ChatResponse;importorg.springframework.ai.vectorstore.SearchRequest;importorg.springframework.ai.vectorstore.VectorStore;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.core.io.ClassPathResource;importorg.springframework.core.io.Resource;importorg.springframework.stereotype.Component;importstaticorg.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;importstaticorg.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;/** * 创建基有知识库检索的AI大模型 */ComponentSlf4jpublicclassChatRagApp{privatefinalChatClientchatClient;// 注入自定义的内存向量数据库 Bean对象jakarta.annotation.ResourceprivateVectorStorememoryVectorStore;// 百练排序模型AutowiredprivateDashScopeRerankModelrerankModel;/** * 构造器 初始化大模型聊天对象 * param dashscopeChatModel 阿里灵积大模型 */publicChatRagApp(ChatModeldashscopeChatModel){// 加载自定义 提示词文件ResourcepromptResourcenewClassPathResource(static/prompt/test_prompt.st);// 创建内存存储对象ChatMemorychatMemoryUseMemoryChatMemory();chatClientChatClient.builder(dashscopeChatModel).defaultSystem(promptResource)// 配置自定义提示词.defaultAdvisors(newMessageChatMemoryAdvisor(chatMemory),// 配置上下文记忆存储对象newMyLoggerAdvisor()// 配置自定义的日志拦截器).build();}/** * 基于计算机内存存储、实现读取、写入聊天上下文 * return */publicChatMemoryUseMemoryChatMemory(){/** * 默认使用MessageWindowChatMemory * 存储位置: JVM 内存 (InMemoryChatMemoryRepository) * 数据持久化: 不持久化应用重启后丢失 * 消息窗口: maxMessages 20默认可配置 */InMemoryChatMemorychatMemorynewInMemoryChatMemory();returnchatMemory;}publicStringdoChat(Stringmessage,StringchatId){// 检索重排序拦截器对象、粗排一般200精排一般默认5RetrievalRerankAdvisorretrievalRerankAdvisornewRetrievalRerankAdvisor(memoryVectorStore,// 向量存储用于粗排检索rerankModel,// 重排序模型用于精排SearchRequest.builder().topK(200).build()// 设置粗排的检索数量、粗排需要设置较大一般设置200);ChatResponsechatResponsechatClient.prompt().user(message).advisors(advisorSpec-advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)// 指定聊天记忆存储ID.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY,10)// 获取最近10条聊天记忆)// .advisors(retrievalRerankAdvisor) // 配置重排拦截器// 通过QuestionAnswerAdvisor拦截器实现检索增强、并指定自定义的内存向量数据库.advisors(newQuestionAnswerAdvisor(memoryVectorStore)).call().chatResponse();// 返回ChatResponse对象StringcontentchatResponse.getResult().getOutput().getText();// 获取chatResponse的结果数据log.info(content: {},content);returncontent;}}精排的场景示例代码用的精排模型是阿里百炼的DashScopeRerankModel、其他大厂的Rank模型还请自行探索对于检索精度较高的场景可以用精排个人专属模型、我更喜欢灵活创意一些、所以把精排的配置代码注释了五、测试代码importjakarta.annotation.Resource;importorg.junit.jupiter.api.Test;importorg.springframework.boot.test.context.SpringBootTest;importstaticorg.junit.jupiter.api.Assertions.*;SpringBootTestclassChatAppTest{/** * Resource 注解的作用是先根据字段名后根据类型 来自动匹配注入对应的 Bean */ResourceprivateChatAppchatApp;/** * 没有上下文记忆功能 */TestvoiddoChat(){Stringmessage你好我叫小洲;StringanswerchatApp.doChat(message);message我是谁;answerchatApp.doChat(message);}}测试结果知识库内容越多回复的内容会更加独特、更偏向你自己的专属风格