链载Ai

标题: 实战|Spring Milvus,Java也能实现的企业级文档问答RAG [打印本页]

作者: 链载Ai    时间: 昨天 21:19
标题: 实战|Spring Milvus,Java也能实现的企业级文档问答RAG
在企业数字化转型的浪潮中,PDF、Word 等海量文档往往沉睡在各个业务系统中,形成“数据孤岛”,难以被智能系统高效利用。

那么,如何构建一个真正面向企业场景落地的 AI 应用,让 AI 成为企业的“智能助手”?本项目提供一套基于 Spring 框架的完整解决方案,结合文档 ETL、向量检索与 RAG 问答技术,覆盖从数据导入到智能对话的全链路实践。

与其他演示不同,本项目强调企业级能力建设——包括 API 安全控制、指标可观测性等工程化特性。借助 Spring 强大的生态系统与 Java 社区对“高可维护、高可扩展系统”的成熟支持,我们将打造一个真正适合在企业环境中上线运行的 AI 应用。

技术栈速览

环境配置避坑指南

示例代码仓库 https://github.com/topikachu/spring-ai-rag

#验证Docker是否正常运行$dockerversion$dockerps

为什么选百度千帆?

关键配置(application.properties)

spring.ai.openai.base-url=https://qianfan.baidubce.comspring.ai.openai.chat.completions-path=/v2/chat/completionsspring.ai.openai.chat.options.model=ernie-3.5-128kspring.ai.openai.embedding.embeddings-path=/v2/embeddingsspring.ai.openai.embedding.options.model=tao-8kspring.ai.openai.api-key=${OPENAI_API_KEY}spring.ai.model.embedding=openai

实操建议

文档 ETL:非结构化数据结构化处理

处理流程:

示例代码:

returndocumentReader.getDocuments().flatMap(document->{varprocessChunks=Mono.fromRunnable(()->{varchunks=textSplitter.apply(List.of(document));vectorStore.write(chunks);//expensiveoperation}).subscribeOn(Schedulers.boundedElastic());returnFlux.concat(Flux.just(document),processChunks.then(Mono.empty()));}).doOnComplete(()->log.info("RunIngestion()finished")).doOnError(e->log.error("Errorduringingestion",e));}

注意:百度千帆嵌入 API 仅支持单文档请求,需确保 ETL 实现为“单文档单请求”。

@BeanBatchingStrategysingleDocumentBatchingStrategy(){returndocuments->documents.stream().map(List:f).toList();}

ingFang SC", system-ui, -apple-system, "system-ui", "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: 0.544px;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;clear: both;min-height: 1em;display: block;background-color: rgb(255, 255, 255);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">向量存储:用 Milvus 实现“秒级语义检索”

配置示例:

spring.ai.vectorstore.milvus.initialize-schema=truespring.ai.vectorstore.milvus.embedding-dimension=1024
说明:例如用户问“刘备结义排第几”,Milvus会返回相关文档段落,再由语言模型生成自然语言答案。

ingFang SC", system-ui, -apple-system, "system-ui", "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: 0.544px;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;clear: both;min-height: 1em;display: block;background-color: rgb(255, 255, 255);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">RAG 对话:结合知识库与上下文的智能问答

核心逻辑:

对话向量检索与记忆集成示例:

publicChatClient.ChatClientRequestSpecinput(StringuserInput,StringconversationId){returnChatClient.builder(chatModel).build().prompt().advisors(newQuestionAnswerAdvisor(vectorStore),MessageChatMemoryAdvisor.builder(chatMemory).build()).advisors(spec->spec.param(CONVERSATION_ID,conversationId)).user(userInput);}

提升前端体验:使用stream接口返回Flux,通过SSE实现打字机效果:

publicFlux<String>stream(StringuserInput,StringconversationId){returninput(userInput,conversationId).stream().content();}
@PostMapping(path="/chat",produces=MediaType.TEXT_EVENT_STREAM_VALUE)publicFlux<String>chat(@RequestBodyChatRequestchatRequest,@RequestParam()StringconversationId,Principalprincipal){varconversationKey=String.format("%s:%s",principal.getName(),conversationId);returnchatService.stream(chatRequest.userInput,conversationKey).doOnError(exp->log.error("Errorinchat",exp));}

ingFang SC", system-ui, -apple-system, "system-ui", "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: 0.544px;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;clear: both;min-height: 1em;display: block;background-color: rgb(255, 255, 255);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">API 安全加固

权限控制示例:

@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.httpBasic().and().authorizeRequests(authz->authz.antMatchers("/api/v1/index").hasRole("ADMIN").anyRequest().authenticated());}

企业级加固:在正式的生产环境中建议升级为OAuth2/JWT认证方案。


ingFang SC", system-ui, -apple-system, "system-ui", "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: 0.544px;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;clear: both;min-height: 1em;display: block;background-color: rgb(255, 255, 255);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">系统可观察性

链路追踪:本项目使用OpenTelemetry JavaAgent,覆盖Chat → Milvus → 模型调用的全链路调用追踪(grpc调用链关键)

-javaagent:<path/to/opentelemetry-javaagent.jar>\-Dotel.metrics.exporter=none\-Dotel.logs.exporter=none

指标监控:使用Micrometer自动暴露Prometheus指标,比如:

#HELPgen_ai_client_operation_seconds#TYPEgen_ai_client_operation_secondssummarygen_ai_client_operation_seconds_count{...}1
#HELPdb_vector_client_operation_seconds#TYPEdb_vector_client_operation_secondssummarydb_vector_client_operation_seconds_count{...}1

配置:

management.endpoints.web.exposure.include=prometheus

Tip:Spring Boot 3.2 引入 OTEL starter,但由于不能覆盖 gRPC(Milvus client)调用链,本项目采用 JavaAgent 接入方式,以确保完整链路追踪。


exportOPENAI_API_KEY=<百度千帆APIKEY>mvncleantestpackagedockercomposeup-djava-javaagent:target/otel/opentelemetry-javaagent.jar-Dotel.metrics.exporter=none-Dotel.logs.exporter=none-Dinput.directory=$PWD/src/test/resources/corpus-jartarget/rag-0.0.1-SNAPSHOT.jarcurl--location'localhost:8080/api/v1/index'\--user"admin:password"\--header'Content-Type:application/json'\--data'{}'curl--location'localhost:8080/api/v1/chat?conversationId=liubei'\--header'Content-Type:application/json'\--user"user:password"\--data'{"userInput":"刘备结义时排第几?"}'curl--location'localhost:8080/api/v1/chat?conversationId=liubei'\--header'Content-Type:application/json'\--user"user:password"\--data'{"userInput":"他哪里人?"}'curl--location'localhost:8080/api/v1/chat?conversationId=guanyu'\--header'Content-Type:application/json'\--user"user:password"\--data'{"userInput":"关羽结义时排第几?"}'curl--location'localhost:8080/api/v1/chat?conversationId=guanyu'\--header'Content-Type:application/json'\--user"user:password"\--data'{"userInput":"他哪里人?"}'curl"http://localhost:8080/actuator/prometheus"

打开trace 界面 http://localhost:16686/,可以查看调用的tracing情况,如下图

从文档解析到智能对话,这个项目不仅仅是技术的堆叠,更是一次工程实践与 AI 认知的结合。通过 Spring AI + 向量数据库 + 企业级安全与可观测性,真正打通了“知识沉淀 → 智能服务”的链路。

如果你也在探索 AI 与企业系统的融合,欢迎留言交流,一起构建更智能、更可靠的未来系统。






欢迎光临 链载Ai (https://www.lianzai.com/) Powered by Discuz! X3.5