ARTICLE DETAIL

资讯详情

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

【Spring AI 实战 · 阶段三·篇1】RAG 检索增强起步与知识库底账化

【Spring AI 实战 · 阶段三·篇1】RAG 检索增强起步与知识库底账化 系列说明一个 Java 后端视角的 Spring AI 渐进式实战教程载体为开源项目「劳小司 · 智能法律助手」。序章技术栈全景与 AI 学习指南阶段一 · 流式对话内核篇1 SSE 流式·停止·思考可见化 / 篇2 会话记忆压缩与滚动体验阶段二 · 工具调用篇1 Function Calling 与法律计算器 / 篇2 联网搜索与工具预算阶段三 · RAG 知识库篇1 起步与底账化本文/ 篇2 Agentic RAG 与引用可信度 / 篇3 检索质量与体验阶段四 · 多模型路由篇1 五路级联路由阶段五 · 安全与质量门篇1 安全层与强制检索 / 篇2 质量门与评估门禁 / 篇3 指代消解与阻塞隔离阶段六 · 产品化与用户体系篇1 认证·配额·门禁 / 篇2 前端·移动端·身份 / 篇3 劳动法专精与多模态阶段七 · 存储演进与部署篇1 存储迁移 / 篇2 部署契约本篇涉及rag/KnowledgeImportService.java、rag/LawArticleEntity.java、application.ymlpgvector 段。一、RAG 要解决的两个硬伤问大模型劳动合同法第 47 条经济补偿怎么算它大概率能答个大概但有两个硬伤幻觉条文编号张口就来无法验证和不可更新知识停在训练截止日你自有的条文它一条都装不进。RAG检索增强生成的思路用户提问 → 先去知识库检索相关条文 → 把条文拼进 Prompt → 模型有法可依地回答模型的角色从背书答题变成开卷考试。本篇先把最基础的一环打通——向量化 检索再解决一个真正的工程问题法条会增删改知识库怎么低成本同步。二、三个技术零件① Embedding把文本编码成高维向量语义相近则向量距离近。本项目用百炼qwen3.7-text-embedding-flash1024 维。用户哪怕用大白话被公司辞退能拿多少也能命中规范表述解除劳动合同经济补偿。② 向量库Spring AI 把各家向量库统一成VectorStore接口——add(documents)写入、similaritySearch(request)检索。换向量库只换 starter业务代码不动。本项目用pgvectorPostgreSQL 的向量扩展 HNSW 索引好处是底账、向量、会话记忆同库运维最简序章第五节展开过存储可插拔。③ Naive RAG最朴素的用法——每轮对话前置检索 Top-K拼进 System Prompt。它有三个问题闲聊也白检索、检索词就是用户原话、只能检一轮正是篇2 要改进的地方。本篇先把检索得到、同步得起做扎实。三、底账化唯一事实源 可重建索引这是本篇的核心工程决策。知识库不是一坨向量而是两层PG law_article底账唯一事实源 │ 导入管道全量扫描 → MD5 对比 → 向量化 ▼ PG vector_storepgvector 检索索引可随时用底账重建铁律law_article是唯一事实源vector_store只是检索索引——它随时可以删掉重建坏了不心疼。这条分工让换向量库“重建索引”改 embedding 模型都变成低风险操作。早期是启动时一次性把所有条文灌进向量库法条一多每次改一个字就要全量重嵌embedding 费用和时间都扛不住。于是引入增量同步。四、MD5 指纹增量同步新增 / 变更 / 删除三态每条条文算一个 MD5 指纹和指纹底账表rag_ledgerarticleId → md5;docId列表对比得出三态差异只对差异部分做向量化Stringhashmd5(article.toEmbeddingText()|embeddingModel);// 注意掺了模型名StringoldoldHashes.get(idStr);if(oldnull||!old.startsWith(hash;)){if(old!null){vectorStore.delete(parseDocIds(idStr,old));// 变更先回收旧向量}pending.add(newArticleSync(idStr,ledgerValue,docs));// 新增/变更待重嵌}// 底账有、PG 已删/逻辑删 → 回收向量SetStringtoDeletenewHashSet(oldHashes.keySet());toDelete.removeAll(newHashes.keySet());改一个字都会触发该条重嵌没变的一条都不动。468 条法条里只改 3 条就只重嵌 3 条。一个关键设计——指纹掺入 embedding 模型名md5(文本 | embeddingModel)。换向量模型后所有指纹失配自动触发全量重建。为什么因为新旧模型的向量不在同一语义空间混在一起检索质量会静默劣化不报错但结果变差最难查。把模型名掺进指纹换模型 必然全量重建杜绝混用。五、确定性文档 ID增量同步的前提要精准重导入 / 精准回收向量文档的 ID 必须是确定的——同一条文永远同一个 ID。本项目用语义键law:article:{id}长条文分块则law:article:{id}#c{i}。但这里有个坑PgVectorStore 的 id 列是 uuid 类型写入时UUID.fromString不接受law:article:1001这种字符串键。解决是用 **UUID v3MD5 派生**把语义键确定性映射成 UUIDprivatestaticStringdeterministicUuid(Stringkey){returnUUID.nameUUIDFromBytes(key.getBytes(StandardCharsets.UTF_8)).toString();}同键永远同 UUID确定性 ID的精准重导入/回收决策得以保留。六、长条文分块Small-to-Big 的前半一条法条可能很长整条向量化会稀释语义。导入时按chunk-max-length480 字把长条文拆成多个子块文档#c0、#c1……短条文整条一个文档。检索粒度用小块保精度——命中哪个子块算哪个至于命中子块后还原整条全文注入注入粒度保完整是篇3 检索管线的第五步这里先把分块存好。七、断点续传与进度大批量重嵌可能中途失败。设计成每批BATCH10embedding 写入后立即落 ledger checkpoint条文粒度中断后再点同步已 checkpoint 的条文 MD5 命中自动跳过——断点即 ledger。进度写 Redisadmin:sync:progress供管理端轮询多实例共享。同步改由管理端按钮手动触发import-on-startup: false不再启动即跑避免每次重启都扫库。八、踩坑备忘① DashScope embedding 批量上限 10 条/请求。一次塞超过 10 条直接报错。所以BATCH 10攒够就 flush。② 改维度/索引类型不会触发重建。initialize-schema: true每次启动执行的是CREATE ... IF NOT EXISTS——已存在就空转。这意味着改dimensions/index-type/distance-type配置不生效旧表不动。正确四步DROP TABLE vector_store→DELETE FROM rag_ledger不清指纹会判无变更不重嵌→ 重启按新配置建表 → 管理端点一次全量重嵌。而仅换 embedding 模型名不需要这套流程——因为模型名已掺进指纹点同步自动全量重建。③ 勾选部分法律同步时删除回收必须限定在同一作用域。否则未勾选法条的指纹不在本次newHashes里会被误判成底账已删而整体回收掉向量——这是勾选同步最危险的坑代码里用toDelete.retainAll(作用域内 id)兜住。④ 向量库不可用不能让启动崩。导入失败仅告警不阻断启动检索侧会降级为空对话退化为纯 LLM。九、小结概念一句话底账化law_article 唯一事实源vector_store 可重建索引MD5 增量新增/变更/删除三态只重嵌差异掺模型名防混用确定性 ID语义键经 UUID v3 映射精准重导入/回收的前提分块长条文拆子块小块保检索精度断点续传每批落 ledger checkpoint中断从断点继续十、下篇预告检索能跑、同步省钱了但 Naive RAG每轮无条件前置检索仍然又费又不准而且引用可信度是假的——前端引用卡展示的是模型说了什么而不是检索命中了什么。下一篇把检索交给模型自主决策Agentic RAG并做引用可信度专项。源码与体验Gitee国内快https://gitee.com/spaserby/laoxiaosi.git GitHub https://github.com/spaserby/laoxiaosi.git 在线演示https://laoxiaosi.noctisblue.com本系列全套代码皆开源觉得这篇有帮助欢迎顺手点颗 ⭐
返回列表