☰
用Java/Spring构建LLMOps平台:RAG、工作流编排与落地实践
2026/10/8 9:56:21 网站建设 项目流程

先说结论:用 Java/Spring 技术栈做 LLMOps 平台,不但可行,而且对绝大多数企业级场景来说,可能比直接套用 Python 系工具更合适。这篇文章要聊的是我实际搭过的一套东西——基于 Spring Boot 技术栈,把可视化界面、工作流编排、RAG 知识库这三块核心能力全部揉进一个 LLMOps 平台里。内容包括完整的架构思路、RAG 落地细节、编排引擎设计、监控评估闭环,以及若干只有真正踩过坑才写得出来的经验。适合正在用 Java 写 AI 应用、或者团队被要求"用现有 Java 技术栈做 AI 平台"的工程师参考。

很多人一听到 LLMOps 就默认得用 Python:LangChain、Dify、Flowise 满大街都是,Java 社区能打的工具确实少。但真正把 LLMOps 落地到企业内部时,会遇到权限体系、工单流程、审计日志、存量系统打通这一堆破事,这时候 Java/Spring 的积累反而是最大优势。我这边跑通的方案,核心栈是 Spring Boot + Spring AI,前端画布用成熟开源库,编排定义走 JSON DSL,知识库走 PGVector 向量检索,再套一层模型适配和管理面。下面按模块拆开讲。

1. 为什么敢用 Java/Spring 技术栈做 LLMOps

1.1 先搞清楚 LLMOps 到底在管什么

LLMOps 这个概念我理解下来,本质上是把大模型应用当成一个需要持续交付、持续运维的业务系统。传统 DevOps 管的是代码、构建、部署、监控,LLMOps 管的则是 Prompt、模型版本、知识库、评估、成本、调用链。具体拆开大概是这几层:模型网关(统一接入各家大模型)、Prompt 管理(模板化、版本化、灰度)、知识库服务(RAG 的切片、向量化、检索)、编排引擎(把多个模型调用和工具调用串联成流程)、评估与观测(回答质量、Token 成本、链路追踪)。

很多 Java 团队的误区在于:一听说要上 AI 平台,第一反应是"那我们去学 Python 吧",但其实翻车点往往不在模型调用,而在周边系统集成。我见过不少项目用 Python 写了个很漂亮的 Agent 原型,最后卡在统一登录、数据权限、审批流对接上,折腾几个月回不来。Java/Spring 在这块有天然优势,但前提是 AI 本身的核心链路也得打通,否则还是绕不开。

1.2 选型思路:Spring AI 就是 Java 界的 LangChain 抽象

Spring AI 这个项目,定位和 LangChain 不完全一样,但目标类似——给 Java 开发者一个标准化的大模型接入层。它把模型对话、Embedding、向量存储、结构化输出这些能力做了统一抽象。你写业务代码时面向的是 ChatClient、EmbeddingModel、VectorStore 这些接口,具体底层是接通义千问、GPT、DeepSeek 还是一些开源模型,通过配置切换就行。这一点对做平台类系统非常重要,因为企业客户往往有多个模型来源,测试阶段用便宜的,线上切贵的,或者按租户分配不同模型。

在这个项目里我用 Spring AI 作为中间层,配合 Spring Boot 的自动装配能力,把模型调用封装成内部服务。团队原本就会 Spring,上手成本很低。和 Python 生态对比,Python 胜在算法实验灵活、Agent 社区生态多,Java 胜在企业集成深度、性能调优空间、以及 Spring 全家桶的既有能力。我的结论是:做原型、做探索性实验用 Python 没毛病,但做需要和现有系统深度耦合的 LLMOps 平台,Java 完全是一条正经路线。

1.3 平台的整体模块划分

我搭的平台在逻辑上分成了六个模块,每个模块独立演进,但用 Spring Boot 单一应用承载早期版本,减少运维成本。

