02 几十行Java代码,搭一个自己的AI知识库

02 几十行Java代码,搭一个自己的AI知识库
摘要本文通过一个生动的学霸带小抄比喻深入浅出地解释了 RAG检索增强生成的核心原理。文章详细演示了如何使用 Spring AI 框架仅用几十行 Java 代码快速搭建一个可运行的 RAG 问答系统涵盖 Maven 依赖配置、文档注入、向量检索、Prompt 设计等关键步骤。针对 Java 开发者面临的 Python 生态迁移难题提供了从理论到实践的完整解决方案并分享了生产环境中的常见坑点与面试标准回答帮助读者快速掌握 RAG 的落地能力。上篇文章结尾我留了个钩子下一篇用几十行 Java 代码搭一个能用的 RAG demo。有人私信我说老哥你别是画饼吧几十行能干啥今天就把饼烙出来给你看。先说说为什么我坚持用 Java 来搭这玩意儿。你去搜 RAG 教程十个有九个用 Python。什么 LangChain、LlamaIndex环境配置就能折腾一整天。装好之后才发现你公司的后端全是 Java你一个人用 Python 写了个 Demo没人能接。我就问你你写了个没人能接的 Demo图什么呢Spring AI 不一样。Maven 加依赖——注入——调 API全是 Java 后端玩顺手的东西。搭好之后直接塞进你现有的 Spring Boot 项目里Git 一提交同事就能用。什么叫落地能力这就是。先说清楚 RAG 是干嘛的别着急上代码。先说个例子你听着。你家楼下有个学霸什么都会。你问他1953年冬奥会在哪办的他秒回——奥斯陆。这是大模型。厉害吧但你问他咱们公司上个月的退货率是多少他傻了。他不知道你们公司的数据。他还不知道你们公司叫啥。这是大模型真正的短板——训练数据截止了私域数据更是一点都没学到。RAG 的做法是什么你不是问我公司数据吗好我先去数据库里翻一翻找到上个月所有退货订单统计一下——哦退货率 3.2%。然后我拿着这个数据告诉学霸这是你要的背景信息现在你帮用户回答要参考这些数据别瞎编。RAG 带小抄的学霸。先翻资料再答题。流程图走一遍1. 你上传一堆文档Word、PDF、TXT→ 系统读出来2. 大段文档切成小块太长了检索不准3. 每块转成向量数学上的语义编号4. 存到向量数据库里备用5. 用户提问 → 把问题也转成向量 → 到库里找最像的几个块6. 拿出来组装成 Prompt → 扔给大模型 → 大模型看完再回答一共三步存进去 → 搜出来 → 回答。第一步Maven 依赖咱们不用几百个依赖。Spring AI 的核心就这几个dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version1.0.0-M6/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pgvector-store-spring-boot-starter/artifactId version1.0.0-M6/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pdf-document-reader/artifactId version1.0.0-M6/version /dependency看出来了吗没有 Python、没有 PyTorch。全是 Spring Boot 那一套starter 开箱即用。OpenAI starter → 帮你配好了 ChatClient你直接Autowired就能调 GPT。pgvector starter → 帮你连上了 PostgreSQL 的向量插件不需要自己去写 JDBC 查询向量距离。PDF reader → 文件解析工具把 PDF 内容读成字符串。pgvector 是什么就是 PostgreSQL 的一个插件给普通数据库加了向量检索的能力。不需要额外部署一个 Milvus——当然大流量场景 Milvus 更强这个咱们第4篇聊。第二步文档注入——把知识存进去假设你有一堆项目文档PDF 和 TXT 混着。你想做成一个内部问答系统。第一步就是把它们倒进数据库Service public class DocumentIngestionService { Autowired private VectorStore vectorStore; public void loadDocuments(String pdfPath, String txtPath) throws Exception { // 1. 读 PDF PdfDocumentReader pdfReader new PdfDocumentReader( new FileSystemResource(pdfPath)); ListDocument pdfDocs pdfReader.read(); // 2. 读 TXT TextDocumentReader txtReader new TextDocumentReader( new FileSystemResource(txtPath)); ListDocument txtDocs txtReader.read(); // 3. 合并 ListDocument allDocs new ArrayList(); allDocs.addAll(pdfDocs); allDocs.addAll(txtDocs); // 4. 自动切块默认每块 800 tokens重叠 200 TokenTextSplitter splitter new TokenTextSplitter(800, 200); ListDocument chunks splitter.apply(allDocs); // 5. 写入向量数据库 // 这一步会自动调 OpenAI 的 embedding API 转成向量 vectorStore.accept(chunks); System.out.println(成功注入 chunks.size() 个文档块); } }来一句一句解释PdfDocumentReader—— Spring AI 内置的 PDF 解析器。你把文件路径给它它把 PDF 一页一页读成Document对象。每个 Document 里有两个字段content存文字内容metadata存页码、文件名这些信息。TokenTextSplitter(800, 200)—— 切块器。800 是每块最大的 token 数不是字数。200 是块与块之间的重叠 token 数。为什么要有重叠因为一段话可能在上一块结尾和下一块开头之间被切断了。重叠保证关键信息不会刚好被切丢。vectorStore.accept(chunks)—— 神奇的一行。你传入文档列表它自动调用 embedding 模型把每个块转成向量 → 把向量和原文一起存到 pgvector。一行代码做了三件事。第三步检索问答——问它文档存好了现在有人来问问题了Service public class RAGService { Autowired private VectorStore vectorStore; Autowired private ChatClient chatClient; public String ask(String question) { // 1. 把问题转向量在库里搜最像的 3 个块 ListDocument similarDocs vectorStore .similaritySearch(SearchRequest.query(question) .withTopK(3)); // 2. 把搜到的内容拼成上下文 String context similarDocs.stream() .map(Document::getContent) .collect(Collectors.joining(\n---\n)); // 3. 组装 Prompt String prompt 你是公司内部知识库助手。 请只根据以下资料回答问题。 如果资料里找不到答案直接说「我不知道」不要编。 资料 %s 问题%s .formatted(context, question); // 4. 调大模型 return chatClient.prompt() .user(prompt) .call() .content(); } }再一句一句拆vectorStore.similaritySearch(...)—— 这就是 RAG 的核心操作。把你问的问题转成向量之后去库里做余弦相似度搜索找到最相关的 topK 个文档块。withTopK(3)意思是要 3 条。太少了信息不够太多了会引入噪音3-5 是个经验值。Document::getContent—— 取出每个文档块的原文字段。注意这里取的是content不是metadata。metadata 只存辅助信息页码、文件名content 才是你当初写进去的那段话。prompt—— 模板字符串。关键设计加了不要编的指令。很多人用 RAG 效果不好不是因为向量搜得不准是因为大模型自以为是——搜到了 A 却觉得 B 更合理自己脑补了一段。你必须告诉它有资料就用资料没资料就说不知道。chatClient.prompt().user(prompt).call().content()—— 经典的 Spring AI 调用链。prompt()创建一个 Prompt 对象 →user()设置用户消息 →call()发出去等结果 →content()拿到模型返回的文字。跑起来需要什么配置别忘了在application.yml里配一下spring: ai: openai: api-key: ${OPENAI_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.1 vectorstore: pgvector: index-type: HNSW distance-type: COSINE_DISTANCE index-list-size: 100 datasource: url: jdbc:postgresql://localhost:5432/rag_db username: postgres password: postgrestemperature: 0.1—— 温度。越低回答越死板越精确RAG 场景就该低你不需要它创意发挥。0.9以上适合写诗写段子不适合回答技术问题。index-type: HNSW—— 向量索引类型。HNSW 是一种多层图的检索算法搜索快适合生产。你可以简单理解为给数据库里的向量建了个网状寻路图找东西不用逐个比顺着图跳着走几步就到了。COSINE_DISTANCE—— 向量距离算法。就是把两个向量比一下方向是不是接近。方向一致就是相似cos1方向相反就是不相关cos-1。整个流程串起来是什么样你打开 IDE。跑Application.java。控制台打一行日志。然后你在 Postman 发个请求GET /api/ask?question咱们项目的线上监控系统是怎么设计的等了大概 3 秒调大模型 查向量数据库的时间返回我们项目使用 Prometheus Grafana 进行线上监控。 关键指标包括JVM堆内存使用率、接口响应时间P99、GC暂停频率和时间。 告警通过钉钉机器人推送到值班群级别分为WARNING和CRITICAL。 详细监控配置文档见http://wiki.internal/metrics-setup你什么都没干就是上传了几份 PDF写了二十多行代码。但是对外可以说我用 Spring AI 搭建了内部知识库问答系统实现了基于 RAG 的文档智能检索。你猜面试官听到这个会不会眼睛一亮踩坑提示我说几个我遇到的坑你留着看坑一OpenAI 的 embedding 模型有输入长度限制。text-embedding-3-small最长 8192 tokens。超过的文档块会被静默截断。Spring AI 的 TokenTextSplitter 默认 800 一块基本不会超。你要自己切的话注意控制块大小。坑二首次启动会创建向量表自动做。但如果你改了 schema得手动 DROP 重建。Spring AI 的 VectorStore 实现会在启动时CREATE TABLE IF NOT EXISTS。索引也是自动创建的。这很方便但你要改索引类型比如从 IVFFlat 换 HNSW它不会自动重建。坑三大模型可能还是会在你不想要的时候创意发挥。上面的代码加了不知道就说不知道的约束但大模型不是 100% 听话的。生产环境一定要加校验层让大模型给出引用的文档块 ID前端可以点进去看原文验证。我见过一个 RAG 系统员工问薪资政策大模型自己编了一套——法务部门差点炸了。 面试官视角的标准回答如果面试官问你用过 RAG 吗具体怎么实现的用 Spring AI 搭的。就是标准的三阶段 RAG 流程——注入、检索、生成。注入阶段用 Spring AI 内置的 PdfDocumentReader 和 TokenTextSplitter 处理文档。切块策略我设的 800 token 加 200 token 重叠存进 PostgreSQL pgvector。Spring AI 的 VectorStore 接口封装了 embedding 存储一行代码搞定。检索阶段用 similaritySearch 做向量检索topK 设 3。问题是用户输入的问题本身也会被 embedding 转成向量然后去库里做余弦相似度匹配。生成阶段搜到的文档块拼成 context塞进一个预设好的 Prompt 模板里。Prompt 里加了指令约束——有资料用资料没资料说不知道不让模型自由发挥。调通用 chatClient.prompt().user(prompt).call().content()。整体效果还可以。几十行代码搞定了一个能在内部用的文档问答系统。当然生产化还需要做超时处理、多轮对话的上下文管理、以及大模型输出的校验必须带引用原文。好这一篇先把 RAG 的骨架搭起来了。下一篇我们聊一个更虚但更重要的东西——Prompt 怎么写大模型才有你想要的答案。别觉得 Prompt 就是随便说句话。同一道题好 Prompt 和烂 Prompt 的效果能差 40%。私信回复「666」一次性领走面试宝典Java 高频考点速查表、HashMap/ConcurrentHashMap 源码笔记、JVM 调优案例、Spring Boot 面试 50 问AI 编程工具箱Cursor/Copilot/Codex 六工具对比表、10 个 Prompt 模板、Debug 万能公式、Cursor 速查手册、AI 图片生成入门、30 效率工具包一份资料包两个专栏都能用。「唠点键盘之外的」只讲干货。