从零构建企业级RAG知识库平台:Spring AI Alibaba实战全历程

📅 发布时间:2026/7/23 2:48:42
从零构建企业级RAG知识库平台:Spring AI Alibaba实战全历程 写在前面大家好。过去几个月我利用业余时间完成了一个让我自己都感到惊喜的项目——Argus百眼巨人一个基于 Spring AI Alibaba 从零构建的 RAG 知识库平台。说实话在开始这个项目之前我对 RAG 的理解还停留在就是给 GPT 外挂一个向量数据库的层面。但当我真正深入进去才发现这条路上有太多值得探索的技术细节——从文档解析到向量索引从混合检索到 Agent 工具编排每一步都让我对 AI 应用开发有了全新的认识。今天我想把这段从 0 到 1 的完整历程分享出来希望能给同样在 AI 应用开发道路上探索的朋友们一些启发。一、为什么选择 RAG Spring AI Alibaba1.1 一个真实的困惑在 2025 年初我开始思考一个问题大语言模型确实很强大但当一个企业想把自己的私有文档产品手册、技术规范、运维指南交给 AI 来回答问题时会发生什么答案让人沮丧——LLM 会编造答案。它不知道你的文档里写了什么只能基于训练数据中的通用知识来推测结果往往是看起来很专业、实际上完全错误的内容。这就是 AI 领域著名的幻觉问题。而 RAGRetrieval-Augmented Generation检索增强生成正是解决这个问题的关键方案。它的核心思想非常简单在 LLM 回答之前先从你的私有文档中检索出最相关的内容然后让 LLM基于这些真实文档来生成回答。这样一来AI 就不再是凭空想象而是有据可查。1.2 为什么选择 Spring AI Alibaba确定了 RAG 的技术方向后接下来面临的是技术选型问题。作为一个 Java 开发者我自然希望能用 Spring Boot 生态来构建这个项目。当时我考察了几个方案方案优点缺点LangChain Python生态最成熟社区资源丰富需要学习 Python 生态与我现有的 Java 技术栈不兼容Spring AI OpenAIJava 原生Spring 生态无缝集成国内访问 OpenAI API 困难延迟高Spring AI AlibabaJava 原生 国内大模型深度集成文档相对较新社区尚在成长中最终我选择了Spring AI Alibaba理由非常务实•DashScope 原生集成通义千问在国内的访问速度和稳定性都非常好API 延迟远低于海外服务•Chat/Embedding 分离架构Chat 走 DashScope 原生 APIEmbedding 走 OpenAI 兼容模式——各取所长•ReactAgent 图执行引擎这是 Spring AI Alibaba Agent Framework 提供的核心能力让我在后来的 V4.0 阶段能够构建出真正的 Agent 对话系统•Spring Boot 生态兼容MyBatis-Plus、Spring Security、Spring Retry 等成熟组件可以无缝接入1.3 项目定位与命名我给这个项目取名为Argus——希腊神话中的百眼巨人。传说 Argus 即使睡着身上的眼睛也始终保持警惕。这个名字完美契合了平台的愿景全面洞察你的私有知识资产让每一次提问都有据可查。项目采用渐进式迭代的开发方式分四个版本逐步构建每个版本聚焦一个核心主题二、V1.0万丈高楼平地起 —— 用户认证与群组协作2.1 为什么从认证开始说实话刚开始我也犹豫过——是不是应该先从炫酷的 AI 对话功能开始但仔细想想一个企业级平台最基础的要求是什么是安全和隔离。如果不能让不同用户的数据相互隔离不能让不同团队在各自的知识库空间中协作那后面所有的 AI 能力都无从谈起。所以 V1.0 我选择从最基础的认证和群组系统开始。2.2 JWT 双令牌认证机制在认证方案上我没有选择传统的 Session 模式而是采用了JWT 双令牌机制这个设计有几个关键考量Access Token 短期化15 分钟即使 Access Token 被泄露攻击者也只有 15 分钟的操作窗口。相比某些系统动辄 24 小时的 Token 有效期这是一个更加保守但更安全的选择。Refresh Token 存储于 httpOnly Cookie 数据库httpOnly 意味着 JavaScript 无法读取XSS 攻击无法窃取。同时Refresh Token 在数据库中也有记录每次刷新时会进行Rotation旧 Token 删除、新 Token 生成这样即使某个 Refresh Token 被盗用使用一次后就会失效。BCrypt 密码加密用户密码在数据库中存储的是 BCrypt 哈希值即使数据库被拖库攻击者也无法还原明文密码。2.3 三级角色权限体系权限设计上我实现了三个层级的角色控制角色权限范围Admin系统管理员可以管理所有用户、查看所有群组Group Owner群组所有者可以邀请成员、审批申请、上传文档、删除文档Group Member群组成员可以查看群组文档、在知识库中提问权限校验的实现采用了多层防御策略——先经过 JWT 认证过滤器再经过角色校验最后在数据查询层面还会附加groupId过滤条件。这样即使某一层出现了漏洞后续的防御层仍然能够保护数据安全。2.4 群组协作机制群组协作支持两种加入方式•邀请制群组 Owner 生成邀请码被邀请者通过邀请码直接加入•申请制用户主动申请加入群组Owner 审批通过后成为成员这两种机制覆盖了不同的协作场景——邀请制适合小团队申请制适合开放的知识社区。三、V2.0打通数据链路 —— 文档管理与 ETL 流水线3.1 挑战大文件上传怎么搞V2.0 是整个项目中最硬核的一个版本。这个阶段要做的事情是让用户能够上传文档然后系统自动把文档解析、切片、向量化、建立索引最终变成可检索的知识。听起来简单实际上光上传这一个环节就让我头疼了好几天。在网络环境下上传大文件比如几百 MB 的 PDF最直接的问题是如果网断了怎么办用户辛辛苦苦传了 95%网络一抖全部从头再来——这种体验简直是灾难。3.2 三阶段分片上传协议为了解决这个问题我设计了一个三阶段分片上传协议这个协议解决了三个核心问题秒传Instant Upload如果同一个群组内已经存在相同 SHA-256 哈希的文档系统直接返回已有文档的 ID完全不占用带宽和存储空间。断点续传上传会话有 24 小时的有效期。如果上传中断客户端重新初始化时会收到uploadedChunks列表已上传的分片序号只需要上传缺失的部分即可。幂等安全分片记录使用 PostgreSQL 的ON CONFLICT ... DO UPDATEupsert语法即使客户端重复上传同一个分片也不会产生脏数据。3.3 ETL 异步流水线上传完成后接下来是文档的消化过程——也就是 ETLExtract-Transform-Load流水线。我采用Spring Event Async的异步机制来驱动这个过程这样上传接口可以立即返回不会让用户等待这里有几个设计细节值得展开为什么用 Spring Event 而不是消息队列对于教学项目来说引入 RabbitMQ 或 Kafka 会增加运维复杂度。Spring Event Async的组合在单机部署场景下完全够用——事务提交后异步触发 ETL不影响 HTTP 响应时间。而且TransactionalEventListener(AFTER_COMMIT)保证了只有在数据库事务成功提交后才会触发处理避免了文档还没落库就开始处理的竞态问题。结构感知切片是什么简单的文本切片按固定字符数切割会破坏文档的语义结构可能在段落中间切断。我的实现会先识别 Markdown 标题层级和段落边界优先在标题或段落边界处切割确保每个切片都是一个相对完整的语义单元。切片过小的就合并到上一个过大的则递归向下拆分。PGvector HNSW 索引参数的选择向量索引使用了 HNSWHierarchical Navigable Small World算法参数m16, ef_construction200。这不是随便选的——m控制每个节点的最大连接数越大检索越快但构建越慢ef_construction控制构建时的候选集大小越大索引质量越高但构建耗时越长。对于百万级以内的数据集m16, ef_construction200是质量和速度的平衡点。四、V3.0让 AI 真正理解你的文档 —— RAG 问答系统4.1 从检索到回答中间还差什么V2.0 完成后我已经有了两路检索能力PGvector 的向量语义检索和 Elasticsearch 的关键词全文检索。理论上拿到检索结果后喂给 LLM就能生成回答了。但实际试了一下发现效果远不如预期。问题出在哪里问题一用户的问题千奇百怪用户问这玩意儿怎么搞“直接拿去检索向量相似度很低但如果改写为文档上传流程和操作方法”检索效果就好很多。这就是**查询规划Query Planning**要解决的问题。问题二两路检索结果怎么融合向量检索返回的相似度分数是 0 到 1 的浮点数ES 返回的 BM25 分数可能是 0 到几十——两种分数不在同一个尺度上没法直接比较。这就是RRF 融合排序要解决的问题。问题三检索到的证据够不够有时检索回来的内容跟问题其实关系不大如果强行让 LLM 回答它还是会编。这就是证据评估要解决的问题。4.2 RAG 问答的完整流程34.3 RRF 融合排序让向量和关键词握手RRFReciprocal Rank Fusion是一个优雅的算法。它的公式简单到只有一行[RRF_score(d) \sum_{c \in channels} \frac{1}{k rank_c(d)}]其中k60是一个平滑参数。这个公式的妙处在于•它在两个通道的排名上做文章而不是原始分数——这就完美解决了分数尺度不统一的问题•某个文档在向量检索中排第 1、在关键词检索中排第 10它的 RRF 分数是1/(601) 1/(6010) 0.0164 0.0143 0.0307•另一个文档在向量检索中排第 3、在关键词检索中排第 3它的 RRF 分数是1/(603) 1/(603) 0.0317可以看到双通道都排名靠前的文档最终得分更高——这正是我们想要的两个通道交叉验证过的结果更可信。融合之后还有两步优化类簇聚合同一个文档中连续的几个切片如果都命中了就合并为一个证据单元提供更完整的上下文邻居窗口扩展每个命中的切片向前后各扩展 1 个切片补充上下文避免碎片化4.4 四级证据评估AI 的自知之明这是整个 RAG 系统中我最喜欢的设计。在传统的搜索 GPT方案中LLM 总是会尝试回答——即使检索到的内容完全不相关它也会编一个听起来合理的答案。我设计了一个四级证据评估机制等级触发条件回答策略NONE检索结果为空直接拒答不调用 LLM省钱WEAK仅单通道命中 文档数 2生成回答但标注依据有限仅供参考PARTIAL双通道命中 OR 文档数 ≥ 2正常回答但标注覆盖不足的方面SUFFICIENT文档数 ≥ 2 AND (双通道命中 OR 最高分≥0.95)正常回答禁止臆测这个设计有两个关键考量文档数量门槛单文档证据即使高相关也可能是文档本身的偏向性——比如一篇产品宣传文可能过度夸大某个功能。至少 2 个不同文档的切片命中交叉验证的可信度才足够高。NONE 级别直接拒答这是成本控制的关键。在 NONE 的情况下LLM 根本不会被调用既节省了 API 费用也避免了一本正经胡说八道的尴尬。4.5 结构化输出与引用溯源LLM 的输出格式不稳定是一个众所周知的痛点。我的解决方案是通过精心设计的 System Prompt 要求 LLM 输出 JSON 格式language-jsonansweredtrueanswer文档上传流程分为三个阶段...reasonCodenullreasonMessagenull同时准备了一个回退解析器——如果 LLM 输出的 JSON 解析失败偶尔会发生就用正则表达式从原始文本中尝试提取answered字段和answer内容。这确保了系统在 LLM 输出异常时也能优雅降级而不是直接报错。每条回答都会附带引用列表Citations包含来源文档名、切片序号、相关性评分。这让用户可以追溯到回答的依据——“这条信息来自哪个文档的哪一段”。五、V4.0让 AI 拥有记忆 —— AI 助手 Agent5.1 从一次性问答到多轮对话V3.0 的 RAG 问答虽然强大但有一个明显的局限每次提问都是独立的。你问什么是 RAG“AI 回答了你再问那它有什么优势”AI 不知道它指的是 RAG。这就像每次对话都在跟一个失忆的人聊天。V4.0 的目标就是解决这个问题——让 AI 助手具备上下文感知的多轮对话能力。5.2 ReactAgent统一对话引擎在技术选型上我选择了 Spring AI Alibaba 的ReactAgent 图执行引擎。这是一个基于图Graph的执行框架支持思考→行动→观察→再思考的循环模式这里有一个有意思的设计决策即使是纯对话模式CHAT我也让它走 ReactAgent 的图执行引擎只是不装配任何工具。这样做的好处是•两种模式共用同一套 Hook、流式处理、错误处理逻辑•代码复用度高不需要维护两套对话处理流程•虽然 CHAT 模式走 Agent 图有一定的微小开销图节点调度、MemorySaver checkpoint但在 LLM 调用时延数秒级面前可以忽略不计5.3 短期记忆三级压缩策略多轮对话面临的核心矛盾是LLM 的上下文窗口有限即使是最新的模型也有上限但对话历史会无限增长。简单的滑动窗口方案只保留最近 N 条消息在长对话中会丢失早期的关键信息。比如用户在对话开始时说我在做一个电力行业的项目30 轮对话后如果你忘了这个背景AI 的回答可能就完全跑偏了。我设计了一个三级渐进压缩策略第一级summary_text会话摘要当会话消息数超过 20 条或 token 估算超过 8000 时触发。规则很简单保留最近 N 条原始消息将更早的消息压缩为用户问了什么助手回答了什么的格式文本。摘要可复用——如果 7 天内没有新消息直接使用已有的摘要。第二级session_memory会话记忆这是最精妙的一级。每当新增 4 条消息或新增 token 超过 1200 时调用 LLM 进行增量更新——不是重新摘要全部历史而是把新消息合并到已有记忆中。Prompt 模板引导 LLM 保留关键事实、用户偏好和重要决策丢弃临时性的寒暄和重复内容。例如用户可能在对话中多次提到我在电力行业工作、“我们的巡检手册要求…”——这些信息会被 session_memory 保留下来而好的、“谢谢”、明白了之类的废话会被丢弃。第三级compact_summary紧凑摘要当会话总 token 超过 6500 时触发。这是对 session_memory 的进一步压缩——基于现有 compact_summary session_memory 待压缩消息生成更精炼的版本。Prompt 引导 LLM 只保留最核心的信息。运行时压缩最后防线如果前面的压缩机制全部失效理论上不应该发生还有一个硬编码的 50000 token 阈值。超过时直接截断消息列表只保留末尾 3 条。这是一个逃生舱确保系统永远不会因为上下文溢出而崩溃。5.4 BEFORE_MODEL Hook无侵入的上下文注入你可能会问这些摘要和记忆是怎么喂给模型的这就用到了 ReactAgent 框架提供的MessagesModelHook机制。我实现了一个BEFORE_MODELHook在每次模型调用之前自动执行1从RunnableConfig的 metadata 中读取userId、sessionId、toolMode、groupId2从数据库中加载该会话的compactSummary、sessionMemory、最近消息3按顺序组装消息列表[compact summary 作为系统消息] → [session memory 作为系统消息] → [历史消息 1] → [历史消息 2] → ... → [工具调用结果如果有] → [当前用户问题]4使用REPLACE 模式完全替换 Agent 框架默认的消息列表这整个过程中Agent 的业务代码完全不需要关心上下文是怎么组装的——Hook 在框架层面自动完成了全部工作。这种无侵入的设计让代码保持了很高的内聚性。5.5 SSE 流式输出与 Delta 去重流式输出听起来简单——模型生成一个字就推送一个字——但实际上有一个坑某些模型后端在流式模式下返回的不是增量 delta而是截至当前的全文。如果直接透传给前端用户会看到不断重复的前缀文字。我的解决方案是用一个StringBuilder持续累积已推送的文本。每次收到新文本时检查它是否以已累积的文本为前缀——如果是就裁掉前缀只把真正的增量推送给前端第 1 次收到: RAG → 推送 RAG 第 2 次收到: RAG检索增强 → 推送 检索增强 第 3 次收到: RAG检索增强生成是一种 → 推送 生成是一种同时还有一个AGENT_MODEL_FINISHED兜底路径——某些模型不走逐字流式通道而是直接在 finished 节点返回全文。此时如果finalReply为空就将完整文本作为一次性 delta 发送。六、前后端分离与实时通信6.1 前端技术选型前端我选择了Vue 3 TypeScript Element Plus的组合。选择 Vue 3 的理由很简单它的 Composition API 让组件逻辑的组织更加清晰TypeScript 的类型系统能在编译期就发现大量潜在问题。状态管理使用了PiniaVue 3 官方推荐的状态管理库相比 Vuex 更加轻量且 TypeScript 支持更好。Markdown 渲染使用了marked库支持 GFMGitHub Flavored Markdown语法。6.2 流式对话的前端实现SSE 流式对话的前端实现使用fetchAPI ReadableStreamlanguage-typescriptconstawait/api/assistant/chat/streamPOSTContent-Typeapplication/jsonAuthorizationcolor:#ce9178JSONconstconstnewwhiletrueconstawaitifbreakcolor:#6a99556delta新文本color:#6a99556前端收到delta事件后将文本增量追加到消息显示区域实现逐字打印的打字机效果。done事件到达后将完整消息保存到 Pinia store 中。七、未来发展规划项目目前已经完成了 V4.0但这只是开始。我计划在后续版本中逐步增加以下能力V4.1 — 体验优化•会话记忆可视化在前端展示压缩摘要内容让用户了解 AI “记住了什么”•消息分页加载当前只支持加载最近 N 条消息长会话需要分页支持•会话归档与恢复将不活跃的会话归档需要时再恢复V4.2 — 工具扩展•更多 Agent 工具文档管理工具列出文档、搜索文档、群组管理工具查看成员、查看统计•多工具协作Agent 可以在同一轮对话中调用多个工具处理更复杂的用户请求•工具调用可视化在前端展示 Agent 的思考过程——它调用了哪些工具、得到了什么结果V4.3 — 对话增强•对话分支从任意消息节点创建分支对话探索不同的回答方向•消息编辑与重新生成编辑已发送的消息让 AI 基于修改后的内容重新回答•Prompt 版本管理支持不同版本的 System Prompt方便 A/B 测试V5.0 — 重大升级•多模态支持除了文本文档支持图片、表格等多模态内容的检索与问答•WebSocket 升级将 SSE 替换为 WebSocket支持双向实时通信•前端管理控制台全面升级文档管理、群组管理、问答历史、数据统计等功能的完整控制台•消息队列迁移将 Spring Event 异步机制升级为 RabbitMQ/Kafka支持分布式 Worker 调度八、学习心得与经验总结8.1 从文档出发而不是从教程出发整个开发过程中我最大的感受是官方文档是最好的学习资料。Spring AI Alibaba 的官方文档写得很用心——不仅告诉你 API 怎么用还解释了背后的设计理念。比如关于 Chat/Embedding 分离提供者的说明让我理解了为什么 Embedding 要走 OpenAI 兼容模式而不是 DashScope 原生 API因为 Spring AI 的 OpenAI embedding 客户端更成熟稳定。相比之下网上的很多教程往往只给代码不给原理看完之后知其然不知其所以然。遇到稍微复杂一点的需求就束手无策了。8.2 渐进式迭代的力量这个项目分了四个版本每个版本聚焦一个主题。这种方式让我在每个阶段都能保持专注不会被过多未完成的功能分散注意力。更重要的是每个版本的交付物都是可用的——V1.0 有可用的认证系统V2.0 有可用的文档上传和检索V3.0 有可用的 RAG 问答V4.0 有可用的 Agent 对话。这种每一步都有交付的开发节奏不仅给了我持续的正反馈也让我在每个阶段都能进行完整的测试和验证。8.3 遇到的技术挑战与解决思路挑战一Markdown 预览变成纯文本在开发文档预览功能时我发现 MD 文件预览只显示纯文本没有任何格式。排查后发现后端MdDocumentParser.stripMarkdown()方法会主动剥离所有 Markdown 语法#、**、列表标记等返回的是处理后的纯文本。修改方案是让 MD 文件绕过解析器直接从 MinIO 读取原始内容返回给前端由前端的marked.js完成渲染。教训调试时要沿着完整的数据链路排查——从前端请求到后端处理再到数据存储任何一个环节都可能是问题所在。挑战二SSE 流式输出的 Delta 去重前面提到过某些模型后端返回的是全文而不是增量。这个问题花了我不少时间排查——一开始我以为是前端解析 SSE 事件的逻辑有 bug后来才发现是后端推送的内容本身就是重复的。教训不要假设第三方组件的行为一定符合预期。即使文档上说流式推送实际行为也可能因模型后端的不同而有差异。挑战三短期记忆的并发写入冲突在多轮对话中用户发送消息后BEFORE_MODEL Hook和AFTER_AGENT回调都可能触发记忆更新。如果两个操作同时尝试更新assistant_session_contexts表就会产生并发冲突。我的解决方案是使用乐观锁——在更新 SQL 的 WHERE 条件中加入context_version #{expectedVersion}更新失败影响行数为 0时抛出异常回滚事务。教训在涉及状态变更的系统中并发控制是一个必须从一开始就考虑的问题。8.4 给想入门 RAG 开发的建议如果你也想从零开始构建一个 RAG 应用我的建议是1先理解原理再动手写代码。搞清楚 Embedding 是什么、向量检索怎么工作、RAG 的完整链路是怎样的——这些基础知识会让你在遇到问题时更容易定位原因。2从最简单的实现开始。先用最直接的方式跑通文档上传 → 向量检索 → LLM 回答这个核心链路然后再逐步优化检索质量、增加 Agent 能力、引入记忆管理。3Spring Boot Spring AI Alibaba 是一个很好的起点。如果你有 Java 基础这个组合让你可以在熟悉的生态中快速构建 AI 应用不需要额外学习 Python 或 LangChain。4记录你的踩坑过程。我在开发过程中养成了记录遇到的问题和解决方案的习惯这不仅帮助我自己理清思路也让我在写这篇文章时能够回顾当时的思考过程。写在最后从一行代码都没有到最终交付一个包含认证授权、文档管理、ETL 流水线、混合检索、RAG 问答、Agent 对话、短期记忆管理的完整平台这段旅程让我深刻体会到AI 应用开发不是调 API那么简单它需要你对检索、存储、并发、架构等基础工程能力有扎实的理解。但正是这种全栈的挑战让整个过程充满了乐趣和成就感。如果你对这个项目感兴趣欢迎访问 GitHub 仓库[1] 查看完整源码也欢迎提 Issue 和 PR 一起讨论改进。让每一次提问都有据可查 —— 这是 Argus 的初心也是我对 AI 应用开发的信念。学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】