用 Java 接入大模型(十八):把 RAG 接进已有 Java 服务

简介: 本文是RAG工程落地收官篇,聚焦Java服务中稳定性、成本与安全三大上线难题:分层缓存需嵌入租户权限防越权;增量更新必删旧块防数据污染;多租户隔离须在检索层过滤;性能需全链路耗时分解与降级;可观测性要覆盖查询、命中块、权限与降级路径——任一环节疏漏,皆可能引发事故。

阶段三走到最后一篇。前面十七篇把 RAG 的每个环节都拆过了,但那些都是"单独能跑"的状态。真正上线时面对的是另一类问题:怎么让它在已有的 Java 服务里稳定运行、成本可控、还不出安全事故。 这一篇讲的就是这些收尾工作,其中有一处安全问题特别容易被忽略。


一、最后一公里是工程问题,不是算法问题

先说清这一篇的定位。前面讲的切分、向量化、检索、重排、评测,都属于"让效果达标"。而这一篇要解决的是另一批问题:

  • 每次查询都要跑一遍完整链路,成本怎么压;
  • 资料天天在变,新内容怎么快速生效、旧内容怎么清理;
  • 系统是多租户的,怎么保证 A 部门查不到 B 部门的资料;
  • 链路从一次模型调用变成四五个环节串起来,延迟和失败面都变大了。

这些问题跟算法无关,但它们决定这套东西能不能真的上线。而且其中任何一条没处理好,后果都比"效果差一点"严重——尤其是租户隔离,那是事故级别的。


二、缓存:分三层做,但有个前提

RAG 的链路长,每一段都值得缓存,但性质不一样。

第一层:查询改写缓存。 同一个问题反复出现时,改写结果可以直接复用,省掉一次模型调用。

第二层:检索结果缓存。 同样的问题,检索到的那批块通常不会变(除非资料刚更新过)。这一层的收益最大——检索本身包含一次 embedding 调用加一次向量搜索。

第三层:最终答案缓存。 命中了就直接返回,连生成都省了。这层最省,但风险也最大:答案会过期。

第三层要用语义缓存效果才好——不是精确匹配问题原文,而是把问题向量化,相似度超过阈值就复用。因为同一个意思,用户的问法千变万化。

但这里有个必须先解决的前提:缓存不能只按"问题"做 key。

这就是容易被忽略的那处安全问题。设想:

  • 员工 A 问"本部门的预算流程是什么",系统检索到了 A 有权限看到的文档,答案被缓存;
  • 员工 B 问了措辞相近的问题,缓存命中,直接拿到 A 的答案——而这份答案来自 B 无权查看的文档。

整个过程没有报错,检索、生成都没执行,因此权限过滤根本没被触发。这就是纯粹由缓存引入的越权。

所以缓存的 key 里必须带上权限维度,而不是只带问题。实践上有两种做法:

  • 把租户 ID(或权限标签的哈希)拼进 key,不同租户各自一套缓存;
  • 按权限等级分命名空间,同等级共用。

第一种更安全、更简单,代价是缓存命中率下降(不同租户之间不能共享)。在没有把握之前,宁可牺牲命中率。 越权这类事故的代价,远高于多跑几次检索。

另外提醒一点:语义缓存本身会放大这个风险。 精确缓存只在问题完全相同时命中,而语义缓存是"意思相近就命中"——两个不同租户的人本来可能问出措辞相近的问题,所以它撞上的概率更高。如果要用语义缓存,权限维度更不能不进 key。


三、增量更新:最容易漏的是"删旧块"

资料更新是常态。一份文档改了,你得让新内容能被检索到,同时让旧内容消失。

完整流程是这样:

  1. 检测到文档变更;
  2. 重新解析、切分;
  3. 对新的块做向量化;
  4. 写入向量库;
  5. 删掉这份文档对应的旧块。

第五步是最容易漏的,第十五篇专门强调过。漏了它的后果是:新旧块同时存在,检索时可能同时命中,模型收到自相矛盾的两份资料,然后给出一个基于过时内容的答案。而且这种错误不会报错,只会表现为"AI 给了旧答案"。

要能删干净,前提是块必须能追到它的来源文档——这就是为什么元数据里要有 source_id(第十三篇的元数据四件套)。没有它,你根本不知道哪些块属于这份文档。

增量还是全量? 这里有明确的边界:

  • 可以增量:文档内容变了,但切分策略和 embedding 模型都没变。这时只需处理变更的文档。
  • 必须全量重建:切分策略改了、或者换了 embedding 模型。这两种情况下,库里所有向量和所有块的边界都失效了(第十四篇讲过:换模型意味着坐标系变了),只能全部重来。

