ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring AI 1.0 + PGVector 搭建个人知识库问答系统实战指南

Spring AI 1.0 + PGVector 搭建个人知识库问答系统实战指南 简介本资源是一个基于Spring AI 1.0与PGVector向量数据库构建的个人知识库AI问答系统实战项目面向Java后端开发者、AI工程实践者及深度学习初学者解决私域知识高效检索与自然语言交互问答的技术落地问题。压缩包共214个文件4.29MB涵盖84个Java核心业务与AI集成代码、30个TypeScript/21个TSX前端交互组件、20个XML配置与SQL脚本、以及CSS/LESS样式、YML/YAML配置、MD文档和PNG/SVG图标等完整呈现从知识切片入库、嵌入向量化、语义检索到流式响应的全链路实现。已有127人学习下载项目结构清晰含可运行的Spring Boot服务、React前端、本地知识加载工具及预置示例数据配套.env.example、git hooks与lint/prettier规范文件开箱即用且便于二次扩展。 看到Spring AI 1.0加上PGVector这个组合我第一反应是终于有Java开发者能把个人知识库问答这套东西做得既正统又轻量了。以前要做类似的事要么自己封装大模型的HTTP请求要么去啃LangChain4j的文档要么干脆把数据搬到SaaS服务里。现在Spring官方把RAG链路里最繁琐的环节文档解析、切分、向量化、向量检索、Prompt增强都抽象成了标准API你只需要负责接数据和写业务逻辑。这篇博文就围绕“Spring AI 1.0 PGVector实现基于个人知识库的AI问答系统”这个主题把从零搭建到调优的完整过程拆开讲清楚。无论你是一个人在本地积累了大量Markdown笔记、PDF资料还是想给团队做一套内部文档问答工具这套方案都足够实用而且全程不依赖某个特定云厂商数据向量就存在你自己的PostgreSQL里。1. 内容整体设计与思路拆解1.1 需求拆解个人知识库问答真正要解决什么个人知识库问答系统听起来高大上真正落到需求上其实就三件事第一把你散落在各个文档里的知识收拢起来第二让大模型能基于这些知识回答而不是凭空编造第三回答结果要快、要准、要能明确指出依据来自哪份资料。很多新手容易把“个人知识库问答”理解成一个简单的对话机器人这是最大的误区。普通对话机器人只需要对接大模型接口然后把用户问题原样转发过去就行。而知识库问答的核心区别在于它必须引入**检索增强生成RAG**链路。RAG的做法是先把你的文档切成小块用Embedding模型把每一块转成向量存进向量数据库用户提问时系统先把问题也转成向量从数据库里找出最相似的几个文本片段再把这些片段作为参考资料拼进Prompt最后让大模型基于这些资料生成回答。为什么非要走这条路因为大模型的训练数据是截止到某个时间点的通用数据它根本不知道你个人的笔记内容、你团队内部的技术文档、或者某本绝版书里的冷门知识。微调模型倒是有可能让模型迁移这些知识但微调成本高、周期长、每次知识更新都要重新训练对个人和中小团队完全划不来。RAG的优势就是知识即插即用改文档不用改模型新增一批资料直接入库就能用。1.2 技术选型Spring AI 1.0和PGVector为什么是绝配先说说Spring AI 1.0。这是Spring官方在2024年开始推进的AI框架1.0版本意味着API已经稳定不再是天天改的里程碑版本。它做的事情是定义了一套统一的接口比如ChatClient负责和大模型对话、EmbeddingModel负责文本向量化、VectorStore负责向量数据的读写、Advisor负责在对话链路中插入检索逻辑。你只需要选择模型供应商OpenAI、通义千问、Ollama本地模型等和向量存储PGVector、Milvus、Qdrant等代码层面几乎不用改就能切换底层实现。再来看PGVector。它是PostgreSQL的一个扩展让你可以在熟悉的SQL数据库里直接存储向量并执行相似度检索。相比Milvus、Qdrant这些专用向量数据库PGVector可能不是性能最强的但对个人知识库系统来说它的优势极其明显你本来就可能用PostgreSQL存业务数据加一个扩展就同时拥有了向量检索能力少维护一个中间件备份、迁移、权限管理全都复用原有方案。数据量在几万条文本片段以内PGVector的检索速度完全够用。我对比过几类方案整理成表格供参考方案优点缺点适用场景PGVector随PostgreSQL一体部署运维成本低支持SQL联合查询数据量极大时检索性能不如专用向量库个人知识库、中小规模业务系统Milvus分布式、高并发、海量向量表现好部署复杂资源占用高生产级大规模向量检索QdrantRust编写检索性能优秀支持过滤需要额外部署服务大规模且需要复杂过滤的场景Elasticsearch向量插件全文检索和向量检索融合资源占用大配置复杂已有ES体系的企业最后说说为什么不用LangChain4j。LangChain4j确实不错抽象层次高但Spring AI 1.0和Spring Boot的集成更自然自动配置、starter依赖、属性绑定都很顺畅。如果团队本来就熟Spring学习成本会低很多。1.3 整体架构与数据流向整个系统的数据流可以分成两个阶段离线索引阶段和在线问答阶段。离线索引阶段做的事情是把文档转换成向量并入库。流程是读取文档内容支持PDF、TXT、Markdown等格式→ 把长文本切成固定大小的片段 → 调用Embedding模型把每个片段转成向量 → 把向量和原始文本一起存入PGVector。这个过程一般通过一个管理接口或启动任务触发不需要在每次问答时重复执行。在线问答阶段做的事情是接收用户问题并返回基于知识库的回答。流程是用户输入问题 → 把问题同样转成向量 → 在PGVector中按相似度检索出Top K个文本片段 → 把问题和检索结果一起组装成Prompt → 调用大模型生成回答 → 返回给用户同时附上检索来源的关键词供你确认答案是否可靠。这个架构的好处在于问答阶段的核心是“检索生成”整个链路在Java服务内部完成数据库和模型都通过统一接口访问无论是部署在本机还是公司内网服务器代码都不用变。2. 环境准备与基础依赖配置2.1 基础软件环境在动代码之前先把基础环境准备好。我实际跑通这套系统用到的环境如下JDK 17Spring Boot 3.x强制要求Maven 3.6 或 Gradle 7.5PostgreSQL 14以上建议直接用16一个Embedding模型和一个对话模型我这边测试时用兼容OpenAI的接口也验证过通过Spring AI接入通义千问和Ollama本地模型效果都不错有一个容易忽略的点Spring AI 1.0要求Spring Boot 3.x你用旧版Spring Boot是跑不起来的。建议直接用Spring Boot 3.3.x或3.4.x兼容性最稳定。关于模型服务商怎么选我给两条建议。如果你要完全私有化部署本地装Ollama用bge-m3做Embedding、qwen2.5或llama3做对话模型Spring AI提供了Ollama的starter配置后无感切换。如果你更看重回答质量用商业API也完全可以Spring AI的适配层会帮你屏蔽底层差异。2.2 在PostgreSQL上安装PGVector插件这是第一个容易出现坑的地方Windows和Linux安装方式差异很大。Linux环境最省事。如果你的PostgreSQL是用官方APT源装的直接执行sudo apt install postgresql-16-pgvector装完后进入数据库执行CREATE EXTENSION IF NOT EXISTS vector;如果系统里没有现成包也可以走源码编译先clone pgvector仓库在PostgreSQL的pg_config目录下执行make和make install最后同样执行CREATE EXTENSION vector。Windows环境稍微麻烦一点。最推荐的方式是在安装PostgreSQL时通过Stack Builder选择pgvector进行安装版本会自动匹配。如果你用的是安装版PostgreSQL但当初没装扩展库也可以去pgvector的GitHub Release页面下载对应的zip包把.dll、.control、.sql文件分别复制到PostgreSQL安装目录的lib和share/extension目录下。注意要选择和你PostgreSQL版本匹配的包Windows下还要区分是否支持AVX指令集选错了启动数据库时会直接报错。安装完成后在psql里执行确认SELECT * FROM pg_available_extensions WHERE name vector;能看到name和default_version就说明安装成功。这一步做成后后面Spring AI自动建表才能正常工作否则启动时报错会非常让人头大。2.3 创建数据库和Spring Boot工程先建一个专用数据库CREATE DATABASE knowledge_db;然后创建Spring Boot工程pom.xml里核心依赖是这样的dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-pgvector/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /dependency如果你要用通义千问或Ollama把spring-ai-starter-model-openai替换成spring-ai-starter-model-qwen或spring-ai-starter-model-ollama就行。Spring AI 1.0的starter命名很规范按模型厂商分。这里强调一个版本管理的关键点Spring AI 1.0的依赖版本不要自己写死最好借助Spring Boot的BOM或者直接引入Spring AI的BOM来统一版本dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement如果不加这个BOM很容易出现spring-ai-core和pgvector starter版本不匹配的问题尤其是在Maven依赖树里有其他间接引用的情况下。3. 核心细节解析与实操要点3.1 配置文件中必须搞清楚的几个参数Spring AI 1.0的配置非常简洁但有几个参数值得仔细说明。我实际用的application.yml如下spring: datasource: url: jdbc:postgresql://localhost:5432/knowledge_db username: postgres password: yourpassword ai: openai: api-key: ${LLM_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.2 embedding: options: model: text-embedding-3-small vectorstore: pgvector: index-type: HNSW distance-type: COSINE_DISTANCE dimensions: 1536 remove-existing-vector-store-table: false第一要弄清楚的是dimensions。这个值必须和Embedding模型输出的向量维度一致。OpenAI的text-embedding-3-small输出1536维text-embedding-3-large输出3072维通义千问的text-embedding-v3输出1024维本地bge-m3输出1024维。你改了Embedding模型这个维度必须同步改否则入库时报错维度不匹配。我第一次用bge-m3时忘了改配置启动就报错排查了半小时才反应过来。第二是distance-type。文本语义相似度检索推荐用COSINE_DISTANCE余弦距离它关注的是向量的方向而不是长度对Embedding模型的结果更友好。EUCLIDEAN_DISTANCE在一些场景下也可以但整体效果不如余弦。第三是index-type。HNSW和IVFFlat两种索引HNSW检索精度高、速度快但构建索引时更耗内存和耗时IVFFlat构建快、省资源但检索精度略低而且需要对已有数据先聚类数据量太少时效果差。个人知识库数据量在几千到几万条我直接推荐HNSW参数用默认的m16、ef_construction64即可。3.2 文档加载与切分的正确姿势个人知识库的文档格式五花八门可能是TXT笔记、PDF扫描书、Word文档、Markdown文件。Spring AI 1.0提供了多种DocumentReader来应对这些格式。最省事的是TikaDocumentReader它基于Apache Tika能自动识别大部分常见文档格式从文本到PDF再到Office文件都能读出来。用法很简单Bean public CommandLineRunner initKnowledgeBase( EmbeddingModel embeddingModel, VectorStore vectorStore) { return args - { TikaDocumentReader reader new TikaDocumentReader( new FileSystemResource(/data/notes/)); ListDocument documents reader.get(); TokenTextSplitter splitter new TokenTextSplitter(500, 100); ListDocument chunks splitter.apply(documents); vectorStore.add(chunks); System.out.println(知识库初始化完成共入库 chunks.size() 个片段); }; }我特别想强调的是切分这一步。很多人拿到文档就直接把整篇丢给Embedding模型然后发现检索效果稀烂。原因很简单一篇文档几千字向量化后只能代表整篇的中心思想用户问某个具体问题时这个“中心向量”和问题向量的相似度反而不高。必须把文档切成小块让每个块聚焦一个主题。TokenTextSplitter有两个关键参数目标块大小和重叠大小。目标块大小一般设置在500到800个token太小时上下文不完整太大时语义容易被稀释。重叠大小设置为目标块的10%到20%用来保证切分边界附近的语义不断裂。为什么按token切而不是按字符切因为大模型上下文窗口和Embedding模型的处理单位都是token中文场景下按字符切会导致单次向量化的文本量偏少切出来的语义也不稳定。3.3 向量化与入库的实现细节在Spring AI 1.0里向量化模型和向量存储都是由自动配置创建的。你只需要在配置类里定义好Bean然后注入使用。Configuration public class VectorStoreConfig { Bean CommandLineRunner loadDocuments( VectorStore vectorStore, EmbeddingModel embeddingModel) { return args - { if (vectorStore.similaritySearch(SearchRequest.query(test).withTopK(1)).isEmpty()) { // 知识库为空时执行初始化 } }; } }实际上我更推荐把文档入库做成一个Rest接口而不是启动时自动加载。因为知识库内容会持续更新每次重启应用都重新加载全量文档既浪费token又耗时。接口化的好处是你随时可以只加载某几个文件或某个目录增量入库。入库时每次调用vectorStore.add()会调用EmbeddingModel把文本转成向量然后写入PGVector的向量表。Spring AI自动建的表结构字段包括id、content、metadata和embedding其中content存原始文本片段embedding存向量metadata是一个JSON字段用来存文件名、页码等辅助信息这些信息在后续检索结果展示时非常有用。有人会问这个自动建的表能不能自定义表名Spring AI的PgVectorStore配置里有一个table-name参数默认是vector_store你可以改成知识库的名称。不过我建议第一次跑通前不要改用默认最稳妥等整个链路通了再个性化。3.4 问答接口用Advisor把检索和生成串起来Spring AI 1.0中实现RAG问答最省事的API是ChatClient搭配QuestionAnswerAdvisor。先看代码再解释RestController RequestMapping(/api/qa) public class QaController { private final ChatClient chatClient; private final VectorStore vectorStore; public QaController(ChatClient.Builder builder, VectorStore vectorStore) { this.vectorStore vectorStore; this.chatClient builder .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore)) .build(); } PostMapping(/ask) public MapString, String ask(RequestBody MapString, String request) { String question request.get(question); String answer chatClient.prompt() .user(question) .call() .content(); return Collections.singletonMap(answer, answer); } }这段代码的核心魔法在QuestionAnswerAdvisor。它的工作流程是当你通过chatClient.prompt().user(question)发起请求时Advisor会先拦截这次请求把用户的问题变成向量调用vectorStore做相似度检索取出Top K个匹配片段然后把这些片段以“参考信息”的身份拼接到Prompt里再发给大模型。这里有一个细节很多人没注意到检索出来的片段会以什么格式拼进PromptSpring AI内部会把每个检索结果的content和metadata组合起来形成一个带引用信息的Prompt片段比如【文档文件名第X页】这样的标记会作为上下文提供。大模型在回答时会参考这些内容生成你最后拿到的答案有据可循而不是模型自己在编。QuestionAnswerAdvisor默认检索Top 5个片段你可以通过构造函数传入SearchRequest来调整。我实测下来个人知识库场景Top 5是性价比最高的平衡点太少容易遗漏关键信息太多会让Prompt过长且引入噪音。还有一个比较容易踩的坑ChatClient.Builder这个Bean不是自动注入的你需要自己确认它存在于Spring容器中。如果你发现启动时报找不到ChatClient.Builder多半是少引了spring-ai-starter-model相关的starter或者自动配置被排除掉了。检查一下pom依赖和启动类上的SpringBootApplication注解即可。3.5 支持多种文档格式和增量更新的完整方案上面读文件和入库的示例比较初级实际使用一般要支持按文件上传。我给你一个更完整的思路前端或接口上传文件用MultipartFile接收根据文件扩展名选择合适的DocumentReaderPDF用PagePdfReader、Word和TXT用TikaDocumentReader转换后切分、向量化、入库返回入库条数和成功状态。入库时建议在Document的metadata里保留文件名document.getMetadata().put(file_name, fileName);这样在检索结果里你能知道回答依据来自哪个文件对用户判断答案可信度至关重要。我在做个人知识库时还会在最后把检索到的Top K文件名列在回答下方看起来更像专业产品。4. 实操过程与核心环节实现4.1 搭建工程的第一步初始化Spring Boot项目我建议直接用Spring Initializr创建项目网址是start.spring.io。选择Maven、Java 17、Spring Boot 3.3.x以上依赖勾选Spring Web和PostgreSQL Driver然后手动往pom.xml里加Spring AI相关依赖。为什么要手动加因为Spring Initializr目前对Spring AI的支持还不全AI的starter需要手动引入。创建完工程后第一步先在application.yml里配置好数据源和模型服务。为了不把API Key写死在代码里用环境变量引用是正确做法spring: ai: openai: api-key: ${LLM_API_KEY}启动应用前在IDE的Run Configuration或系统环境变量里设置好LLM_API_KEY。如果你用的是命令行动态设置export LLM_API_KEYsk-xxxx如果你不想依赖远程大模型API直接用Ollama本地模型配置是这样的spring: ai: ollama: base-url: http://localhost:11434 chat: options: model: qwen2.5:7b embedding: options: model: bge-m3两种配置我都测过通义千问的API质量很棒Ollama本地跑的好处是完全免费且数据不出本机各取所需。4.2 从“检索为空”到“能回答”的测试方法推荐先用单元测试或一个简单的CommandLineRunner跑通链路再写REST接口。直接写Controller然后从前端调出错了不好定位是在向量化、检索、还是生成阶段出了问题。我一般的测试步骤是写一个包含以下内容的测试类SpringBootTest class KnowledgeBaseApplicationTests { Autowired private VectorStore vectorStore; Autowired private EmbeddingModel embeddingModel; Autowired private ChatClient.Builder chatClientBuilder; Test void testVectorStore() { Document doc new Document(Spring AI 是 Spring 官方推出的 AI 应用开发框架支持对接各种大模型和向量数据库。); vectorStore.add(List.of(doc)); ListDocument results vectorStore.similaritySearch( SearchRequest.query(Spring AI 是什么).withTopK(3)); results.forEach(r - System.out.println(r.getContent())); } Test void testChatClientWithRag() { ChatClient chatClient chatClientBuilder .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore)) .build(); String answer chatClient.prompt() .user(Spring AI 是什么) .call() .content(); System.out.println(answer); } }第一个测试只验证“文档能否入库、能否检索到”第二个测试验证“检索结果能否正确影响大模型回答”。如果第一个测试失败问题一定在向量化或存储层如果第二个测试失败问题在Prompt组装或模型调用层。分层测试能让你少走很多弯路。测试通过后再通过curl验证接口curl -X POST http://localhost:8080/api/qa/ask \ -H Content-Type: application/json \ -d {question:Spring AI 1.0 有哪些核心组件}4.3 构建带来源展示的问答响应只返回一个答案字符串其实不够实用。更好的做法是把检索来源一并返回这样你能解释系统为何给出这个答案。改造一下刚才的ControllerPostMapping(/ask) public MapString, Object ask(RequestBody MapString, String request) { String question request.get(question); // 先做检索 ListDocument sources vectorStore.similaritySearch( SearchRequest.query(question).withTopK(3)); // 组装Prompt String systemPrompt 你是知识库助手请严格根据下面的参考内容回答问题。 如果参考内容无法回答用户问题请明确说明“知识库中没有相关信息”。 参考内容 %s .formatted(sources.stream() .map(d - d.getContent() (来源: d.getMetadata().get(file_name) )) .collect(Collectors.joining(\n))); ChatClient chatClient chatClientBuilder.build(); String answer chatClient.prompt() .system(systemPrompt) .user(question) .call() .content(); return Map.of( answer, answer, sources, sources.stream().map(d - d.getMetadata().get(file_name)).toList() ); }这一步其实是在手动复刻QuestionAnswerAdvisor做的事情所以你完全可以用框架自带的Advisor省事。但当你想做更精细的控制比如告诉模型“查不到就直说别编”或者想调整来源展示格式手动组装Prompt反而更灵活。两种方式我都用过日常建议先用默认Advisor跑通需要精细控制时再手动改造。4.4 实测效果与参数调整记录我在本地知识库里放了几十篇关于Spring Boot和数据库的Markdown笔记每篇大约2000到5000字入库前切成500到800 token的片段总共生成了两百多个向量。测试时问了一些笔记里的细节问题比如“Spring Boot 3自动配置的原理是什么”回答基本准确而且回答内容明显参考了笔记里的描述而不是百度百科式的通用答案。检索速度方面两百多个向量的查询时间在10毫秒以内PGVector的索引几乎无感知。等数据量涨到几万条HNSW索引的优势才会更明显。参数调整记录上我推荐一个经验值区间参数推荐值说明TokenTextSplitter目标块大小500~800太小丢上下文太大稀释语义TokenTextSplitter重叠大小50~100缓解切分边界语义断裂QuestionAnswerAdvisor的Top K5兼顾信息量和Prompt长度PGVector的index-typeHNSW个人知识库规模下最省心distance-typeCOSINE_DISTANCE文本语义检索首选实际使用时如果你发现有些问题答非所问先降低切分块大小改成300试试如果发现回答太笼统不够具体调大Top K到8让模型看到更多上下文。5. 常见问题与排查技巧实录5.1 启动阶段最常见的三个报错我自己踩过的坑和帮别人排查的问题大部分集中在启动阶段。整理成表格报错信息根本原因解法org.postgresql.util.PSQLException: ERROR: type vector does not exist数据库里没创建pgvector扩展在目标库执行CREATE EXTENSION IF NOT EXISTS vectorBeanCreationException: Error creating bean with name pgVectorStorePGVector自动配置失败检查spring-ai-starter-vector-store-pgvector依赖和数据库连接Caused by: java.lang.NoClassDefFoundError: org/springframework/ai/chat/client/ChatClientSpring AI依赖没引入或版本不一致用Spring AI BOM统管版本检查Maven依赖树“type vector does not exist”这个报错特别有迷惑性因为报错发生在Spring启动过程中你可能会以为是代码问题。其实是建表语句执行时发现数据库里没有vector类型。解决方式很简单到目标库执行CREATE EXTENSION。我提醒过很多次CREATE EXTENSION要连到正确的库执行不是默认的postgres库Spring连接的是哪个库就要在哪个库建。5.2 向量维度不一致和检索结果为空维度不一致通常发生在你在配置文件里写了dimensions但换用了不同Embedding模型之后。比如配置里写1536但实际用了bge-m31024维执行add时会报“expected 1536 dimensions, but got 1024”。解法是修改spring.ai.vectorstore.pgvector.dimensions为模型对应维度然后重建表。重建表最稳妥的方式是把数据库里已有的向量表drop掉让Spring AI重新创建DROP TABLE IF EXISTS vector_store;检索结果为空的问题先检查入库是否真的成功。可以在psql里执行SELECT count(*) FROM vector_store;如果表里没有数据说明入库逻辑没执行或者异常被吞掉了。如果表里有数据但检索不到检查一下Embedding模型是否和入库时一致以及相似度计算的distance-type是否合适。另一个常见原因是你问的问题和知识库内容主题差异太大向量空间里确实没有足够接近的片段这种情况下返回空其实是正常行为。解决办法是让代码对“无结果”的情况做兜底返回提示信息而不是让模型瞎编。5.3 回答质量差先判断是检索问题还是生成问题当答案质量不理想时一定要先判断是检索阶段出了问题还是生成阶段出了问题。判断方法很简单把检索到的Top K片段打印出来肉眼看一下这些片段和问题有没有关联。如果片段关联度很高但回答不对问题出在Prompt组装或大模型本身可以换模型、调Prompt或者让模型“严格基于参考内容回答不要使用外部知识”如果片段本身就不相关问题出在检索或切分调小切分块、换Embedding模型、改用更好的索引都能在一定程度上提升召回率如果检索不到看看是不是metadata里存了太多无关字段导致向量质量下降。有些Embedding模型对输入文本里的干扰字符敏感metadata不会参与向量化而content才是决定向量质量的关键所以入库前要确认content是干净的正文内容不要包含大量HTML标签或乱码。5.4 性能优化与知识库隔离技巧个人知识库数据量不大但如果你把它扩展到团队场景有几个优化点值得提前考虑。第一个是查询过滤。如果你的知识库里有多个项目、多个用户的文档应该在metadata里加一个业务标识比如user_id或project_id然后在检索时通过SearchRequest的filterExpression参数过滤。Spring AI 1.0的SearchRequest支持表达式过滤比如SearchRequest.builder() .query(question) .withTopK(5) .withFilterExpression(projectId projectA) .build();这个功能非常实用既保证了知识的隔离又避免了在同一个向量空间里检索出无关内容。第二个是索引调优。数据量超过十万条时HNSW的内存开销会明显上升。如果服务器内存紧张可以改用IVFFlat并通过PostgreSQL重新建索引来提高召回效果CREATE INDEX ON vector_store USING hnsw (embedding vector_cosine_ops);用向量cosine操作符类创建索引后相似度检索会走索引性能比全表扫描好很多。第三个是异步化。如果你经常要往知识库里灌入大量文档建议把文档解析和向量化放到线程池或消息队列里异步执行否则接口会被长时间阻塞用户上传文件后可能等几十秒才有响应。个人使用可能无所谓但做成服务给别人用时体验差距会很大。6. 部署到电脑上的扩展经验6.1 本地部署和局域网共享很多人搜“个人知识库ai怎么部署在电脑上”其实这套Spring Boot应用打包后就是一个可执行jar文件部署方式和你部署任何Spring Boot服务一样mvn clean package -DskipTests java -jar target/knowledge-base-0.0.1-SNAPSHOT.jar如果你想在局域网内让其他设备访问启动时加一个参数指定监听地址java -jar target/knowledge-base-0.0.1-SNAPSHOT.jar --server.address0.0.0.0然后手机或另一台电脑就能通过局域网IP加端口访问了。个人知识库部署在本地的好处是数据完全在自己手里向量、文档、数据库都在你的电脑上不依赖云端。如果想做得再完整一点把spring-boot-starter-thymeleaf加进来写一个简单的问答页面整个就是一个开箱即用的本地AI知识库客户端。6.2 再结合Spring AI Alibaba的能力做扩展我看最近Spring AI Alibaba社区也很活跃尤其Graph相关的实践可以聊聊怎么扩展。如果你的知识库不只是文档还包括带复杂关系的数据比如人员关系、项目依赖可以考虑在Spring AI的RAG链路上叠加Graph RAG能力把实体关系图作为额外上下文传给模型。这个适合知识库本身具有图谱特征的场景比如企业内部的组织架构、系统依赖关系梳理。Spring AI Alibaba还支持Skills技能机制你可以在问答系统中定义一些固定动作比如“查询天气”调用天气API、“创建待办”写入待办表让大模型在回答时自动触发外部工具。把RAG和Skills结合你的系统就不只是问答助手还能变成能执行任务的Agent。这个方向后续扩展空间很大但一次别贪多先把RAG链路跑稳。7. 提问技巧与使用建议回答质量不仅取决于代码还取决于你怎么组织知识库和怎么提问。我在实践中发现入库前做一次“知识清洗”很有必要把文档里的大标题、目录、页眉都清理掉只保留正文。为什么因为Embedding模型会把整段文本的语义压缩成一个向量如果文本里混入了大量噪音生成的向量就不够纯粹检索效果会打折扣。提问时可以适当明确一点比如“请总结Spring Boot自动配置的流程”比“Spring Boot是什么”更能命中知识库里的内容。你在提示词里还可以加上“如果知识库中没有答案请直接说不知道”防止大模型硬编。有些人觉得这是小事其实这对回答准确率影响很大。我自己测试过不强制约束时模型有超过20%的概率会用通用知识回答而忽略参考资料加上这句之后模型会认真对待检索结果。关于如何用好问答系统我的一个心得是别把知识库做成“文档堆积”入库前花点时间按主题整理文件一个文件尽量聚焦一个主题。这样切出来的片段语义更集中检索命中率会高非常多。所有知识库问答系统的瓶颈最后往往不在技术而在知识库本身的质量。我见过一个特别极端的例子有人把一整本500页的书当做一个片段入库问书里任何具体细节都答不准。切分和整理这两步才是决定系统效果的核心。在实际开发和测试这套系统的过程中我感受最深的一点是Spring AI 1.0把RAG的门槛降得非常低但你如果把框架当成黑盒直接用遇到问题会非常被动。花点时间搞懂Document、EmbeddingModel、VectorStore、Advisor这几个核心抽象再去看官方文档和源码整个链路的每一环都了然于胸排错思路也就清晰了。如果你打算自己搭建个人知识库这套组合是目前Java生态里最值得投入的方案没有之一。本文还有配套的精品资源点击获取
返回列表