Java大模型后端编写全流程指南
2025-12-15 18:15 tlnshuju 阅读(0) 评论(0) 收藏 举报用 Java 做大模型后端,整体可以理解为两件事:
把“模型能力”包装成一个可靠的服务(API)
让业务方(Web/小程序/APP/内部系统)能安全、高效地调用它
我先给你一个整体开发流程,然后分模块说需要注意什么,中间穿插一点 Spring Boot 风格的示例,方便你落地。
一、整体开发流程(从 0 到 上线)
第 1 步:明确需求和使用场景
先把这些问题写清楚:
是做 聊天问答、补全、多轮对话,还是 工具调用(函数调用)、agent 流程?
延迟要求:用户能接受多少秒的响应?是否需要 流式输出(像 ChatGPT 那样一字一字出)?
并发量预估:QPS、峰值、日调用量?
模型来源:
直接调用云端大模型 API(如 OpenAI、DeepSeek、Moonshot、本地/私有化等)
还是自己部署本地/私有化大模型(vLLM、Triton 等推理服务)?
数据要求:是否涉及敏感数据 / 内网数据,是否要做 脱敏和日志控制?
这些会直接影响你后面:
架构复杂度
需要哪些中间件(缓存、消息队列、向量库等)
安全/合规要怎么做
第 2 步:技术选型与基本架构
后端框架(推荐):
Spring Boot:生态最成熟,文档多,适合大部分企业项目
对性能要求特别高或需要响应式的,可以考虑 Spring WebFlux 或 Vert.x,但一般 Spring MVC 足够
典型架构:
gateway/api:对外暴露 HTTP/HTTPS 接口llm-service:封装调用大模型的逻辑vector-service(可选):封装向量和检索,用于 RAGauth-service:鉴权、限流、配额logging & monitoring:统一日志、打点、链路追踪
可以先从 单体 Spring Boot 应用 开始,把这些作为不同 package / module 抽象,将来再拆微服务。
第 3 步:项目初始化
以 Spring Boot 为例:
用 Spring Initializr 建一个项目:
依赖:
Spring Web、Spring Validation、Spring Boot Actuator、Lombok、Spring Security(按需)等
设计基本目录结构,例如:
com.example.llmbackend
├── api // controller 层
├── service // 业务逻辑 & 模型编排
├── client // 调用大模型/向量库/其他服务
├── config // 配置类
├── dto // 请求/响应对象
├── domain // 领域对象(如果有)
└── infra // 基础设施,例如缓存、持久化等
第 4 步:对接大模型(核心)
这里分两种情况:调用外部模型 API 和 调用本地/私有化模型服务。
4.1 调用外部模型 API
一般就是 HTTP + JSON,流程:
定义一个 client(可以用
RestTemplate或WebClient,或 OkHttp/Feign)封装通用请求参数(模型名、温度、max_tokens、system prompt 等)
做好:
超时控制
重试策略
错误码转换(外部错误 -> 内部统一错误码)
伪代码示例(同步调用):
@Service
public class LlmClient {
private final WebClient webClient;
public LlmClient(WebClient.Builder builder) {
this.webClient = builder
.baseUrl("https://api.your-llm.com/v1")
.build();
}
public String chat(String model, String prompt) {
LlmRequest request = new LlmRequest(model, prompt);
LlmResponse response = webClient.post()
.uri("/chat/completions")
.header("Authorization", "Bearer " + "YOUR_API_KEY")
.bodyValue(request)
.retrieve()
.bodyToMono(LlmResponse.class)
.block(); // 简单起见,用 block,同步模型调用
return response.getChoices().get(0).getMessage().getContent();
}
}
实际上还需要加上超时、重试、日志等配置,避免调用卡死线程。
4.2 调用本地 / 私有化推理服务
比如你自己用 Python/vLLM 部署了一套 HTTP 服务,这里 Java 还是当“HTTP 客户端”,模式一样,只是:
要注意 内网通信安全(mTLS、内网地址隔离)
服务发现(k8s Service、Nacos、Consul 等)
可能需要 批量接口(一次请求多条 prompt,做批推理)
第 5 步:设计对外 API(REST / WebSocket)
一般会有几类接口:
对话接口(非流式)
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ChatService chatService;
public ChatController(ChatService chatService) {
this.chatService = chatService;
}
@PostMapping
public ChatResponse chat(@Valid @RequestBody ChatRequest request) {
return chatService.chat(request);
}
}
ChatRequest里通常有:sessionId(会话 ID)messages(历史消息)tools/functions(可选)temperature、maxTokens等
ChatResponse需要包含:回复内容
token 消耗信息
是否触发工具调用等状态
对话接口(流式,效果更友好)
HTTP chunked / SSE / WebSocket 三种常见方式
Spring Boot 可用:
SseEmitter或 WebFlux 的Flux<ServerSentEvent<...>>
注意点:
流式接口要配合前端使用,例如前端逐字填充聊天内容
要考虑 中途取消(客户端断开,服务端要停止模型推理)
第 6 步:会话管理 & 上下文
多轮对话必须解决“上下文”问题:
简单做法:前端每次带上全部历史消息(但消息长会很贵)
稍微专业一点:
只带最近 N 轮消息
做 摘要(summary),把远历史压缩成一段短描述
结合 RAG:从知识库取 Top-K 相关文档,放入 system prompt 或上下文中
服务端需要做的:
存储会话 & 消息(Redis/MySQL/Mongo 均可)
提供接口:创建会话 / 拉取历史 / 删除会话
设计一个“构造上下文”的策略类(ContextBuilder),在调用模型前组合 prompt
第 7 步:集成 RAG(如果要查知识库)
简单版本流程:
文档离线切分 + 向量化,存入向量库(如 Milvus、PgVector、Elasticsearch + dense vector 字段 等)
查询时:
根据用户问题做向量检索,拿到相似的文档片段
将这些片段拼接到 prompt 里面,告诉模型:“以下是相关资料,你只能基于这些回答”
Java 侧需要:
一个
VectorStoreClient来封装向量库操作(插入/查询)一个
RagService,负责:调用向量库取文档
组装 prompt
调用
LlmClient
第 8 步:性能优化 & 稳定性
这里是大模型后端最容易踩坑的部分。
8.1 并发与线程模型
模型调用一般是 IO 密集(网络/推理服务),要避免大量阻塞线程
Spring WebFlux +
WebClient(响应式) 可以更高效地利用线程池对外 API 的 timeout 要 小于 模型调用和上游负载均衡的 timeout,避免“僵尸请求”
8.2 限流 & 熔断 & 重试
针对接口、IP、用户、API key 做限流(如基于 Redis + Lua,或用 Resilience4j / Sentinel)
模型服务不稳定时:
快速失败(熔断),避免拖垮整条链路
明确错误信息给调用方(例如:系统繁忙,请稍后重试)
8.3 连接池 & Keep-Alive
调用外部模型 API 的 HTTP 客户端要:
复用连接(keep-alive)
设置最大连接数 / 每主机连接数
设置合理读写超时
第 9 步:日志、监控、追踪
至少要做到:
请求日志(不记录敏感信息 / prompt 可脱敏)
调用大模型的耗时、token 消耗、成功率
针对错误场景的报警(例如:5xx 比例 > 某阈值)
可以引入:
Prometheus + Grafana 做指标
Zipkin / Jaeger + Spring Cloud Sleuth 做链路追踪
ELK/EFK 做日志检索
第 10 步:部署与环境
容器化:用 Docker 打包 Spring Boot 应用,部署到 k8s
配置管理:使用 config server / Nacos / k8s ConfigMap & Secret
区分环境:
dev:直接连沙盒模型/小模型,便于调试
staging:接近生产的模型/参数,用来做压力测试
prod:配好限流、监控、告警后,再对外开放
二、开发中需要特别注意的点(踩坑清单)
我按主题给你列一份“注意事项”清单:
1. 安全 & 权限
对外一定要有 鉴权(token、API key、OAuth2 等)
对内部调用模型时的 API key / 密钥,放在:
环境变量,或
k8s Secret,或
专门的密钥管理系统
对日志中的:
用户输入(prompt)
模型输出(尤其含隐私信息)
做脱敏或可控开关
2. 成本控制
统计每次调用的:
输入 token
输出 token
按用户/部门/应用聚合统计,用于:
限额
结算/报销
优化提示词和上下文长度
3. Prompt 设计 & 可配置化
system prompt / 模板尽量做成 可配置(数据库 / 配置中心),避免写死在代码里
给不同业务线不同模板(客服、代码助手、知识问答等)
4. 版本管理 & 回滚
模型版本切换要小心:
比如从
model-v1切到model-v2最好支持灰度:某个比例/某些用户先试新模型
保留能力:快速切回旧模型(配置开关 + 配置中心)
5. 测试策略
单元测试:
对业务逻辑、上下文构建、RAG 逻辑做单测
对 LlmClient 可以 mock 掉真实模型调用
集成测试:
在测试环境调用真实/小号模型,看整体链路
回归测试:
抽取一批典型问题,做 A/B,对比新旧版本的“主观效果”(可以人工评审)
三、给你一个极简端到端示例结构(非完整代码)
请求 DTO:
@Data
public class ChatRequest {
@NotBlank
private String sessionId;
@NotBlank
private String userMessage;
private Double temperature;
private Integer maxTokens;
}
响应 DTO:
@Data
@AllArgsConstructor
public class ChatResponse {
private String reply;
private long promptTokens;
private long completionTokens;
}
Service:
@Service
public class ChatService {
private final LlmClient llmClient;
private final ConversationStore conversationStore; // 自己实现,可用 Redis/DB
public ChatService(LlmClient llmClient, ConversationStore conversationStore) {
this.llmClient = llmClient;
this.conversationStore = conversationStore;
}
public ChatResponse chat(ChatRequest request) {
// 1. 取历史记录,构造上下文
List history = conversationStore.getHistory(request.getSessionId());
// 2. 构造 prompt(这里只是简单拼接)
String prompt = buildPrompt(history, request.getUserMessage());
// 3. 调用大模型
LlmResult result = llmClient.chat("your-model-name", prompt, request.getTemperature(), request.getMaxTokens());
// 4. 存历史
conversationStore.append(request.getSessionId(), request.getUserMessage(), result.getReply());
// 5. 返回
return new ChatResponse(result.getReply(), result.getPromptTokens(), result.getCompletionTokens());
}
private String buildPrompt(List history, String userMessage) {
// 这里可以做更多控制:截断、摘要、RAG 等
StringBuilder sb = new StringBuilder();
for (Message msg : history) {
sb.append(msg.getRole()).append(": ").append(msg.getContent()).append("\n");
}
sb.append("user: ").append(userMessage);
return sb.toString();
}
}
四、如果你想要一个“实战版”的开发路径
我帮你排一个可以直接照着走的 checklist:
用 Spring Boot 初始化一个项目,跑通一个
/health接口写一个
LlmClient,先随便用 mock 返回固定回答(不连真实模型)定义
ChatRequest/ChatResponse,写ChatController+ChatService,本地调通一问一答把
LlmClient替换成真实模型 API 调用,加入:超时
错误处理
简单日志
加上会话管理(按 sessionId 存历史),实现多轮对话
实现一个简单的限流和鉴权(比如每个 API key 每分钟 N 次)
部署到测试环境(Docker + k8s / 云主机)
加监控(Actuator + Prometheus)和日志搜索
压测(JMeter/Locust),根据瓶颈优化线程池、超时、连接池
最后再逐步加:RAG、流式输出、工具调用、灰度模型等能力
浙公网安备 33010602011771号