所以"切分策略不能轻易改"这句话的实际含义,就是"改一次要全量重建一次"。 这个代价要在动手前想清楚,而不是上线之后才发现。

工程项目化上还有几点要注意:

  • 变更检测要有可靠依据,比如内容哈希或更新时间戳,别靠文件名——文件改了名字没变,增量就漏了。
  • 重建是后台任务,不是同步操作。 资料量大时它要跑很久,必须异步执行,并且能被观测:处理了多少、剩多少、失败在哪。
  • 中断要能续传。 任务跑到一半挂了,重启后应该从断点继续,而不是从头再来。
  • 重建期间线上查询怎么办。 如果直接删旧库再写新库,中间会有一段"什么都查不到"的空窗。稳妥做法是双写加切换:新库建好再切流量,旧库留一段时间再删。

四、多租户隔离:权限过滤必须在检索层

这一节是整篇里最需要执行到位的一条。

检索必须在权限过滤之后进行,不能先检索再在应用层过滤。 原因有两个:

第一,应用层过滤会漏。 检索返回了 20 个块,你在代码里筛掉无权访问的——看起来没问题,但更危险的是:只要有任何一条代码路径忘了筛,就是一次越权。 而过滤放在检索层,是无权访问的块根本不返回,从源头就断了。

第二,过滤放在检索层对召回率有影响。 第十五篇讲过:过滤条件很激进时,近似搜索可能在前几十个候选里一个都不满足。所以要配合分区或分库策略——权限边界清晰的场景,不同租户的数据物理分开(不同 collection 或不同库),比在同一堆数据里加过滤条件更可靠。

几条硬性要求:

  • 权限标签随块入库(第十三篇提过,第一版就要加),检索时作为必传条件;
  • 缓存按租户隔离(上一节的结论);
  • 日志和埋点里不要记录原文内容,尤其是无权访问的块——日志泄露是常见的越权渠道;
  • 回答里带上出处(第十二篇讲过),但要确认出处本身也是用户有权看到的。

五、性能:延迟被摊薄到四个环节

这一节讲一个容易被低估的事实:RAG 的延迟天然比单次模型调用高好几倍。

拆开看,一次 RAG 查询串了这些环节:

  1. (可选)查询改写 —— 一次模型调用;
  2. 查询向量化 —— 一次 embedding 调用;
  3. 向量检索 —— 一次库查询;
  4. 重排 —— 一般也是一次模型调用,而且要对多个候选算;
  5. 生成 —— 一次模型调用(这一步通常最慢)。

任意一环慢,整体就慢。 而单次模型调用只有一个环节,所以那种"模型调用很快"的直觉,在 RAG 里不成立。

几个实际做法:

  • 先把耗时分解测出来。 别猜哪一环慢——在每段打点,拿到真实分布再决定优化哪一块。很多时候你以为慢在检索,实际慢在重排。
  • 每段都要能独立超时和降级。 重排超时就跳过重排,用原始召回顺序;改写超时就用原始问题检索。这些降级几乎不影响功能可用性,但能避免一个次要环节把整个请求拖死。
  • 检索挂了要有明确的行为。 如果向量库不可用,是直接失败,还是降级成"不带资料直接问模型"?后者可以接受,但必须在答案里明确标注"本次未查询资料"——否则用户会把模型的自由发挥当成有依据的回答,那比报错危险得多。

流式能不能用? 可以,但有前提:检索必须全部完成,生成阶段才能开始流式输出。 也就是说,用户等待的首字延迟里包含了检索和重排的时间,流式只能优化"生成"这一段。所以 RAG 的首字延迟明显高于纯对话场景——这一点要在产品层面接受,或者靠"先给个进度提示"来缓解。


六、集成形态:库还是服务

两种常见做法。

做成 SDK 库:业务方引入依赖直接调用。优点是调用简单、没有网络开销;缺点是版本升级要各家跟着升,而且向量库的连接、模型调用都在业务进程里,资源不好统一管理。

做成独立服务:业务方通过 HTTP 调用。优点是资源集中、升级只改一处、限流和缓存可以全局生效;缺点是多一跳网络,且它本身会成为新的故障点(这时候第四篇和第十一篇的超时与降级就用上了)。

多数团队在中后期会演进到独立服务,因为模型调用和向量库连接这两样东西,放在业务进程里管理成本很高。但如果只是内部少量调用,先用库跑起来也完全合理——别为了架构漂亮提前拆服务。

另外,资料入库/重建这类重活应该走任务队列,不要放在请求链路里。它们是分钟级甚至小时级的操作,和在线查询的资源需求完全不同,混在一起会互相挤占。


七、可观测性:该记什么