第一层是接入层,处理 HTTP/WebSocket 请求,负责认证鉴权和 API 暴露。第二层是应用服务层,承载问答对话、知识库管理、工作流运行这类业务入口。第三层是编排引擎,负责解析工作流 DSL、调度节点执行。第四层是知识库服务,处理文档解析、切片、向量化、检索。第五层是模型网关,统一封装各家大模型调用、Token 统计、重试降级。最后一层是管理面,提供可视化界面给管理员和业务人员配置 Prompt、查看工作流、评估效果。

这个分层思路不是一开始就有的,最初就是 Controller 里直接调大模型 API,后来发现一旦要做版本管理、灰度发布、成本统计,代码就乱成一团,才逐步抽出模型网关和编排引擎。给还在起步期的团队一个建议:哪怕第一版不做完整平台,也要把模型调用单独抽一层,否则后面重构成本很高。

2. RAG 知识库从零到能用,中间要过多少坎

2.1 三种知识库先说清楚:向量库、知识图谱、结构化库

热词里反复提到"KG知识库、RAG知识库和结构知识库的区分",这个确实值得先说透,因为很多人从概念上就混了。RAG(检索增强生成)是一个机制,它的核心思路是在生成回答之前先从外部知识源检索出相关内容,塞进 Prompt 再交给大模型。落地时 RAG 最常见的载体是向量数据库,存的是文档切块后的 Embedding 向量。知识图谱(KG)则不同,它存的是实体和关系,擅长回答"谁和谁是什么关系""这家公司有哪些产品线"这类需要多跳推理的问题。结构化知识库就是我们平时说的业务数据库或者 API,适合精确定点查询,比如查订单状态、账户余额,数据是强一致的。

具体到应用场景怎么选,我做项目后的体会是:非结构化的制度文档、合同、产品手册适合走向量 RAG;需要关系推理和全局关联的,比如反欺诈、资产图谱,适合知识图谱;而任何需要精确计算的查询,都应该直接落到业务接口或数据库,别硬往向量库里塞。大多数平台实际是三种混着用:问题先进路由,判断意图后决定走哪种检索,或者它们的结果做融合再喂给大模型。

2.2 文档处理流水线:解析、切块、向量化、入库

RAG 的地基是文档处理流水线。我在项目里把这条链路拆成四步:格式解析、内容清洗、切片、向量化和入库。

格式解析是个容易被低估的坑。纯文本和 Markdown 好处理,但一旦上 PDF、Word、PPT,版面信息丢失导致切出来的块语义支离破碎。Java 这边解析 PDF 用 Apache PDFBox、Tika,Word 用 POI,通常 Tika 一把梭最省事,但遇到扫描版 PDF 就需要先做 OCR,这块得单独接 OCR 服务。内容清洗要处理页眉页脚、表格噪音、乱码引用——表格最好转成 Markdown 格式再入知识库,否则检索出来模型也读不懂。

切片是质量和成本之间的平衡。切太碎,上下文不够,模型答不对;切太长,Token 成本飙升,而且向量被平均化,检索精度下降。我常用的是递归字符切分,优先按标题、段落层级来切,每个 chunk 控制在 500 到 800 字,重叠 100 字左右。有一套参数要看语料特征调,没有万能值。

向量化就简单了,选一个 Embedding 模型,中文场景我用过开源 BGE 系列和国内云厂商的文本向量模型,稳定性都还行。向量库我选的是 PGVector——原因很直白,团队已经用 PostgreSQL,不想为一个向量库再引一套复杂组件。数据量在千万级以下 PGVector 完全够用,这是很多团队忽略的现实。

2.3 检索链路:召回、重排、组装才是 RAG 的灵魂

好多人以为 RAG 就是"把文档切了一切一存,提问时搜一下",其实真正的差异在检索链路上。我的检索链路分三段:召回、重排和组装。

召回阶段,我建议别只做向量检索,纯向量召回对精确词匹配很弱。比如用户搜"合同编号 XYZ-001",向量检索大概率不如一句数据库查询。我的方案是双路召回:一路是向量召回,负责语义相似;一路是关键词召回(BM25 或 PostgreSQL 全文检索),负责精确匹配。两边各取 Top N,然后做归一化分数融合,最终取 Top K 进入重排。

