发布时间:2026/8/18 12:48:34
Java RAG 实战(第 11 篇):RAG 知识工作台网页 系列导航所属专栏《Java 开发者从零实现 RAG 知识库》学习位置第 11 篇 / 共 12 篇上一篇《第10篇知识管理 API写入、更新与删除》下一篇《第12篇AI 与 RAG 术语索引》当前进度已完成。浏览器已经可以完成知识入库、更新、删除和 RAG 问答。本篇的网页不是新的 RAG 实现。它是前两篇 HTTP API 的可视化入口左边管理知识右边提问并查看回答来源。为什么要学第 8、9 篇完成了后端 API但每次操作都要手写curl和 JSON。这样适合验证接口不适合日常管理知识库也不容易让其他人体验项目。这一篇给已有 API 增加一个单页工作台浏览器页面 ├── 知识文档表单 ──→ KnowledgeController ──→ Qdrant └── 问题表单 ──→ RagController ──→ Ollama Qdrant网页没有替代后端。它只是把用户填写的内容组装成 JSON通过 HTTP 调用前面已经实现的 Controller。本篇目标在 Spring Boot 中直接提供 HTML、CSS 和 JavaScript。用网页提交或替换 Markdown 文档。用网页删除指定documentId的知识。提问并显示模型答案、来源和相似度。处理等待、成功、失败和无来源状态。在刷新页面后恢复尚未提交的表单草稿。完成进度1. 启动依赖和 Spring Boot 应用2. 打开工作台认识左右两条调用链3. 提交一篇带##二级标题的 Markdown4. 提问并查看答案、来源、相似度和 Point ID5. 使用相同documentId更新文档6. 删除测试文档并确认来源消失7. 运行自动化测试第 1 步运行依赖和应用确认 Ollama 已安装模型ollama list确认 Qdrant 正在运行dockerps--filternameqdrant-studycurl-shttp://localhost:6333/collections|jq启动 Spring Bootmvn-f05-spring-rag/pom.xml spring-boot:run浏览器打开http://localhost:8080只需要启动一个 Spring Boot 服务不需要安装 Node.js也不需要再启动一个前端开发服务器。第 2 步理解静态资源目录页面文件位于05-spring-rag/src/main/resources/static/ ├── index.html 页面结构 ├── app.css 布局、颜色和移动端适配 └── app.js 表单状态和 API 调用Spring Boot 会自动寻找classpath:/static/index.html并把它作为/的欢迎页。因此GET http://localhost:8080/ ↓ Spring Boot 静态资源处理器 ↓ static/index.htmlindex.html再加载同源的/app.css和/app.js。网页与 API 都使用localhost:8080所以不需要额外配置 CORS。第 3 步页面怎样提交知识用户填写documentId 文档的稳定身份 source 展示给使用者的资料来源 content 完整 Markdown点击“提交入库”后app.js执行fetch(/api/knowledge/documents,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify({documentId:documentId,source:source,content:content})});完整调用链知识文档表单 ↓ submit 事件 saveDocument() ↓ fetch POST /api/knowledge/documents ↓ KnowledgeController.ingest() ↓ KnowledgeIngestionService.ingest() ↓ 切分 Chunk → Embedding → 删除旧 Point → 写入新 Point使用同一个documentId再次提交就是更新。服务端会先删除这个文档的旧 Chunk再写入本次内容避免残留。第 4 步页面怎样删除知识点击“删除文档”后网页会先显示确认框。确认后调用DELETE /api/knowledge/documents/{documentId}encodeURIComponent(documentId)会把文档 ID 中不适合直接出现在 URL 的字符进行编码。成功响应是204 No Content表示删除成功但没有 JSON 正文因此前端不能继续调用response.json()。删除只根据documentId执行。source和 Markdown 输入框不会参与定位。第 5 步页面怎样完成 RAG 问答点击“开始提问”后网页发送fetch(/api/rag/ask,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify({question:question})});完整链路是问题输入框 ↓ askQuestion() ↓ POST /api/rag/ask RagController.ask() ↓ RagService.ask() ├── bge-m3问题向量化 ├── Qdrant检索相似 Chunk ├── Java组装 Prompt └── qwen3:14b根据资料生成回答 ↓ RagResponse { answer, sources } ↓ renderAnswer() 安全渲染答案和来源页面中的answer来自模型sources来自 Java 对 Qdrant 真实检索结果的映射。来源包含title Chunk 标题 source 原文来源 score 向量相似度 pointId Qdrant Point 身份前端使用textContent写入这些内容不把模型回答当 HTML 执行避免回答中的文本变成页面脚本。第 6 步理解页面状态一次网络请求不是瞬间完成的网页需要明确区分状态页面行为等待表单可以编辑顶部显示“等待首次请求”请求中按钮禁用显示正在入库、检索或生成成功显示 Chunk 数量、答案和来源API 失败优先显示后端返回的message无来源显示回答并明确来源数量为 0requestJson()统一处理所有 HTTP 请求。这样三个操作不用重复解析错误 JSON也能正确区分204和普通 JSON 响应。关键代码一统一处理 HTTP 成功和失败对应源码05-spring-rag/src/main/resources/static/app.jsasyncfunctionrequestJson(path,options){letresponse;try{responseawaitfetch(path,{...options,headers:{Content-Type:application/json,...(options.headers||{})}});}catch(cause){thrownewRequestError(无法连接 Spring Boot 服务,cause);}if(response.ok){if(response.status204){returnnull;}returnresponse.json();}letmessage请求失败HTTP${response.status};try{constapiErrorawaitresponse.json();if(apiError.message){messageapiError.message;}}catch(_){// 非 JSON 错误响应保留 HTTP 状态提示。}thrownewRequestError(message);}这个函数把三类结果收口网络层无法连接、HTTP 成功、HTTP 错误。页面的入库、删除和问答都只需关心各自的业务数据。关键代码二答案和来源分开渲染functionrenderAnswer(result){constsourcesArray.isArray(result.sources)?result.sources:[];elements.answerText.textContentresult.answer||没有返回回答内容。;elements.sourceCount.textContent${sources.length}条来源;elements.sourceList.replaceChildren();if(sources.length0){constemptyItemdocument.createElement(li);emptyItem.textContent本次回答没有可展示的检索来源;elements.sourceList.append(emptyItem);}else{sources.forEach((source,index){elements.sourceList.append(createSourceItem(source,index));});}}answer和sources从 API 响应的两个不同字段读取不会从模型回答文字中猜来源。所有可变文本使用textContent不会当作 HTML 执行。输入草稿保存在浏览器localStorage中。它只是本机浏览器的使用体验功能不会写入 Qdrant真正入库仍然要点击“提交入库”。第 7 步自己验证完整闭环先提交一篇独立测试文档文档 IDweb-demo 来源web-demo.md## Ingress Ingress 用来声明进入集群的 HTTP 和 HTTPS 路由规则。然后提问Ingress 用来做什么应当看到页面返回 AI 回答。来源标题中出现Ingress。来源文件是web-demo.md。页面显示相似度和 Point ID。最后填写web-demo点击“删除文档”。再次提问时这篇文档不应再出现在来源列表中。第 8 步运行测试mvn-f05-spring-rag/pom.xmltestSpringRagApplicationTest会启动随机端口通过真实 HTTP 请求验证/返回 HTTP 200。响应类型兼容text/html。页面包含工作台标题。API Controller、入库服务、Qdrant 映射和 Ollama 客户端仍由原有测试覆盖。常见问题打开根地址仍然是 404网页文件只有在 Spring Boot 重新启动后才会进入运行时 classpath。先停止旧进程再重新执行mvn-f05-spring-rag/pom.xml spring-boot:run检查首页响应curl-Ihttp://localhost:8080/应返回 HTTP200和Content-Type: text/html。页面显示“外部服务暂时不可用”这对应后端 HTTP503。依次检查curlhttp://localhost:11434/api/tagsdockerps--filternameqdrant-studycurlhttp://localhost:6333/collections/kubernetes_chunksOllama 和 Qdrant 是独立进程Spring Boot 启动成功不代表它们一定可用。提交文档时提示没有可入库章节当前拆分规则只索引##二级标题。下面的内容不能入库# 只有一级标题 这段正文没有二级标题。至少增加一个二级标题## 可检索章节 这段内容会生成 Chunk。入库成功但回答资料不足先确认问题与文档内容确实相关再检查页面是否显示写入了至少 1 个 Chunk。documentId是否误删或被其他内容替换。rag.search.minimum-score是否设置过高。回答的sources是否为空。阈值过滤发生在模型调用之前。没有 Chunk 达到阈值时程序会直接返回“根据现有资料无法确定”。端口 8080 已被占用查找占用进程lsof-nP-iTCP:8080-sTCP:LISTEN也可以临时改用 8081mvn-f05-spring-rag/pom.xml spring-boot:run\-Dspring-boot.run.arguments--server.port8081浏览器地址随之改为http://localhost:8081。刷新后仍然出现以前填写的内容这是草稿恢复功能不代表内容又被写入 Qdrant。清空输入框后会同步清空对应的浏览器草稿。删除按钮无法使用先填写要删除的documentId。删除接口只通过文档 ID 定位知识不使用source或 Markdown 内容定位。本篇完成检查可以打开http://localhost:8080可以提交 Markdown 并看到 Chunk 数量可以更新相同documentId的文档可以提问并看到答案和来源可以删除测试文档刷新页面后表单草稿仍在mvn -f 05-spring-rag/pom.xml test全部通过完成这一篇后项目已经从命令行练习发展成可直接体验的本地 RAG 应用。下一阶段可以学习 Docker 镜像、配置外置和 Kubernetes 部署不必继续增加前端框架复杂度。概念混淆时可以回到**《第12篇AI 与 RAG 术语索引》**按照“模型调用、检索、完整 RAG、动态入库”四条主线复习。本篇自测网页会自己调用 Qdrant SDK 吗为什么前端要区分普通 JSON 响应和 HTTP 204为什么模型回答要用textContent渲染不直接使用innerHTMLlocalStorage中有 Markdown 是否代表 Qdrant 中已经有这篇文档判断一次 RAG 验证是否可信为什么要同时看回答和sources参考答案网页只调用 Spring Boot HTTP APIQdrant SDK 在 Java 后端204 没有 JSON 正文继续解析会失败textContent不会把模型文本当成 HTML 执行localStorage只是浏览器草稿回答可能看起来合理但sources才能证明本次确实检索到了哪些资料。本篇小结Spring Boot 直接托管static/下的 HTML、CSS 和 JavaScript不需要 Node.js也不需要额外的前端开发服务器。网页没有替代后端它只是把表单内容组装成 JSON调用已经实现好的KnowledgeController与RagController。页面要显式区分等待、请求中、成功、API 失败和无来源五种状态requestJson()统一处理响应并正确区分204与普通 JSON。答案和来源使用textContent渲染不把模型输出当 HTML 执行避免回答内容变成页面脚本。localStorage只保存表单草稿草稿恢复不等于内容已经写入 Qdrant。下一篇 本专栏下一篇《第12篇AI 与 RAG 术语索引》完整代码都在 GitHub欢迎 Star ⭐本专栏的全部示例代码都已开源包含 5 个可独立运行的 Maven 模块、自动化测试和完整分篇教程。建议Fork / Clone下来边读边跑 https://github.com/bysbsh/ai-rag-learning-guide代码与教程同步更新对照每一篇动手实践效果最好。如果这份教程帮到了你点个Star就是对我最大的支持也方便你之后找回最新版本。遇到问题或发现错漏欢迎在仓库提 Issue / PR。项目采用 MIT 协议可自由学习与二次创作。