RAG 出问题时,光看日志里一句"接口返回 200"是没法排查的。至少要记下:

  • 原始问题与改写后的查询。这一对数据能立刻看出改写是否把问题带偏了。
  • 命中的块 ID 与来源。用来核对检索对不对,也是发现"召回了不该召回的内容"的依据。
  • 各环节耗时分解。定位慢在哪一段。
  • 是否走了降级路径(跳过重排、未查资料等)。降级路径上的质量下降,如果不记录,你会把它当成正常表现。
  • 最终答案与引用来源。用于事后抽检。

这些埋点还有个附带价值:它们就是评测迭代和后续优化的数据来源。 上一篇讲的在线指标、人工抽检,全都依赖这些记录。


八、小结与检查清单

  • 缓存分三层(改写 / 检索 / 答案),语义缓存收益大但必须把租户与权限维度写进 key,否则会绕过权限过滤造成越权
  • 增量更新:以内容哈希检测变更、重新切分向量化、必须删旧块、重建走异步任务且可观测可续传、双写切换避免空窗
  • 全量重建的边界:切分策略变或换 embedding 模型就是全量,改动前先算代价
  • 权限过滤在检索层做,不放应用层;权限边界清晰时物理分区比过滤更可靠;缓存按租户隔离;日志不记无权内容
  • 性能:先把耗时分解测出来再优化;每个环节独立超时与降级;检索不可用时降级要在答案中明确标注
  • 流式:检索完成后才能开始生成,首字延迟天然高于纯对话
  • 集成:先用库、后期拆服务;入库重建走任务队列,不占请求链路
  • 可观测性:记原始/改写后查询、命中块与来源、耗时分解、是否降级、答案与引用

一道判断标准:如果有人问你"现在这次查询命中的块,是哪些文档、用户有没有权限看",你能不能立刻答出来? 答不出来,说明这套系统的可观测性或权限设计还有缺口——而这两个缺口中的任何一个,都足以把一次技术尝鲜变成一次事故。


下一篇

阶段三到这里收尾:从"该不该做 RAG",到切分、向量化、入库、检索优化、评测,再到今天的工程落地,这条链路走完了。

接下来进入阶段四,主题是让模型会做事——也就是工具调用和 Agent。第一个问题是 Function Calling:模型怎么知道有哪些工具可用、参数怎么传、执行结果怎么回灌给它。下一篇从一个最小可运行的例子开始。


本文是「用 Java 接入大模型」系列第十八篇,阶段三收尾。上一篇讲了 RAG 效果怎么量化评测,建议按顺序看。文中缓存比例、重建策略与集成分层为通用实践,具体取值请按你的资料规模、租户模型与延迟预算确定。

目录
相关文章
|
13天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)
5538 41
|
14天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
4811 58
|
8天前
|
Linux 开发工具 iOS开发
2026最新codex下载安装保姆级使用教程(下载+安装+登录+使用,含所有历史版本)
本文详解OpenAI Codex国内快速上手指南:提供Win/macOS/Linux全平台免安装包、npm/Homebrew等多方式安装、账号/API Key双登录、简体中文切换,覆盖下载、安装、汉化、使用及高频避坑(如微软商店失败、npm超时等),10分钟即可跑通。
2092 6
|
6天前
|
人工智能 安全 API
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(dsh)是DeepSeek AI于2026年开源的轻量级Agent运行框架,基于Cordis内核,践行“一切皆插件”理念。支持Web/桌面双端,开箱即用;内置多模型接入、丰富插件生态(超3.5万),适配编程、办公、创作等场景。(239字)
1297 0
|
14天前
|
iOS开发 Docker MacOS
【2026实测】Docker Desktop下载、安装、汉化、配置教程(附官网安装包)
Docker Desktop 是 Docker 官方推出的桌面容器管理工具,集成引擎、CLI、图形界面与 Kubernetes,支持 Win/macOS/Linux。2026 最新版 4.88.1 强化 ARM 原生运行、镜像加速及资源可视化,附中文汉化与国内镜像配置指南。
2286 7
|
8天前
|
云安全 人工智能 安全
|
1天前
|
云安全 人工智能 安全
|
7天前
|
人工智能 并行计算 调度
本地 AI 一键部署工具 LocModel:Windows 端集成画图、视频生成、Agent 的开源模型运行方案
LocModel 是专为 Windows 设计的一站式本地 AI 工具,支持零配置启动、自动硬件检测、GGUF 模型一键加载、文生图/视频生成及 Agent 自动化任务。适配 8G 显存,内置显存分片调度,轻松实现 AI 漫剧批量创作与离线大模型调用。(239 字)

热门文章

最新文章