如果你写 Java 写了几年,现在被安排「给系统接个大模型」,大概率会卡在这几步:不知道该用 Spring AI 还是 LangChain4j,不知道同步阻塞的 Servlet 栈怎么接流式输出,也不知道上下文、向量库这些组件在 Java 侧怎么落。这篇不聊原理八卦,只给你一条从选型到上线的执行路径,以及每一步该去站内哪一页核对当时的数字。
如果你写 Java 写了几年,现在被安排「给系统接个大模型」,大概率会卡在这几步:不知道该用 Spring AI 还是 LangChain4j,不知道同步阻塞的 Servlet 栈怎么接流式输出,也不知道上下文、向量库这些组件在 Java 侧怎么落。这篇不聊原理八卦,只给你一条从选型到上线的执行路径,以及每一步该去站内哪一页核对当时的数字。
先明确一件事:Java 接大模型不是「换一个 SDK」那么简单,它牵扯到分层、超时、流式、上下文、检索、成本这几块。下面按顺序讲,每节都给判断标准和可执行动作,你可以照着一步步做,也可以挑自己卡住的那节先看。
第一步:主流框架怎么选,别一上来就纠结
先把三类东西分清:模型客户端 SDK(各家官方 Java SDK、OpenAI 兼容封装)、应用框架(Spring AI、LangChain4j、Semantic Kernel 的 Java 版)、编排层(工作流、Agent)。多数业务只需要前两类,编排层是后面真要串多步逻辑再引入的东西,别一上来就堆。
- 先分清三类东西:模型客户端 SDK、应用框架、编排层。多数业务只需要前两类。
- 选型只看三件事:你的 Spring 版本能不能对上、需要的能力(流式、Function Calling、RAG、多模型切换)框架有没有现成实现、出问题能不能自己读源码改。
- 团队已经是 Spring Boot,优先用与版本匹配的 Spring AI,学习成本最低;模型要频繁换、需要灵活链式编排,看 LangChain4j。
- 不适用的情况:只调一次接口做个小功能,别引框架,直接 HTTP 客户端加一层薄封装更省事。
| 方案 | 适合场景 | 什么时候别用 |
|---|---|---|
| Spring AI | 已经是 Spring Boot 体系,能力需求常规 | Spring 版本对不上又不愿升级 |
| LangChain4j | 模型要频繁切换、需要链式编排 | 团队没人愿意读框架源码 |
| 直接 HTTP 封装 | 只做一两个接口、逻辑很薄 | 需要流式、多轮、RAG 等成套能力 |
判断标准就一句话:框架能帮你省掉多少自己写、又不会挡着你改。如果引入框架后每加一个功能都要绕开它的抽象,那说明当前阶段不该用它。各家框架支持的能力细节会变,选之前到 /guides 看当时的对比再定。
把模型调用封装成一层,别让它散在业务代码里
这一步是后面所有工作的地基。如果模型调用散落在各个 Service 里,换模型、换中转站、加限流都要改一大片。正确做法是建一个 ModelClient 接口,方法签名只暴露业务语义,比如 generate、stream、embed,底下换什么实现都不影响上层。
- 定义接口只暴露业务语义方法,返回值用业务对象包装,不要把上游 SDK 的响应原样抛给上层。
- 集中配置base_url、api key、超时、重试次数、并发上限全部走配置中心或环境变量,禁止硬编码在代码里。
- 加可观测字段每次调用带上请求 id、模型标识、耗时、token 用量、是否命中缓存。日志里不要打完整 prompt 和用户隐私字段。
- 量延迟基线换模型前到 /tools/speed-test 跑一遍你本地到接口的往返耗时,再决定超时和重试阈值。
配置项里最容易踩的是鉴权头和参数名差异。不同家的字段名、鉴权方式、流式返回结构都有差别,封装层要吃掉这些差异。具体各家参数到 /apis 核对当时的文档口径,不要凭记忆写。
流式输出:Java 侧的阻塞模型怎么处理
同步栈接流式,核心就一句:别把上游的分块响应攒完再一次性返回。Spring MVC 里用 SseEmitter 最直接,注意设置超时时间并且每条消息后 flush;WebFlux 栈就用 Flux 返回,两者别混着用,混了容易出线程模型问题。
- 首字延迟
- 从发出请求到收到第一个增量片段的耗时,用户体感最直接的指标
- 总时长
- 从请求到最后一个片段到达的耗时,用来判断上游是否整体变慢
- 退避重试
- 用指数加抖动的间隔重试,别固定间隔猛冲上游
异常处理要分两类:连接中途断开(客户端还在,可提示重试)、上游限流或超时(需要退避重试)。退避要用指数加抖动,固定间隔容易在限流时把问题放大。另外,如果还要支持非流式的批处理场景,两条路径共用同一个 ModelClient,只在最外层区分返回类型。
上下文与记忆:多轮对话怎么存、怎么裁
多轮对话的历史建议落库或落 Redis,按会话 id 存有序消息列表。短期上下文放进程内存的做法只适合 demo,多实例部署后会各看各的,用户刷新一下就像失忆。
- 裁剪策略至少准备两种:按条数滑窗、按 token 预算截断。
- 截断时优先丢最早的普通轮次,保留系统提示和最近几轮。
- 系统提示词单独抽出来做版本管理,改了要能回滚,别写死在代码字符串里。
- 往上下文里塞检索结果时,控制条数和总长度,别把窗口全占满导致最近对话被挤掉。
上下文窗口的选法到站内模型资料页面按当时口径核对,不同模型上限差异很大,不要凭印象设一个固定值。裁剪逻辑最好写成可测试的纯函数,方便用构造的会话数据验证边界情况。
RAG 落地的 Java 组件与顺序
RAG 的链路固定是五步:文档解析 → 切分 → 向量化 → 入库 → 检索拼接。每一步都能单独替换,不要一开始就追求端到端跑通,先把文档解析和切分做对,后面才有意义。
向量库先按现有运维能力选:已有 PostgreSQL 可以先用带向量扩展的方案,已有 ES 也有对应能力,没必要为了向量再引一套新中间件。引入新中间件意味着多一套备份、监控、扩容要管,小团队要算这笔账。
| 环节 | 常见做法 | 验证方式 |
|---|---|---|
| 文档解析 | 按格式选解析器,表格和代码单独处理 | 抽几篇真实文档看文本是否完整 |
| 切分 | 按语义或固定长度,设置重叠 | 检查切出来的块是否读得通 |
| 向量化 | 走 ModelClient 的 embed 方法 | 对比相似句子得分是否明显高于无关句 |
| 检索拼接 | 取 top-k 拼进 prompt,带出处 | 看回答能否引用到正确来源 |
检索结果拼进 prompt 时给出处标注,方便前端做引用展示,也方便排查「答非所问」是检索差还是生成差。切分参数要拿真实文档试,通用默认值往往不适合表格和代码类语料。
上线前必须做的稳定性与成本动作
上线前把这几件事做了,能省掉大部分半夜被叫起来的情况。限流与并发要给自己定一个每模型的并发上限,超出就排队或降级,不要任由业务代码无限并发打上游。降级预案要有主模型不可用时切备用模型或返回缓存结果的开关。
- 定并发上限按模型的配额和你的机器能力设上限,超出就排队或降级。
- 做降级开关主模型不可用时切备用模型或返回缓存结果,开关要能被配置中心动态控制。
- 记成本口径记录每次调用的输入输出 token 数,按当时单价估算,用 /tools/model-cost-calculator 对一遍。
- 灰度与回滚新 prompt、新模型先放小流量,带上对比指标,出问题能一键切回旧配置。
出问题时的排查顺序
模型应用出问题,最容易一上来就怀疑模型能力,其实真正常见的锅在网络、鉴权和限流。按下面顺序排查,能少走很多弯路。
- 查网络与鉴权接口返回 401/403 先查 key 与 base_url 是否被代理层改写。
- 查上游限流拿到 429 就看自己的并发与重试策略,别只怪模型。
- 查 prompt 变化对比最近的 prompt 版本与历史输入,快速回滚验证。
- 最后怀疑模型换成同规格另一个模型做对照,如果结果一样,问题大概率在检索或数据。
这个顺序背后的逻辑是:从最确定、最容易回滚的地方查起。prompt 回滚是一条命令的事,换模型和查数据要花的时间长得多。平时把每次调用的请求 id、模型标识、prompt 版本记全,出事时排查会快很多。
下一步要把上面的动作真正落地,先确认你要对接的模型和中转站当时的口径——价格、可用性、参数差异都会变,去站内核对一遍再动手,比照记忆写代码靠谱得多。→常见问题
Java 做 AI 应用是不是比 Python 差一截?
在模型训练和实验脚本上确实 Python 生态更全,但业务应用侧 Java 并不吃亏:Spring 体系在鉴权、事务、并发、监控上更成熟。真正的差别是部分新框架首发只有 Python 版本,Java 侧要等社区适配,或者你自己用 HTTP 对接接口绕过。
一定要用 Spring AI 之类的框架吗?
不一定。只有一个模型、一个场景、几处调用,用 HTTP 客户端加一层薄封装反而更好维护。框架的价值在你要多模型切换、要 Function Calling、要 RAG 组件现成拼装的时候才体现出来,否则就是多背一套概念。
上下文太长把请求撑爆了怎么办?
先量出实际 token 用量,再定截断策略:系统提示固定保留,最近几轮保留,中间按预算丢。检索类的场景优先压缩检索片段而不是压缩对话历史。具体上限按当时模型口径到 /models 核对,不要写死常量。
Java 侧接向量库会不会很麻烦?
大部分向量库都有 Java 客户端,但更实际的做法是先复用你已有的存储,比如带向量能力的 PostgreSQL 或 ES,少引一套中间件。等到数据量或延迟真的成为瓶颈,再单独换专用向量库,迁移成本可控。
怎么控制大模型调用费用不失控?
三条:调用前做请求合并与缓存、调用中记录 token 用量、调用后按单价估算并看趋势。选型阶段的成本对照可以到 /official-api 看官方价目与规格,再结合 /tools/model-cost-calculator 估算,别只凭感觉。