重排阶段,用 Rerank 模型对召回的候选重新打分。这步很关键但很多人跳过,导致上游检索的一点小误差直接传导到最终答案。重排能把真正相关的排到最前面,成本只多一次模型调用,却能让效果上一个档次。组装阶段,则是把选中的文本块按相关度排序拼进 Prompt,每组文本标注来源片段编号,然后明确告诉模型"优先依据提供的资料回答,资料不足时直接说不知道,不要编造"。

2.4 别回避问题:RAG 的真实瓶颈和优化方向

RAG 不是银弹,这点我在项目里体会很深。最大的瓶颈有三个:一是召回质量,特别是跨文档、需要多跳推理的问题,常规向量检索基本抓瞎;二是幻觉风险,有时候检索出来的内容和问题弱相关,模型反而被带偏,一本正经胡说八道;三是知识更新延迟,文档改了,向量库没同步,模型还在按旧知识回答。

关于热词里那个问题"RAG知识库能存储图片嘛",答案是能,但不是直接把图片存进向量库当"图片知识"。常规做法是调多模态模型把图片内容描述成文本,再把描述文本切片后进向量库;检索时用文本命中描述,再回源把原图展示给用户。如果要做训练集级别的图片检索,那就得走多模态向量模型,链路和文本 RAG 完全不同。

优化方向上,我做过最有用的两件事:一是 Query 改写,检索前先让大模型把用户口语问题改写成更适合检索的关键词组合,短问题补全、指代消解,召回率明显上升;二是父子切块,父块大保证上下文、子块小做精检索,命中子块后把父块内容给模型。GraphRAG 我也调研过,适合强关联的跨文档场景,代价是构建图成本高,小团队暂时可以不做。

3. 可视化界面和工作流编排,到底怎么落地

3.1 编排模型:一切皆节点,流程是一张有向无环图

工作流编排是 LLMOps 平台里最容易被低估的部分。很多人以为做一个拖拽画布、连线、运行就能完事,但真正难的是编排模型的设计。我采用的方案是:把工作流定义为一张有向无环图(DAG),图里有两类元素——节点(Node)和连边(Edge)。

节点类型包括开始、大模型调用、知识库检索、条件分支、HTTP 请求、代码片段、人工审批、结束。每种节点有输入输出参数定义。连边表达数据流向和控制流向。整个工作流的定义用 JSON 描述,这个 JSON 是平台的通用语言:前端画布读取它来渲染图形,后端引擎解析它来调度执行,DB 里存一份作版本管理。

这种设计的好处是,整个编排体系天然支持序列化、版本化和可视化。热词里提到"dify工作流转成spring ai java代码",我确实做过类似的事:Dify 的 DSL 本质也是 JSON,只要你自己的 DSL 结构设计得清晰,写一个转换器把 Dify 的节点映射到自己的节点类型,完全可行。但我不建议直接造一个和 Dify 一样重的 DSL,内部平台要的是满足 80% 场景、足够直观,而不是追求功能大而全。

3.2 画布选型:别自己造轮子,用成熟开源库

可视化界面这块,我建议直接选成熟开源画布库,自己做拖拽引擎成本极高还做不稳。我评估过几款:AntV X6 功能最全、文档丰富,国内大厂用的多,适合复杂流程图;LogicFlow 是滴滴开源的,对流程编排场景有专门的优化,还自带节点编辑面板;Vue Flow 轻量简洁,适合 Vue3 项目。

我这里前端技术栈是 Vue3,最终选了 LogicFlow。原因很简单:它对"流程节点 + 表单配置"这个模式支持很舒服,节点的自定义渲染够用,社区活跃度也可以。如果你团队前端是 React,那 React Flow 是稳妥选择。选画布库有一个原则:你能不改源码就绝不改源码,很多自定义需求通过节点模板和插件机制就能实现,改源码意味着未来升级成本全部自担。

画布之外,管理面还要有 Prompt 调试器、Token 用量仪表盘、知识库文档管理页。这些页面不需要花哨,但信息密度要高,因为是给内部运营看的数据,不是给 C 端用户看的。

