
简介本资源是一个基于Spring AI 1.0与PGVector向量数据库构建的个人知识库AI问答系统实战项目面向Java后端开发者、AI应用工程师及深度学习初学者解决个性化知识检索与自然语言问答落地难的问题。项目完整实现文档嵌入、向量化存储、语义检索与LLM响应生成闭环配套深度学习入门课、产业实践案例、面试题库等学习模块兼顾工程实践与能力提升。压缩包共214个文件含84个Java核心业务类如AI配置、向量检索服务、30个TypeScript/21个TSX前端交互组件、20个XML配置与14个PNG界面资源结构清晰前后端分离明确整体仅4.29MB轻量易部署。目前已有127人下载学习提供可直接运行的完整工程骨架、SQL建表脚本、.env环境配置示例及Git提交规范脚本助开发者快速理解RAG架构在Spring生态中的集成路径与调优要点。 平时收集的技术文章、会议笔记、项目文档越来越多散落得到处都是。要用的时候翻半天找不到而且找到了也常常是一大篇里只有一句有用。与其这样不如搭一个个人知识库问答系统——把文档统一存进PostgreSQL靠Spring AI把它们向量化再通过大模型来回答问题。这套方案我用Spring AI 1.0正式版和PGVector完整跑了一遍从Windows环境安装、文档入库到问答链路全部落地整个过程踩了不少坑。这篇就按实际操作顺序把从0到1实现个人知识库AI问答系统的完整过程整理出来包括代码、配置、参数和排错经验适合正在用Java做AI应用的开发者参考也适合想自建知识库问答工具但不想依赖SaaS平台的个人开发者。1. 先想清楚个人知识库问答系统到底要解决什么问题老规矩动手之前先把这个项目到底在做什么聊透。很多人一上来就写代码结果做完才发现回答质量很差、检索结果不相关根本原因就是没搞明白个人知识库问答的技术本质。1.1 一条完整问答链路的四个环节个人知识库问答系统的技术栈官方称呼叫RAG检索增强生成。它解决的核心问题是大模型没有学习过你的私人资料但它又具备极强的文本理解和生成能力所以我们需要先把你的资料找出来再塞给大模型让它基于这些资料回答问题。链路拆开看是四个环节文档加载把PDF、Word、Markdown、TXT等各种格式的资料读出来。切分与向量化把长文本切成适合检索的片段然后通过Embedding模型把每个片段变成一串浮点数向量。这一步的原理是语义相近的文本在向量空间里的距离也相近。相似度检索用户提问时把问题也做一次向量化然后在向量库里找出与问题最相似的文本片段。生成回答把检索到的文本片段作为上下文连同用户问题一起提交给大模型让它基于这些资料作答。这四个环节环环相扣任何一个步骤做得不好都会直接体现在最终回答质量上。所以我强烈建议先想清楚每个环节用什么工具、什么参数再动手写代码。1.2 为什么选Spring AI和PGVector而不选LangChainChroma现在做RAG的框架不少Python生态有LangChain、LlamaIndex向量数据库有Chroma、Milvus、Weaviate。如果你本身就是Java技术栈那么Spring AI几乎是天然选择。Spring AI是Spring官方出的AI应用开发框架1.0正式版里已经把ChatClient、EmbeddingModel、VectorStore这些接口都稳定下来了。它的设计思路跟Spring Boot一脉相承依赖注入、自动配置、Starter机制全都现成。这意味着你不需要自己写一堆胶水代码去对接各家模型和向量库配置好之后就按Spring的习惯写业务代码就行。PGVector则是PostgreSQL的一个扩展插件让传统关系型数据库直接具备向量存储和相似度检索能力。选择它最关键的原因是你的知识库资料不可能只有正文通常还有标题、来源、标签、日期这些结构化元数据。用PGVector可以一边做向量相似度检索一边用SQL对元数据做精确过滤一套数据库全部搞定不需要额外维护一套专门的向量库。如果你的资料量不是百万级以上PGVector完全够用。数据规模再大的话再考虑把向量库拆出来换成Milvus之类但那是后话。1.3 模型接入OpenAI、本地Ollama还是国内云厂商Spring AI 1.0最大的好处之一是模型接入层已经抽象好了。同一个ChatClient接口底层可以接OpenAI、Ollama、阿里云百炼、DeepSeek等不同模型服务。你只需要更换依赖和配置业务代码几乎不用改。个人知识库场景我建议按下面三种情况选对数据隐私有要求、想完全离线跑用Ollama部署本地模型。chat模型推荐qwen2.5:7b或更小的llama3.2embedding模型用nomic-embed-text配上中规中矩的消费级显卡完全够跑。想要最好的回答质量、且网络条件允许接OpenAIchat模型用gpt-4o-mini或gpt-4oembedding用text-embedding-3-small1536维效果确实好。国内访问国外模型服务不稳定时接阿里云百炼或其他国内兼容OpenAI协议的服务。Spring AI里用OpenAI的starter把base-url指向兼容endpoint就行适配成本极低。我个人实际是OpenAI兼容接口和本地Ollama两套配置都保留本地跑不通外网时一键切换。2. Windows下环境准备PGVector安装、PostgreSQL配置、Spring Boot工程初始化这个部分看着基础实际是翻车重灾区。尤其PGVector在Windows上的安装网上资料少且零散不少人卡在这一步根本走不下去。我按自己的踩坑顺序完整过一遍。2.1 PGVector在Windows上的安装两种方式对比PGVector本质上是一个PostgreSQL扩展安装方式取决于你的操作系统。如果你的机器上有Docker最省事的方式是直接用现成的PostgreSQL镜像docker run --name pgvector-db -e POSTGRES_PASSWORDpostgres -p 5432:5432 -d pgvector/pgvector:pg17注意镜像标签要对应你的PostgreSQL版本。但很多Windows本地开发环境没有Docker Desktop或者公司电脑装不了Docker那就只能手动安装。手动安装的流程是这样先去PostgreSQL官网下载并安装PostgreSQL版本建议选16或17安装时记住超级用户密码和数据目录位置。到PGVector的GitHub Releases页面下载Windows预编译包。注意选择跟你的PostgreSQL版本和架构匹配的zip文件比如pg17对应的就是vector-0.8.0-pg17-windows-x64.zip之类的名字。解压后你会看到三个文件vector.control、vector--0.8.0.sql、vector.dll。把它们分别拷贝到PostgreSQL安装目录下的share\extension和lib目录里。重启PostgreSQL服务。在Windows服务管理里找到postgresql-x64-17这个服务右键重启。拷贝路径如果搞混了很容易出现“无法加载扩展”的错误。建议第一次装完后立即用下面的语句验证一次CREATE EXTENSION IF NOT EXISTS vector; SELECT extname, extversion FROM pg_extension;能查出一条vector记录说明装好了。如果你报错说无法加载库文件先检查是不是VC运行库没装去微软官网装最新的Visual C Redistributable再试这是Windows下最常见的坑。2.2 数据库初始化与连接配置扩展装好之后创建知识库专用数据库CREATE DATABASE knowledge_db;然后连接这个库执行CREATE EXTENSION IF NOT EXISTS vector;Spring AI的PgVectorStore会在应用启动时自动创建所需的表结构默认表名叫vector_store理论上是全自动的。但我建议你在库里手动看一眼确认表确实建出来、且embedding字段类型是vector(1536)或vector(768)这一步能提前暴露维度配置问题。连接配置直接用Spring Boot标准的DataSource配置即可spring: datasource: url: jdbc:postgresql://localhost:5432/knowledge_db username: postgres password: postgresPGVector的Java驱动不需要额外引入PostgreSQL官方JDBC驱动已经支持向量类型。前提是JDBC驱动的版本别太老用42.7.x以上基本没问题。2.3 工程依赖引入Spring AI 1.0的BOM管理Spring AI 1.0正式版跟之前的RC版差异不小最大的变化就是官方提供了BOM版本管理变得干净了很多。新建Spring Boot项目时Java版本建议17或21Spring Boot版本用3.4.x或3.5.x。pom.xml里这样引入parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.5.3/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /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 /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement如果你计划用Ollama本地模型就把spring-ai-starter-model-openai替换成spring-ai-starter-model-ollama。如果你想用国内兼容OpenAI协议的服务保留openai这个starter在配置里改base-url即可。2.4 配置文件与多环境切换我个人习惯准备多套配置本地Ollama和OpenAI兼容服务之间一键切换。application.yml里的核心配置如下spring: ai: vectorstore: pgvector: index-type: HNSW distance-type: COSINE_DISTANCE dimensions: 1536 openai: base-url: https://your-endpoint.example.com api-key: ${AI_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.3 embedding: options: model: text-embedding-3-small如果切换Ollama侧配置spring: ai: ollama: base-url: http://localhost:11434 chat: options: model: qwen2.5:7b temperature: 0.3 embedding: options: model: nomic-embed-text vectorstore: pgvector: dimensions: 768这里有个特别容易踩的坑dimensions必须跟你的embedding模型输出维度一致。OpenAI的text-embedding-3-small是1536维nomic-embed-text是768维。配置不一致的时候向量写入时PGVector不会立刻报错但相似度检索结果会完全错乱。我第一次切换模型就因为这个参数没同步改查了半下午才定位到问题。3. 知识库入库文档加载、切分、向量化、存储的全过程链路设计好之后第一步要做的不是问答接口而是先把文档处理入库。这段代码做的事情是把一堆杂乱的PDF和TXT变成PGVector里一个个向量化的文本片段。3.1 文档解析PDF和文本文件的读取方式Spring AI在1.0版本里提供了专门的DocumentReader策略接口官方实现了PDF、TXT、Markdown等常见格式的读取器。PDF读取用PagePdfDocumentReader它会把PDF按页解析成多个Document对象。import org.springframework.ai.document.Document; import org.springframework.ai.reader.pdf.PagePdfDocumentReader; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; Resource resource new FileSystemResource(D:/docs/spring-ai-notes.pdf); PagePdfDocumentReader reader new PagePdfDocumentReader(resource); ListDocument documents reader.get();对于纯文本和Markdown文件直接用TextDocumentReaderTextDocumentReader textReader new TextDocumentReader(new FileSystemResource(D:/docs/meeting-2025-01.md)); ListDocument textDocs textReader.get();如果你需要读取Word文档目前没有官方内置Reader。实际上Word文件可以先转成文本再读取或者用Tika集成自己写一个但那是进阶需求。个人知识库最常见的还是PDF和Markdown先把这两个跑通就够用了。3.2 切分策略为什么不能整篇灌进去文档读取完成后是一个个按页划分的Document直接拿去做向量化其实效果不好。原因有两层第一层是模型上下文窗口限制。大模型对输入长度有限制比如4k、8k tokenPDF里一页几百甚至上千字加上问题和系统提示词后很容易超出限制。第二层是检索粒度问题。向量检索的目标是找到“与问题语义最接近的一小段内容”如果一段文本太长中间混杂太多不相关信息向量会往“平均语义”方向上偏导致检索结果看起来相关、实际不精准。就好比你查“Spring AI的ChatClient用法”结果检索到一整章“Spring AI入门”有效信息被稀释了。所以切分的核心目标是把语义相对完整的内容切成小段每段要有独立的意义。Spring AI提供了TokenTextSplitter它按token数量切分比按字符切分更均匀我实际使用的配置如下import org.springframework.ai.transformer.splitter.TokenTextSplitter; TokenTextSplitter splitter new TokenTextSplitter(800, 300, 5, 10000, true); ListDocument chunks splitter.apply(documents);参数含义说明800每个切块的目标token数量。这个值不是越大越好我测下来800左右在检索精度和上下文完整度之间比较平衡。300相邻切块的重叠token数。切分过程中让相邻片段保留部分重叠能避免一句话被从中间切断导致语义断裂。太大会导致冗余信息多太小则切割处信息容易丢失。5切块的最小字符长度小于这个值的片段会丢弃避免一堆碎片垃圾入库。10000单次处理的最大块数防止内存溢出。true是否保留原始分隔符。3.3 Embedding与写入PGVector切分完成后每个Document就是一个“知识片段”。接下来把它交给Spring AI的EmbeddingModel做向量化然后写入VectorStore。Service public class KnowledgeBaseService { private final VectorStore vectorStore; public KnowledgeBaseService(VectorStore vectorStore) { this.vectorStore vectorStore; } public void ingestPdf(String filePath, String sourceName) { Resource resource new FileSystemResource(filePath); PagePdfDocumentReader reader new PagePdfDocumentReader(resource); ListDocument documents reader.get(); TokenTextSplitter splitter new TokenTextSplitter(800, 300, 5, 10000, true); ListDocument chunks splitter.apply(documents); for (Document chunk : chunks) { MapString, Object metadata new HashMap(chunk.getMetadata()); metadata.put(source, sourceName); chunk.setMetadata(metadata); } vectorStore.add(chunks); System.out.println(入库完成共写入 chunks.size() 个切块); } }这里vectorStore的实例是PGVector自动配置生成的依赖注入即可。vectorStore.add()内部会自动调用EmbeddingModel为每个Document生成向量然后批量写入PostgreSQL。metadata在这个阶段很重要。我给每个切块打上source标签后面检索时可以根据来源过滤比如只想查某一年份的会议纪要时就传一个source条件进去检索精度会提升一个档次。4. 问答链路实现手动RAG与Spring AI 1.0的Advisor入库做完最核心的问答链路开始。Spring AI 1.0里实现RAG有两条路一条是完全手写把每一步都摆在明面上理解透彻另一条是使用框架内置的Advisor机制代码更简洁。我建议先看完手写版理解原理再决定用哪种。4.1 手动RAG检索、拼装、生成手动RAG的代码路径非常直白每个环节都清晰可见。先做相似度检索import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.document.Document; ListDocument docs vectorStore.similaritySearch( SearchRequest.builder() .query(userQuestion) .topK(5) .similarityThreshold(0.5) .build() );query是用户问题文本Spring AI会自动将其向量化并去PGVector中做余弦相似度检索。topK表示返回最相似的5个片段。similarityThreshold是相似度下限低于这个值的片段会被过滤掉0.5是我调出来的一个相对合理的平衡点。检索结果拿到后拼装成上下文再交给ChatClientimport org.springframework.ai.chat.client.ChatClient; String context docs.stream() .map(Document::getText) .collect(Collectors.joining(\n\n---\n\n)); String answer chatClient.prompt() .system(你是一个严谨的知识库助手。请只依据用户提供的资料回答问题。如果资料中没有提到相关内容请直接回答“资料中没有提及”不要编造。) .user(资料如下\n\n context \n\n问题 userQuestion) .call() .content();这是整个RAG最核心的几行代码。sytem提示词约束模型行为user消息把资料和问题一起给它。这样模型在生成回答时只能从资料里找依据而不是凭训练时的记忆自由发挥。完整的Controller可以这样写RestController RequestMapping(/api/qa) public class QaController { private final ChatClient chatClient; private final VectorStore vectorStore; public QaController(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { this.chatClient chatClientBuilder.build(); this.vectorStore vectorStore; } PostMapping(/ask) public String ask(RequestBody AskRequest request) { ListDocument docs vectorStore.similaritySearch( SearchRequest.builder() .query(request.question()) .topK(5) .similarityThreshold(0.5) .build() ); String context docs.stream() .map(Document::getText) .collect(Collectors.joining(\n\n---\n\n)); return chatClient.prompt() .system(你是一个严谨的知识库助手。请只依据用户提供的资料回答问题。如果资料中没有提到相关内容请直接回答“资料中没有提及”不要编造。) .user(资料如下\n\n context \n\n问题 request.question()) .call() .content(); } public record AskRequest(String question) {} }这是最基础、最容易理解的手动RAG版本。没有魔法每一步都清清楚楚。4.2 使用Spring AI 1.0的自动化RAG如果不想手写上面的拼接逻辑Spring AI 1.0提供的QuestionAnswerAdvisor可以自动完成“向量检索 上下文拼装”的过程。你需要做的只是告诉ChatClient一个向量库然后提问。Bean ChatClient chatClient(ChatClient.Builder builder, VectorStore vectorStore) { return builder .defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore) .searchRequest(SearchRequest.builder() .topK(5) .similarityThreshold(0.5) .build()) .build()) .build(); }Controller就变成一行PostMapping(/ask) public String ask(RequestBody AskRequest request) { return chatClient.prompt() .user(request.question()) .call() .content(); }看起来少写了十几行代码但代价是你对检索和拼装细节的控制力变弱了。比如你想在系统提示词里自定义“不知道就说不知道”的约束或者想过滤特定来源的文档用Advisor就需要额外配置promptTemplate和filterExpression。我的建议是第一版试运行用Advisor快速跑通理解你的数据特征之后再换成手动RAG做精细调优。4.3 检索参数怎么调topK、相似度阈值、索引类型这几个参数直接决定回答质量值得花时间讲清楚。topK是返回给模型的文档片段数量。数值太小可能漏掉关键信息数值太大相关资料里混入噪声模型反而被干扰。我测下来1000条以下的知识库建议topK4到61万条以上的库里topK可以放宽到8到10。similarityThreshold是相似度阈值。这个参数官方默认没有设意味着无论多不相似的文档都会被返回。实际使用中如果不设用户问题完全超出知识库范围时模型仍会从一堆不相关内容中“硬答”很容易产生幻觉。我建议设到0.4到0.5之间。设太高的话一些表述变换过的问题会检索不到相关内容出现“知识库明明有答案模型却说不知道”的情况。PGVector的索引类型也是一个重要因素。Spring AI配置里的index-type支持HNSW和IVFFlat两种HNSW基于图的近似最近邻索引查询速度快召回率高适合中小规模数据。缺点是构建索引较慢、占用内存高。个人知识库场景下闭眼选这个。IVFFlat基于倒排列表的索引构建快、占用内存低但需要先有部分数据才能有效建索引而且如果数据分布不均匀召回率会受影响。距离类型方面文本Embedding场景首选COSINE_DISTANCE它对向量绝对值变化不敏感更适合语义相似度比较。默认配置就是它不用改。5. 实测效果与调优回答质量从“能用”到“好用”代码全跑通之后你大概率会遇到一个现实问题能答了但答得不好。这一步是最花时间的没有捷径只能一点点调。我把自己调优的几个关键节点和判断方法写下来。5.1 第一版跑通的基线效果第一版我用了一批Spring Boot技术文档做测试库一共入库了大约2000个切块。直接用一个很具体的问题去问“Spring AI的ChatClient怎么创建”模型的回答基本正确能说出用builder模式构建说明核心链路跑通了。但再换几个问题问题就暴露了。比如问“怎么配置向量数据库”模型居然回答出一堆通用技术方案而我们的库里明明有关于PGVector配置的专门文档。这说明检索环节没有把最重要的那段资料捞出来上下文里给不对。排查这类问题我有一个固定套路先不看大模型的回答而是把检索结果直接打印出来人工检查topK返回的片段是不是真的包含了关键答案。ListDocument docs vectorStore.similaritySearch(...); docs.forEach(doc - System.out.println(相似度: doc.getScore() 内容: doc.getText() 来源: doc.getMetadata().get(source)));这一步是分诊。把“检索的问题”和“生成的问题”切开避免盲目调参数。如果检索结果没问题那就是提示词或大模型本身的问题如果检索结果不对就回头调切分和检索参数。5.2 切分与检索参数的优化针对“检索不到关键信息”的问题我做了三组对照实验。第一组是调整切分参数。原来的TokenTextSplitter切800 token、重叠300但我的技术文档里很多段落本身就比较短切出来之后常常出现“一段讲完A马上又讲B”的混合块。改成切500 token、重叠100之后每个切块的语义更集中检索命中率明显提升。这说明切分大小并不是固定值要根据你文档的平均段落长度来定。第二组是放大topK。从5调到8之后原本排在第6位的正确片段被捡了回来。代价是上下文变长、token消耗增加、偶尔混入无关内容。最终我保持topK6并用similarityThreshold0.45过滤掉低相关片段效果最稳定。第三组是利用好metadata过滤。我的测试库里同时有“Spring AI技术笔记”和“团队会议纪要”两类文档检索时不加来源过滤两块内容经常互相干扰。后面我在问题检索时支持传入source条件SearchRequest.builder() .query(question) .filterExpression(source spring-ai-notes) .build();加了这一层同样的问题准确率又上了一个台阶。这就是为什么入库时强调metadata的重要性——它是后续精准检索的弹药。5.3 提示词工程控制幻觉与“不知道”表达检索质量稳定之后最后一块拼图是提示词。一个常见的误区是只给模型一大堆资料不加任何使用说明模型便会默认“资料里没有的东西也可以凭常识回答”。这是幻觉的主要来源。我最终使用的系统提示词有两层结构效果不错你是一个严谨的知识库助手。 1. 只依据用户提供的资料回答禁止使用资料之外的知识。 2. 如果资料中没有提到相同主题的内容直接回答“资料中没有提及”。 3. 回答时先给出结论再引用资料中的相关表述。 4. 不要编造引用来源不要补充资料中不存在的数据。加上这些约束之后“不知道”和“答错”的情况减少非常明显。另外temperature参数也要注意问答场景我设置为0.3太高会让模型自由发挥太低则显得机械。6. 避坑手记从依赖冲突到维度不匹配的排查实录最后这部分是把实操中遇到的最典型的几个坑完整复盘。这些坑单看都不算大但都很隐蔽报错信息也不太直观容易让人在错误方向上浪费时间。6.1 Spring AI 1.0依赖冲突与版本兼容性问题Spring AI 1.0正式版对Spring Boot版本有明确要求。我在一个老项目里直接升级到Spring AI 1.0结果启动时报了一堆类找不到的异常原因是父POM还是Spring Boot 3.2.x跟Spring AI要求的3.4.x不兼容。解决方案是新建项目时直接用Spring Boot 3.5.x并通过spring-ai-bom管理版本。另外网上很多旧教程用的是0.8.x或1.0.0-M系列的API比如OpenAiChatModel、ChatClient.create()这些写法在新版本里已经被标记为过时或调整位置。确认API正确性最简单的方式是直接看spring-ai-1.0.0对应版本的源码别跟着老博客照抄。6.2 维度不匹配embedding模型与表结构不一致这个坑我前面提到过但它的隐蔽性值得单独展开。我第一次切换Embedding模型时application.yml里忘了改spring.ai.vectorstore.pgvector.dimensions导致写入PGVector时表里的向量字段维度还是旧模型的大小。写入阶段没有报错但之后每次查询都直接抛异常different vector dimensions 1536 and 768排查过程有点曲折报错信息说的是维度和维度不匹配但我一开始以为是查询语句写错了盯着SQL看了半天。后来一查配置才发现dimensions还是1536而新模型nomic-embed-text输出的是768维向量。数据库表结构、Java配置、模型输出三个地方必须统一。这类“隐藏配置”最容易在切换模型时踩中。所以强烈建议把Embedding模型和维度信息写进项目的README切换模型时第一步就检查三处一致性配置文件的dimensions、数据库表字段类型、模型实际输出维度。6.3 检索空白与“答非所问”的排查链路还有一种情况特别容易让人怀疑人生知识库里的文档确实有相关内容但模型回答“资料中没有提及”。这时候按照下面这个顺序排查能快速定位问题出在哪一环。先查原始查询的向量化。直接调用embeddingModel.embed(你的问题)看返回向量维度是否正确、有没有报错。再查检索结果。用相似度检索打印出topK片段的得分和文本看是不是检索直接返回空集合。如果检索为空检查similarityThreshold是否设得过高。0.7的阈值下很多语义相近但字面不同的表述会被过滤掉。如果检索到的片段内容没问题那就是提示词把模型“压制”得太死。比如你要求“资料中没有提到就必须回答不知道”导致模型面对相关性不高的片段时宁可说不知道也不肯推理。这时要调整提示词的表述允许模型基于片段进行适度归纳。最后再检查切分是否把关键句子切成了两半。这种问题很隐蔽我遇到过一次重要结论出现在第500到530 token之间而切分点正好在第512 token后半句被切到下一个块而又恰好没被检索到。按这个链路走80%的“答非所问”问题都能定位到具体环节。个人知识库问答系统做到这一步其实已经是一个可以日常使用的工具了。回顾整个实现过程我觉得最值得分享的一条经验是RAG系统的调优是系统性的检索、切分、提示词三个环节互相牵制不能孤立调某一个参数。我在实际使用中发现先跑通完整链路、再逐步优化检索质量比一开始就追求完美配置要高效得多。后续如果想把系统做得更完整还可以往几个方向扩展接入Spring AI Alibaba的Graph组件做知识图谱增强通过MCP协议把外部工具接入问答链路或者加上文件上传和自动入库的前端页面。希望这篇记录能帮你少踩几个坑把更多时间花在真正有意思的调优和产品化上。本文还有配套的精品资源点击获取