
很多团队一听到要在现有的 Java 后端中加入 AI 能力,第一反应往往是重新搭一套 Python 服务,甚至想把原有的业务流程推倒重来。但是我们在生产环境实际跑过几轮之后发现,这样做既增加维护成本,又带来不必要的跨语言通信开销。现有的 Spring Cloud 或者标准 Spring Boot 架构完全能够承载企业级知识库系统,关键在于理清架构边界与异步调用链路。
在微服务拆分上,绝对不要把大语言模型(LLM)的直接调用写进已有的订单、用户或文档元数据服务中。传统业务请求的耗时通常在 20 到 200 毫秒之间,而一次大模型检索与生成往往需要 2 到 15 秒。如果把它们揉在一个服务里,Tomcat 默认的 200 个工作线程会在高并发下迅速被耗尽,进而引发整个微服务集群的雪崩。正确的做法是单独抽出一个独立的微服务,例如命名为 `knowledge-ai-service`。原有的文档管理服务继续负责文件的上传、元数据管理和权限判断,当文件就绪后,通过消息队列通知 `knowledge-ai-service` 处理。
在 Java 框架选型上,目前主要有三种路径:Spring 官方支持的 Spring AI、社区活跃的 LangChain4j,以及直接基于 HttpClient/OkHttp 自研对接。以下是三种方案在生产环境中的选型对比:
| 评估维度 | Spring AI | LangChain4j | 自研 HTTP Client 接入 |
|---|---|---|---|
| Spring Boot 兼容度 | 原生整合,自动装配体验最好 | 依赖外部 starter,配置较直观 | 需要手动编写连接池与配置类 |
| 企业级 RAG 组件完备度 | 基础组件齐全,目前迭代节奏快 | 切分器、向量库与重排器极其全面 | 完全需要自行实现分块与向量化流转 |
| 对非标准模型的适配 | 依赖官方 Adapter,扩展相对繁琐 | 抽象层清晰,自定义 Model 接口容易 | 灵活性最高,完全受控 |
| 内存与启动开销 | 中等,随 Spring 上下文加载 | 较低,模块化按需引入较轻量 | 极低,无多余依赖 |
| 生产选型建议 | 适合以 Spring Boot 3 为底座的新项目 | 适合各类现有 Spring 复杂业务架构整合 | 仅适合只需单次简单对话的极简场景 |
根据实际压测情况,如果现有的微服务使用的是 Spring Boot 2.7 或 3.x,优先选择 LangChain4j。因为它的模块拆分细致,不会强制要求升级 Spring 框架版本,同时它对本地模型 Ollama、公有云模型与主流向量库的封装比 Spring AI 更成熟。
知识库管理系统的第一道核心工序是文档处理流水线(ETL)。要让文档检索准确,必须规范操作步骤:
先做第一步,在原业务服务中接收文件并上传到对象存储(如 MinIO),然后向消息队列(如 RocketMQ)发送一条包含文件 ID 与访问地址的消息。
接着做第二步,`knowledge-ai-service` 消费消息,拉取文件并使用 Apache Tika 解析文本内容。切忌在解析大体积 PDF 时直接一次性把字节读进内存,必须采用流式读取,避免触发频繁的 Full GC。
然后做第三步,对文本进行切块(Chunking)。切块不能随意按字符数硬切。在生产实践中,建议将单块大小(chunk_size)设定为 512 个 Token,相邻块重叠度(chunk_overlap)设定为 64 个 Token。重叠区间能保证上下文语义在边界处不会断裂。如果你的文档大多包含表格,必须单独保留 Markdown 格式的表格结构,不要将其打碎。
最后做第四步,批量调用 Embedding 模型(例如 `text-embedding-3-small` 或本地部署的 `bge-large-zh-v1.5`)生成向量,并批量写入向量数据库。每次批量写入的大小建议控制在 50 到 100 条之间,单次量太大容易触发远程接口超时,单次量太小则网络往返开销严重。
向量数据库是知识库的底层核心。市面上选项很多,但针对已有的 Java 技术团队,选型逻辑应当侧重运维复杂度与数据规模:
| 数据库选型 | 优势 | 劣势 | 推荐适用场景 |
|---|---|---|---|
| PostgreSQL + pgvector | 无需额外运维新中间件,支持直接连表做 SQL 事务与业务权限过滤 | 向量数据量超过千万级后,检索耗时显著上升 | 知识库单表数据量小于 500 万条的中小型业务 |
| Milvus | 原生分布式架构,支持百亿级向量,HNSW 索引查询耗时低于 10ms | 依赖 etcd、MinIO、Pulsar/Kafka,部署维护成本较高 | 大规模独立知识中心、海量文档检索 |
| Elasticsearch (8.x+) | 团队已有运维经验,原生支持关键字精确匹配与稠密向量混合搜索 | 内存消耗大,写入吞吐量相对专用向量数据库略低 | 原系统已有 ES 集群,且对全文搜索依赖很高的系统 |
如果团队原本就在使用 PostgreSQL,第一选择必须是 pgvector。因为你可以在同一条 SQL 里面完成“租户 ID 过滤、用户角色过滤以及向量距离排序”,这完全省去了在应用层拼装权限的复杂逻辑。在创建索引时,推荐使用 HNSW 索引,关键参数设置为:M=16,efConstruction=64,查询时 efSearch=32。这组参数在召回准确率和查询耗时之间能取得较好的平衡。
当用户在前端发起提问时,不要直接拿用户的原始输入去查询向量库。真实用户的提问往往很短,或者带有代词。
检索流程应当按照固定路径执行:
1.问题改写:先将用户输入和前两轮历史对话交给一个低延迟的小模型,补充完整主谓宾,改写成独立的搜索语句。
2.混合检索(Hybrid Search):同时执行两路查询。一路是向量相似度检索(Dense Retrieval),捕捉语义相关;另一路是基于分词的传统倒排检索(Sparse Retrieval,如 BM25),捕捉精确的人名、订单号或专有名词。单纯依赖向量检索极易漏掉含有特定编号或专有名词的行业文档。
3.重排序(Rerank):混合检索粗筛出前 20 条候选文本(top_k=20)。接着使用轻量级的重排模型(如 `bge-reranker-large`)对候选结果打分,最终只挑选得分最高的前 3 到 5 条放入 Prompt。相似度分数截断阈值建议设定在 0.65,低于该阈值的候选块直接剔除,防止无关噪声干扰大模型生成。
4.流式组装返回:Java 接口必须使用 Server-Sent Events (SSE) 返回数据,在 Spring Boot 中直接使用 `SseEmitter` 或响应式编程中的 `Flux<ServerSentEvent>`。流式吐字能把用户感知的首字延迟(TTFT)压到 1 秒以内,避免网关层(如 Nginx 或 Spring Cloud Gateway)因为等待超时而直接中断长连接。
系统上线运行,稳定性与成本控制必须由具体的防御机制来托底。
第一,大模型调用必须配置严格的超时与熔断机制。使用 Resilience4j 或者 Sentinel 对 AI 服务下游做隔离。LLM HTTP 调用的读取超时(Read Timeout)硬性设定为 15 秒。一旦下游接口在 1 分钟内错误率超过 40%,立即熔断并向用户返回系统兜底文案,绝不允许慢调用堆积导致内部连接池枯竭。
第二,必须建立多租户安全拦截层。在 RAG 检索组装上下文时,严格在向量库过滤条件中拼接当前登录用户的 `tenant_id` 和 `dept_id`。大模型本身不具备权限意识,如果检索阶段把无权查看的机密合同文本作为上下文放进 Prompt,大模型就会把机密内容直接回答给普通员工。
第三,引入语义缓存降低 token 消耗。很多员工会反复询问类似的问题,比如“公司的年假怎么休”。可以在 Redis 中使用文本相似度或哈希对历史提问做缓存。命中缓存的请求直接返回预先存储的回答,不需要再次调用模型接口。这样做可以直观地降低 API 调用账单,同时把接口响应耗时降低到 50 毫秒以内。
原创文章,作者:七点互动,如若转载,请注明出处:https://ai.qidianhudong.com/aikonw/4416.html