3.3 后端执行器:从 JSON DSL 到真实调度

编排引擎的核心工作是解释执行 DSL。我设计的执行器分为三层:解析器、调度器、节点执行器。

解析器负责把 JSON 里的 nodes 和 edges 构建成内存图结构,并做合法性校验,比如节点属性必填项、边是否指向存在的节点、图有没有环。调度器负责驱动执行,基础方式是拓扑排序——找出入度为 0 的节点开始跑,节点执行完毕更新下游节点的入度,直到全部跑完。遇到条件分支节点,根据表达式结果决定走哪条边,其他分支不执行。这个机制有点像简化版的事件驱动状态机。

节点执行器是整个设计里最有意思的部分。我定义了一个接口WorkflowNodeExecutor,每种 nodeType 对应一个 Spring Bean 实现,然后用一个 Map 把类型映射到 Bean。大模型节点执行器内部调用模型网关,知识库检索节点执行器调用检索服务,HTTP 节点执行器通过 WebClient 发请求,人工审批节点则生成一个待办并挂起流程。

执行状态必须持久化。我建了两张表,一张 workflow_instance 存流程实例状态,一张 workflow_node_execution 存每个节点的执行记录,包括输入输出快照、开始结束时间、错误信息。有了这两张表,你才能在界面上看到流程跑到哪个节点,也才能在节点失败时做定点重试。

3.4 流式输出和后端推送:前端怎么知道"字"在动

LLMOps 平台里,问答过程大多是流式输出——模型一个字一个字吐出来,用户能像聊天一样看到实时回复。后端实现上,Spring AI 的 ChatClient 本身支持流式,返回 Flux 流。推送通道我实测下来,WebSocket 比 SSE 更适合这种场景,因为前端不仅要收 Token,还要反向上传消息,双向通信用 WebSocket 统一更省事。

这里有一个其他教程很少讲的细节:工作流编排里的流式输出,Token 不只是从末端输出,中间节点的状态变化也要推。我的做法是定义了一套统一推送协议,消息体里带 nodeId 和 type 字段:节点开始执行推{"nodeId":"retrieve_doc","type":"node_start"},节点完成推{"nodeId":"retrieve_doc","type":"node_end","output":{...}},模型流式输出推{"nodeId":"llm_chat","type":"token","content":"你好"}。前端按 nodeId 定位画布节点,type 决定是更新节点状态还是拼接 Token——这是我前面说选成熟画布库的另一个原因,节点实时状态高亮,自己写工作量巨大。

4. LLMOps 的监控、评估与闭环,必须一开始就设计进去

4.1 可观测性三件套:链路、成本、质量

很多团队做 AI 应用,上线后一坨黑盒,用户说"刚才那个回答是瞎编的"都查不到原因。LLMOps 平台的基础设施就是把可观测性从第一天就做进去。我的方案是三件套:链路追踪、Token 计量、质量反馈。

链路追踪,核心是让一次用户请求的所有日志能串起来。我用了 MDC 机制,在请求进网关时生成 TraceId,放进日志上下文,无论日志打到哪个服务哪个文件,都能按 TraceId 拉出全链路。模型调用时会额外记录模型名、输入 Token、输出 Token、推理耗时。有了这一步,排查问题就只是"一条 SQL 拉日志"的事,效率翻倍。

Token 计量直接和钱挂钩。大模型按 Token 计费,一个平台如果看不到各业务线的调用量,成本失控是迟早的事。我在模型网关统一记录每次调用的模型、Token 数、耗时,然后按租户维度聚合。管理界面上做两个核心报表:趋势图看调用量和费用波动,明细表可以钻取到具体某一次 Prompt 的内容。

质量反馈往往被当成事后诸葛亮,但其实它是评测闭环的重要输入。我在对话接口里内置了"点赞、点踩"按钮,点踩时记录当前会话 ID 和用户备注。这个信号的商业价值极高——它是免费的线上标注数据,比招聘标注员便宜得多。