相关新闻

2026/8/18 12:48:34

Java RAG 实战(第 10 篇):知识管理 API,写入、更新与删除

系列导航 所属专栏:《Java 开发者从零实现 RAG 知识库》学习位置:第 10 篇 / 共 12 篇上一篇:《第9篇:知识入库基础,切分、向量化与 Point》下一篇:《第11篇:RAG 知识工作台网页》 上一篇已经准…

2026/8/18 12:48:34

cesium 实战系列之地形

cesium三维地形属于webGL,要想加载地形,一定要在初始化的时候将webgl打开,一个是cesium参数中添加viewer new Cesium.Viewer("cesiumContainer", {geocoder: false,sceneModePicker: false,baseLayerPicker: false,navigationHelp…

2026/8/18 12:48:34

【单片机毕业设计】基于 STM32/51 单片机的 ADC0832 水质模拟信号采集控制系统设计 基于 STM32/51 单片机的 LCD1602 水质数据显示与声光报警系统设计(021503)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/18 13:58:41

osu-droid难度星数怎么算?PP与难度计算原理通俗解读

osu-droid难度星数怎么算?PP与难度计算原理通俗解读 【免费下载链接】osu-droid 项目地址: https://gitcode.com/gh_mirrors/os/osu-droid 很多刚接触 osu-droid 的玩家都会好奇:osu-droid难度星数是怎么来的?为什么同样节奏的谱面&a…

2026/8/18 13:58:41

Linux浅谈——管道、网络配置和客户端软件的使用

目录 一、管道 1、管道符 2、过滤功能 3、特殊功能 4、扩展处理 5、xargs命令扩展 二、网络配置 1、ifconfig查看网络信息 2、配置文件详解 网卡配置文件位置 3、systemctl查看网卡状态 4、systemctl启动/重启/停止网卡 三、客户端软件 1、什么是SSH 2、常用SSH终…

2026/8/18 13:58:41

零基础模拟器 root 指南:用 MagiskOnEmulator 保姆级上手

零基础模拟器 root 指南:用 MagiskOnEmulator 保姆级上手 【免费下载链接】MagiskOnEmulator Install Magisk on Official Android Emulator 项目地址: https://gitcode.com/gh_mirrors/ma/MagiskOnEmulator 刚接触 Android 开发的朋友,十有八九都…

2026/8/17 10:49:52

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/18 6:58:27

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/18 0:02:05

Qwen3.8-27B本地部署实战:17GB内存运行270亿参数大模型

1. 这篇文章真正要解决的问题 你是否曾对动辄需要上百GB显存才能运行的百亿参数大模型望而却步?是否觉得在个人电脑上部署一个功能强大的语言模型是天方夜谭?最近,通义千问团队发布的 Qwen3.8-27B 模型,宣称仅需 17GB 内存即可在本…

2026/8/18 0:02:05

ME3169 36V,8A,180KHz 恒压Buck DC-DC 转换器

概述ME3169 是一款180KHz,PWM 模式恒压Buck DC-DC 转换器,8V 到36V 宽工作电压范围,低纹波,内置低导通电阻功率MOS。ME3169 内置环路补偿电路,可以减少外围元器件数量。内部设计有恒压环路,可以通过外部电阻…

2026/8/17 15:07:41

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/17 17:27:06

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/18 7:12:40

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…