Spring AI(11) :ChatPDF-向量数据库、PDF处理、向量写入和向量搜索

发布时间:2026/8/8 8:39:42
Spring AI(11) :ChatPDF-向量数据库、PDF处理、向量写入和向量搜索 本章代码已分享至Gitee:https://gitee.com/lengcz/ai-study.git文章目录向量数据库Spring AI 支持的向量数据库有哪些Spring AI 中如何使用向量数据呢(以redis stack为例安装redis stack引入依赖配置文件如何使用配置SimpleVectorStore(简单的内存向量模型)文档处理和向量写入读取完成文档拆分这个Document有什么组成PDF Reader(示例)向量搜索按照文本的向量搜索增加配置的向量搜索向量数据库向量数据库的核心作用是‌实现语义层面的相似性检索‌区别于传统数据库的字面精确匹配能快速找到“含义最贴近”的数据是AI时代的关键基础设施。核心基础能力‌它可将文本、图片、音视频等非结构化数据转化为高维向量通过余弦相似度、欧氏距离计算向量间的接近程度完成高效的相似性匹配检索支持亿级规模向量的毫秒级查询。主流落地场景‌支撑RAG检索增强生成为大模型对接外部知识库大幅缓解大模型“幻觉”问题提升回答的准确性和时效性。应用于推荐系统基于用户兴趣向量实现精准的内容、商品匹配推送。落地以图搜图、语音/视频内容检索、异常检测等场景完成多模态数据的快速相似查找。Spring AI 支持的向量数据库有哪些官网文档地址 https://docs.spring.io/spring-ai/reference/1.0/api/vectordbs.htmlVectorStore 的实现Spring AI支持以下向量数据库数据库名称官方描述数据库类型适用场景 / 特点Azure Vector SearchThe Azure vector store.云服务全托管深度集成Azure生态适合微软云用户Apache CassandraThe Apache Cassandra vector store.NoSQL宽列高可用、大规模分布式写入适合海量数据ChromaThe Chroma vector store.专用向量数据库轻量级、开源适合本地开发和原型验证ElasticsearchThe Elasticsearch vector store.搜索引擎已有ES集群需同时支持全文检索向量检索GemFireThe GemFire vector store.企业级内存数据库VMware生态高性能缓存与实时计算场景MariaDBThe MariaDB vector store.关系型 (SQL)基于MySQL分支适合小型到中型数据量MilvusThe Milvus vector store.专用向量数据库高性能、分布式适合大规模生产环境MongoDB AtlasThe MongoDB Atlas vector store.NoSQL文档JSON文档存储与向量检索结合灵活易用Neo4jThe Neo4j vector store.NoSQL图数据库需同时处理知识图谱/关系网络与向量检索OpenSearchThe OpenSearch vector store.搜索引擎AWS开源版ES适合云上与全文检索融合OracleThe Oracle Database vector store.关系型 (SQL)企业级重型数据库适合已有Oracle栈的客户PgVectorThe PostgreSQL/PGVector vector store.关系型 (SQL)Postgres扩展最适合已用PG的技术栈PineconeThe PineCone vector store.专用向量数据库云原生全托管、低延迟适合SaaS和Serverless应用QdrantThe Qdrant vector store.专用向量数据库高性能、Rust编写支持过滤和Payload丰富RedisThe Redis vector store.NoSQL键值/缓存实时性要求极高需结合缓存与向量检索SAP HanaThe SAP HANA vector store.企业级内存列式数据库已有SAP生态需进行实时分析和向量融合TypesenseThe Typesense vector store.搜索引擎轻量、易用追求开箱即用的搜索体验WeaviateThe Weaviate vector store.专用向量数据库内置GraphQL支持混合搜索和自动向量化SimpleVectorStoreA simple implementation… good only for testing.内存级测试实现仅限开发/单元测试不适用于生产Spring AI 中如何使用向量数据呢(以redis stack为例注意这里的redis 不是指redis缓存服务器是指redis stack。官网文档地址: https://docs.spring.io/spring-ai/reference/1.0/api/vectordbs/redis.html安装redis stack这里为了方便提供docker命令安装dockerrun-d--nameredis-stack-p6379:6379-p8001:8001 redis/redis-stack:latest引入依赖dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-vector-store-redis/artifactId/dependency配置文件spring:data:redis:url:127.0.0.1:6379ai:vectorstore:redis:initialize-schema:true#向量库索引名index-name:my_doc_ai_index#是否初始化向量向量索引结构prefix:doc:#向量库key浅醉如何使用Spring AI已经为所有向量数据库统一了一套接口都实现了VectorStore各向量数据库除了依赖和配置有差异外使用方式都相同。以下是 VectorStore接口的定义/** * 向量存储接口用于管理文档的向量化表示及其持久化。 * 提供添加、删除以及基于向量相似度的检索能力。 * 继承自 {link DocumentWriter}支持文档写入操作。 */publicinterfaceVectorStoreextendsDocumentWriter{/** * 获取当前向量存储实例的名称。 * 默认实现返回类的简单名称不含包路径。 * * return 存储实例的名称默认使用类名 */defaultStringgetName(){returnthis.getClass().getSimpleName();}/** * 批量添加文档到向量存储中。 * 实现类需将文档内容转换为向量表示并存储同时保存元数据以便后续检索。 * * param documents 要添加的文档列表不能为空 */voidadd(ListDocumentdocuments);/** * 根据文档 ID 列表批量删除文档。 * * param idList 要删除的文档 ID 列表 */voiddelete(ListStringidList);/** * 根据过滤表达式删除符合条件的文档。 * 使用结构化过滤表达式如字段比较、逻辑组合进行条件删除。 * * param filterExpression 过滤表达式定义删除条件 */voiddelete(Filter.ExpressionfilterExpression);/** * 根据字符串形式的过滤表达式删除文档默认实现。 * 提供一种便捷的字符串表达式解析方式具体语法由子类实现。 * 默认实现抛出 {link UnsupportedOperationException}建议子类覆写。 * * param filterExpression 字符串形式的过滤表达式 */defaultvoiddelete(StringfilterExpression){// 默认实现为空或抛出异常这里以省略号表示原有逻辑thrownewUnsupportedOperationException(String filter expression not supported by default.);}/** * 执行基于向量相似度的搜索使用默认的查询向量由 query 字符串生成。 * 返回与查询最相似的文档列表通常按相似度降序排列。 * * param query 查询文本将自动转换为向量 * return 与查询相似的文档列表 */ListDocumentsimilaritySearch(Stringquery);/** * 执行基于向量相似度的搜索支持丰富的搜索参数。 * 可以通过 {link SearchRequest} 指定查询向量、返回数量、过滤条件、相似度阈值等。 * * param request 搜索请求对象封装所有检索参数 * return 符合搜索条件的文档列表 */ListDocumentsimilaritySearch(SearchRequestrequest);/** * 获取底层原生客户端的可选实例。 * 当向量存储基于某个第三方库如 Elasticsearch、Pinecone 等实现时 * 可通过此方法暴露原始客户端以便进行高级操作。 * 默认返回 {link Optional#empty()}表示不支持或无需暴露。 * * param T 原生客户端类型 * return 包含原生客户端的 Optional若不存在则为空 */defaultTOptionalTgetNativeClient(){returnOptional.empty();}}配置SimpleVectorStore(简单的内存向量模型)SimpleVectorStore 仅用于学习请勿用于生产和测试引入依赖dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-vector-store/artifactIdversion${spring-ai.version}/version!-- 请使用实际的 Spring AI 版本 --/dependency配置SimpleVectorStoreBeanpublicVectorStorevectorStore(OpenAiEmbeddingModelmodel){returnSimpleVectorStore.builder(model).build();}文档处理和向量写入读取完成文档拆分这里文档需要经过读取转换写入 三个操作实现文档写入到向量数据库。这个Document有什么组成Document的组成id: 文档的idcontent: 内容metadata: 元数据文件名文件大小创建时间修改时间等Media: 媒体就是音频视频等文件而针对Document Reader 文档读取过程spring-ai已经提供了很多相关的Reader支持不同文件类型的读取解析。JSONTextMarkdownPDF PagePDF ParagraphTika(DOCXPPTHTML…PDF Reader(示例)其他Document Reader 请参考官网文档https://docs.spring.io/spring-ai/reference/1.0/api/etl-pipeline.html#page-titlespring ai 提供了针对不同文件类型的Document Reader根据不同文档类型引入不同的依赖PDF 这里引入的依赖!--Document Reader 针对Pdf 的reader--dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-pdf-document-reader/artifactId/dependency读取并写入到向量数据库TestvoidwriteFileToVectorStore(){// 1. 准备 PDF 资源从 classpath 加载ResourcepdfResourcenewClassPathResource(40、狂神说Docker.pdf);// 2. 配置 PDF 读取器可选PdfDocumentReaderConfigconfigPdfDocumentReaderConfig.builder().withPageExtractedTextFormatter(ExtractedTextFormatter.defaults())// 每页最大词数.withPagesPerDocument(1)//每1页作为一个Document.build();// 3. 创建 PagePdfDocumentReader 实例PagePdfDocumentReaderpdfReadernewPagePdfDocumentReader(pdfResource,config);// 4. 读取 PDF 文档返回 Document 列表Listorg.springframework.ai.document.DocumentdocumentspdfReader.get();// 或使用 pdfReader.read()// 5. 验证读取结果System.out.println(读取到 documents.size() 页文档);// 7. 写入向量数据库vectorStore.add(documents);// 8. 验证写入结果System.out.println(成功写入 documents.size() 个文档块到向量数据库);ListDocumentresultvectorStore.similaritySearch(谁创办了Docker);if(nullresult){System.out.println(没有找到结果);return;}System.out.println(搜索结果);result.forEach(c-{System.out.println(c.getId());System.out.println(c.getScore());System.out.println(c.getText());});}测试用例运行结果PDF 除了按页读取的PagePdfDocumentReader 还有ParagraphPdfDocumentReader 按大纲读取但是很多PDF文件不规范没有大纲标题会导致读取失败。向量搜索按照文本的向量搜索ListDocumentresultvectorStore.similaritySearch(谁创办了Docker);if(nullresult){System.out.println(没有找到结果);return;}System.out.println(搜索结果);result.forEach(c-{System.out.println(c.getId());System.out.println(c.getScore());System.out.println(c.getText());});这里搜出了4页但是实际上一些结果并不相关我们并不想要关联度不是很高的结果。增加配置的向量搜索 使用vectorStore我们已经配置了向量模型故在写向量和搜索向量时并不需要我们显式的进行向量化操作而是由其内部完成向量化转换。// 8. 验证写入结果System.out.println(成功写入 documents.size() 个文档块到向量数据库);SearchRequestrequestSearchRequest.builder().query(谁创办了Docker)//匹配关键词.topK(2)//相似度最高的前几名.similarityThreshold(0.7)//相似度阈值.filterExpression(file_name 40、狂神说Docker.pdf)//过滤项目,表示搜索元数据里面文件名是40、狂神说Docker.pdf的文档.build();// ListDocument result vectorStore.similaritySearch(谁创办了Docker);ListDocumentresultvectorStore.similaritySearch(request);if(nullresult){System.out.println(没有找到结果);return;}System.out.println(搜索结果);result.forEach(c-{System.out.println(c.getId());System.out.println(c.getScore());System.out.println(c.getText());});经过这些过滤条件我们可以看到只搜索到一条记录。