4.2 Prompt 与模型也要有版本管理和灰度

LLMOps 和普通软件开发最大区别在于,Prompt 是运行时行为的一部分。业务代码写错了发版很容易,Prompt 写错了线上流量立刻受损,而且往往要等用户反馈才发现。所以我把 Prompt 当成一等公民管理。Prompt 模板存数据库,每条模板有版本号、创建人、状态(草稿/已发布/已下线)、生效策略。业务代码里不再硬编码任何 Prompt 字符串,统一通过渲染接口传入参数取模板解析。

模型接入也类似。我的模型网关里维护一个模型注册表,每个模型有 provider、model_name、base_url、api_key 引用、超时、并发上限。发布策略支持按租户指定模型,也可以按百分比灰度——比如新模型先说给 10% 的流量,跑几天看指标再放量。这套东西如果不做,后面每次换模型都是全量事故。

模型接入配置上,Spring AI 现在对国内模型的支持已经比较成熟。接阿里云百炼这类服务时,可以通过 DashScope 的适配器或者 OpenAI 兼容协议接入,配置好 base-url 和 api-key,业务代码完全不用感知底层是哪家模型。注意别在代码里写死模型名,全部走配置中心,这是血的教训。

4.3 评估体系:离线评测 + 线上反馈双轨并行

要持续改进 AI 应用,没有评估就谈不上优化。我的评估体系分两条线。离线评测这条线,核心是维护一组标准测试集,每条用例包含输入问题和期望回答要点。每次改动 Prompt、索引或检索策略,就在同一套测试集上跑分。自动评测我用 LLM-as-judge:让一个强模型当裁判,拿它和线上模型输出对比打分。RAG 场景还可以算 RAGAS 那套指标,包括忠实性(答案有没有忠于检索内容)和上下文召回率。

在线评估这条线,就是把 4.1 的质量反馈数据跑起来。每周抽一批被点踩的会话,人工复盘问题出在哪一环:是检索没召回,还是 Prompt 指令不明确,还是模型能力不够。把根因归类后,形成改进项进入下一迭代。我体会最深的点是:评估系统不要追求一步到位做成大而全的 A/B 平台,先有一个能跑的分类标签和打分脚本,养成"每次改动先跑测试集"的习惯,比工具本身重要得多。

5. 实操演示:一个能跑通的 Java RAG 问答工作流

5.1 工程结构与核心依赖

这一节用我项目里的一个最小闭环示例,演示"知识库问答工作流"从定义到运行的完整过程。工程本身就是一个 Spring Boot 应用,核心依赖包括:spring-boot-starter-web、spring-ai-starter、spring-ai-pgvector-store(或 alpha 版本对应坐标)、postgresql、apache-tika、mybatis-plus-spring-boot3-starter。

为什么引入 MyBatis-Plus?热词里提到"根据Java实体类生成创建表的SQL语句",这恰好是我项目里的常规操作:工作流实例表、Prompt 模板表、Token 明细表、评估记录表都是先写实体类,再用 MyBatis-Plus 的建表工具自动生成 DDL。对我来说,用这套方式做 AI 平台里的元数据表管理,比手写一堆 SQL 脚本省心得多,实体即表结构,改字段就改类。

代码结构大致如下,我截取关键目录:

com.example.llmops ├── controller # ChatController、WorkflowController ├── service # RagService、WorkflowService、EvaluationService ├── workflow # engine、node.executor、dsl ├── rag # splitter、embedding、retriever ├── gateway # ModelGateway、TokenRecorder ├── entity # PromptTemplate、WorkflowInstance、TokenUsage └── mapper # 对应 MyBatis-Plus 的 Mapper 接口

5.2 知识库检索链路的核心代码

文档切分我封装了一个简易实现,基于行数控制 chunk 大小。真正的生产环境建议用按段落和标题层级切分,但基础思路一致:维护一个当前块缓冲,块长度达到阈值就截断,并与上一篇保留重叠段:

