news 2026/7/16 4:56:47

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pdf-document-reader</artifactId> <version>1.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)); List<Document> pdfDocs = pdfReader.read(); // 2. 读 TXT TextDocumentReader txtReader = new TextDocumentReader( new FileSystemResource(txtPath)); List<Document> txtDocs = txtReader.read(); // 3. 合并 List<Document> allDocs = new ArrayList<>(); allDocs.addAll(pdfDocs); allDocs.addAll(txtDocs); // 4. 自动切块(默认每块 800 tokens,重叠 200) TokenTextSplitter splitter = new TokenTextSplitter(800, 200); List<Document> 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 个块 List<Document> 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: postgres

temperature: 0.1—— 温度。越低回答越"死板"越精确,RAG 场景就该低,你不需要它创意发挥。0.9以上适合写诗写段子,不适合回答技术问题。

index-type: HNSW—— 向量索引类型。HNSW 是一种多层图的检索算法,搜索快,适合生产。你可以简单理解为:给数据库里的向量建了个"网状寻路图",找东西不用逐个比,顺着图跳着走几步就到了。

COSINE_DISTANCE—— 向量距离算法。就是把两个向量比一下方向是不是接近。方向一致就是相似(cos=1),方向相反就是不相关(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+ 效率工具包

一份资料包,两个专栏都能用。「唠点键盘之外的」,只讲干货。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/16 4:56:25

Unity自动化构建实战:Jenkins+Groovy+CLI打造高效CI/CD流水线

1. 项目概述&#xff1a;为什么我们需要自动化构建&#xff1f;在Unity游戏开发团队里&#xff0c;打包这件事&#xff0c;说多了都是泪。我见过太多项目&#xff0c;每次出包都像一场小型战役&#xff1a;美术同学在群里催着要测试包&#xff0c;程序同学得停下手中的开发&…

作者头像 李华
网站建设 2026/7/16 4:55:12

【赵渝强老师】Hadoop HDFS的回收站和快照

Hadoop的HDFS除了最基本的上传数据和下载数据的功能以外&#xff0c;还提供了很多高级特性用于方便使用和操作&#xff0c;主要有&#xff1a;回收站、快照、配额管理、安全模式、权限管理&#xff0c;同时从Hadoop 3.x开始还提供了纠删码技术。 视频讲解如下 【赵渝强老师】H…

作者头像 李华
网站建设 2026/7/16 4:54:59

C++移动构造函数:原理、实现与性能优化实践

1. 项目概述如果你写过C&#xff0c;尤其是处理过动态内存或者大型对象&#xff0c;肯定对深拷贝带来的性能开销深有体会。每次复制一个包含指针的类对象&#xff0c;都要重新分配内存、拷贝数据&#xff0c;这在处理容器&#xff08;比如std::vector&#xff09;或者函数返回值…

作者头像 李华
网站建设 2026/7/16 4:53:08

C++入门指南:从零开始掌握编程核心与底层逻辑

1. 项目概述&#xff1a;为什么从C开始你的算法之旅&#xff1f;如果你点开了这篇文章&#xff0c;大概率是想踏踏实实学点东西&#xff0c;无论是为了找工作、打比赛&#xff0c;还是单纯想搞懂计算机到底是怎么工作的。在众多编程语言里&#xff0c;我为什么建议你&#xff0…

作者头像 李华
网站建设 2026/7/16 4:52:02

Cursor + MCP:地理空间开发的自然语言新范式

1. 项目概述&#xff1a;为什么“Cursor 中使用MCP”正在成为地理智能开发的新分水岭最近两周&#xff0c;我在三个不同行业的客户现场做技术方案支持&#xff0c;从智慧物流调度系统到城市规划数字孪生平台&#xff0c;再到一个农业遥感数据处理工具链&#xff0c;几乎每场技术…

作者头像 李华
网站建设 2026/7/16 4:51:45

C++性能优化实战:深入缓存原理与数据结构设计提升程序效率

1. 项目概述&#xff1a;为什么缓存优化是C性能的“胜负手”干了十几年C&#xff0c;从桌面应用到嵌入式&#xff0c;再到高并发服务器&#xff0c;我踩过最大的坑&#xff0c;往往不是算法逻辑写错&#xff0c;而是性能瓶颈藏在你意想不到的地方。很多时候&#xff0c;代码逻辑…

作者头像 李华