十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Java RAG 实战(第 11 篇):RAG 知识工作台网页

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 协议可自由学习与二次创作。
返回列表