public List<TextChunk> splitDocument(String content) { List<TextChunk> chunks = new ArrayList<>(); String[] lines = content.split("\\n"); StringBuilder buffer = new StringBuilder(); int idx = 0; for (String line : lines) { if (buffer.length() > 0 && buffer.length() + line.length() > MAX_CHUNK_SIZE) { chunks.add(new TextChunk(idx++, buffer.toString())); // 保留重叠部分,让上下文衔接 buffer.setLength(0); buffer.append(lastLines(buffer.toString(), OVERLAP_LINES)); } buffer.append(line).append("\\n"); } if (buffer.length() > 0) { chunks.add(new TextChunk(idx, buffer.toString())); } return chunks; }

向量化和入库,Spring AI 把过程封装得挺好。先用 EmbeddingModel 把文本转向量,然后通过 VectorStore 写入 PGVector 表。这里有一个真实经验:批量向量化时一定做分批提交和重试,我遇到过中途超时导致整批写入失败的问题,分批后虽然慢一点但稳得多。

public void ingest(String docId, List<TextChunk> chunks) { for (int i = 0; i < chunks.size(); i += BATCH_SIZE) { List<TextChunk> batch = chunks.subList(i, Math.min(i + BATCH_SIZE, chunks.size())); List<Document> docs = batch.stream() .map(c -> Document.builder() .id(docId + "_" + c.getIndex()) .text(c.getText()) .metadata(Map.of("docId", docId)) .build()) .toList(); vectorStore.add(docs); } }

检索阶段,我把双路召回的第一步用 Spring AI 的 VectorStore 做向量查询:

public List<Document> retrieve(String query, int topK) { SearchRequest request = SearchRequest.builder() .query(query) .topK(topK) .similarityThreshold(0.45) .build(); return vectorStore.similaritySearch(request); }

检索结果拿到后,接入重排、组装 Prompt,最终交给 ChatClient 生成回答。组装阶段要控制 Token 上限,我按输出最大字数和参考块数量限制做一个预估,超了就把靠后的参考块扔掉。宁可少给资料,也别把无关内容塞给模型。

5.3 工作流 DSL 与执行器跑通

一个最简单的"知识库问答"工作流,DSL 定义长这样:

{ "name": "知识库问答", "nodes": [ { "id": "start", "type": "start", "name": "开始" }, { "id": "retrieve", "type": "rag_search", "name": "知识库检索", "params": { "topK": 5 } }, { "id": "chat", "type": "llm_chat", "name": "大模型回答", "params": { "promptTemplate": "kb_qa_v3" } }, { "id": "end", "type": "end", "name": "结束" } ], "edges": [ { "source": "start", "target": "retrieve" }, { "source": "retrieve", "target": "chat" }, { "source": "chat", "target": "end" } ] }

执行器核心是策略注册表。每种节点类型的执行逻辑就是一个 Spring Bean,实现统一接口:

public interface WorkflowNodeExecutor { String nodeType(); WorkflowNodeResult execute(WorkflowContext ctx, WorkflowNode node); } @Component public class RagSearchNodeExecutor implements WorkflowNodeExecutor { private final RagService ragService; @Override public String nodeType() { return "rag_search"; } @Override public WorkflowNodeResult execute(WorkflowContext ctx, WorkflowNode node) { String query = ctx.getInput("query"); int topK = node.getIntParam("topK", 5); List<Document> docs = ragService.retrieve(query, topK); ctx.setData("documents", docs); return WorkflowNodeResult.success(Map.of("hitCount", docs.size())); } }

调度器按拓扑序执行,本质上是循环找"所有前置节点都已完成"的节点来跑,直到全部完成或失败。真实跑下来,这套东西的稳定性和可追踪性都很好。节点一旦失败,执行记录表里能看到异常堆栈,配合 TraceId 能快速定位问题。

5.4 管理界面上如何呈现

到了可视化层,前端读取这个 DSL JSON,调用 LogicFlow 渲染成画布。用户拖拽一个"知识库检索"节点到画布,属性面板填写 topK 参数,保存后后端只存一份 JSON。运行工作流时,后端通过 WebSocket 推送节点状态,画布上正在执行的节点高亮、输出参数实时展示,用户能清楚看到"它先搜了知识库,又把结果丢给大模型,最后生成答案"整个过程。

跑这个闭环我第一版花了大概两天,但真正把它打磨到可以拿给产品经理演示又花了一周——主要时间浪费在流式输出的节点状态对上。如果你也在做同样的事,建议先固定协议字段,让前端研发和后端研发同时联调,避免各改各的。

6. 常见问题与排查技巧实录

6.1 一张速查表:症状、排查方向、解法

把我在这个项目里遇到的典型问题整理成一张表,方便对照排查:

现象优先排查方向解决方案
流式回答中途断掉WebSocket 连接是否被网关断开、服务端异常被吞加心跳机制、后端流式异常统一捕获并推送 error 消息
RAG 答非所问召回结果相关度低、重排缺失查看日志里实际检索到的文档块,调 Query 改写和重排
工作流节点重试后 Token 费用翻倍节点执行和重试是否做了幂等节点执行前生成 executionId,模型调用前检测是否已计费
模型回答出现幻觉Prompt 里是否限制知识边界、参考块引用强化指令、参考文档低于阈值时直接让模型说不知道
流程执行卡住依赖下游节点的状态判断是否出错检查节点执行记录表状态机,补一条定时扫描超时实例的任务
换模型后效果起伏大不同模型的输出风格和能力差异先灰度放量,对比评估分数,再决定全量切换

6.2 我最常用的三条排查思路

排查 AI 应用问题,我的固定套路是:先看日志链路,再复现驱动,最后回归测试。看日志链路是第一步,通过 TraceId 拉出一次请求的全部日志,把模型输入输出打印出来,确认大模型收到的 Prompt 长什么样。很多"回答不对"的根因,其实在日志里一眼就能看到——比如参考文档全是无关内容,那问题大概率出在检索,不在模型。

复现驱动是针对工作流类问题的手段。DSL 定义是 JSON,我可以直接通过接口重放一次运行,带上相同的入参,观察每一次节点执行记录,对比出问题的节点是输入异常还是执行异常。这一步能区分是数据问题、代码问题还是配置问题。

回归测试是我坚持的最后一道防线。任何一次修改,跑一遍标准测试集,对比分数变化。没有这套机制,你会陷入"改好了 A 场景、搞坏了 B 场景"的恶性循环。

6.3 三个忠告:给准备入坑 Java LLMOps 的团队

第一,别一上来就奔着"通用 Agent 平台"去,那是个无底洞。先选一个具体业务场景,比如"客服知识库问答",用最小闭环把 RAG、工作流、评估这条链路跑通,再考虑横向扩展。第二,成本可视化和评估体系必须第一期就立项,不要拖到上线后。AI 应用的成本和效果不可见,就等于团队在裸奔。第三,架构上给自己留出换模型的余地——通过模型网关抽象的收益,远大于你预想。

另外说一句关于技能准备的话,热词里反复出现 Spring 三级缓存之类的 Java 面试题,对做这个方向反而有参考价值:LLMOps 平台的核心调度代码本身就是一个 Spring 容器管理大量 Bean 的系统,理解 Spring 的 Bean 生命周期和初始化顺序,对排查"节点执行器为什么没注册上""配置为什么没生效"这类问题帮助很大。我在项目初期就踩过 Bean 初始化顺序的坑,最后是靠画依赖图、理清配置加载顺序解决的。

结尾:一点个人体会

这套平台从第一个 Demo 到现在能稳定服务内部业务,前后大概用了三个多月,中途推翻了两次设计——第一次是没抽模型网关,第二次是工作流执行状态没落库。现在回头看,用 Java/Spring 做 LLMOps 完全走得通,而且和团队既有技术栈、运维体系、权限体系能无缝衔接,这在企业内部落地时是非常实在的优势。如果你也正被要求"用现有 Java 技术栈搭一个 AI 平台",别慌,先把知识库问答这个最小闭环跑通,再把编排和评估补上,路是一步一步走出来的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询