实战 | 基于 Spring Boot 封装大模型 API,构建企业级大模型后端服务

随着大语言模型(LLM)技术的普及,越来越多企业希望将 GPT、文心一言、通义千问等大模型能力集成到自有业务系统中。直接调用官方原生 API 存在接口不统一、鉴权复杂、缺乏业务适配、扩展性差等问题,而基于 Spring Boot 封装大模型 API,能打造一套标准化、可扩展、高可用的企业级大模型后端服务。本文将从架构设计、核心实现到部署优化,完整讲解如何落地这一方案。

一、为什么选择 Spring Boot 封装大模型 API?

Spring Boot 作为 Java 生态的主流微服务框架,天然适配企业级后端开发的需求:

  1. 快速开发:自动配置、起步依赖大幅降低配置成本,聚焦业务逻辑;
  2. 生态完善:可无缝集成 Spring Security(鉴权)、Spring Cache(缓存)、Spring Cloud(微服务)等组件;
  3. 标准化:RESTful API 设计、统一异常处理、日志规范等符合企业开发规范;
  4. 高可用:支持线程池、熔断降级、负载均衡,适配大模型调用的高并发场景;
  5. 易扩展:可灵活对接多厂商大模型,统一接口适配层,降低切换成本。

二、整体架构设计

我们采用 “分层设计 + 适配模式” 构建服务,核心架构分为 5 层:

┌─────────────────┐
│ 接入层(API)   │ 对外提供统一RESTful接口,处理请求参数校验、鉴权
├─────────────────┤
│ 适配层(Adapter)│ 对接不同大模型厂商(OpenAI、百度、阿里),统一调用逻辑
├─────────────────┤
│ 核心服务层(Service)│ 处理业务逻辑(上下文管理、prompt模板、限流)
├─────────────────┤
│ 配置层(Config)│ 管理大模型密钥、调用参数、线程池等配置
└─────────────────┘
│ 基础层(Common)│ 通用工具(日志、异常、缓存、熔断)
└─────────────────┘

核心设计原则:

  • 接口归一化:无论对接哪个厂商的大模型,对外暴露统一的 API;
  • 适配解耦:通过适配器模式隔离不同厂商的 API 差异;
  • 可观测性:全链路日志、监控,追踪大模型调用耗时、成功率;
  • 安全可控:鉴权、限流、敏感词过滤,避免滥用。

三、核心实现步骤

1. 环境准备与项目初始化

(1)创建 Spring Boot 项目

使用 Spring Initializr 创建项目,引入核心依赖:

<!-- 核心依赖 -->
<dependencies>
    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 配置处理器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 日志增强 -->
    <dependency>
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
        <version>7.4</version>
    </dependency>
    <!-- 熔断降级 -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-core</artifactId>
        <version>1.8.6</version>
    </dependency>
    <!-- HTTP客户端 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <!-- JSON处理 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <!-- 测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
(2)配置大模型参数

application.yml中配置多厂商大模型的基础参数(建议通过 Nacos / 配置中心管理):

llm:
  # OpenAI配置
  openai:
    api-key: ${OPENAI_API_KEY}
    base-url: https://api.openai.com/v1
    model: gpt-3.5-turbo
    timeout: 30000
  # 百度文心一言配置
  baidu:
    api-key: ${BAIDU_API_KEY}
    secret-key: ${BAIDU_SECRET_KEY}
    model: ernie-3.5
    timeout: 30000
  # 默认调用厂商
  default-provider: openai

2. 核心层实现

(1)定义统一接口

创建LlmClient接口,定义大模型调用的核心方法:

public interface LlmClient {
    /**
     * 同步调用大模型
     * @param prompt 提示词
     * @return 响应结果
     */
    LlmResponse chat(String prompt);

    /**
     * 流式调用大模型(返回SSE)
     * @param prompt 提示词
     * @return 流式响应
     */
    Flux<LlmStreamResponse> streamChat(String prompt);
}

定义统一响应对象:

@Data
public class LlmResponse {
    // 响应内容
    private String content;
    // 模型名称
    private String model;
    // 调用耗时(ms)
    private long duration;
    // 错误码
    private String code;
    // 错误信息
    private String errorMsg;
}

// 流式响应
@Data
public class LlmStreamResponse {
    private String content;
    private boolean finish; // 是否结束
}
(2)实现厂商适配器

以 OpenAI 为例,实现OpenaiLlmClient

@Component
@ConfigurationProperties(prefix = "llm.openai")
@Slf4j
public class OpenaiLlmClient implements LlmClient {
    private String apiKey;
    private String baseUrl;
    private String model;
    private int timeout;

    private WebClient webClient;

    @PostConstruct
    public void init() {
        this.webClient = WebClient.builder()
                .baseUrl(baseUrl)
                .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .clientConnector(new ReactorClientHttpConnector(
                        HttpClient.create().responseTimeout(Duration.ofMillis(timeout))
                ))
                .build();
    }

    @Override
    public LlmResponse chat(String prompt) {
        long startTime = System.currentTimeMillis();
        LlmResponse response = new LlmResponse();
        try {
            // 构建OpenAI请求参数
            Map<String, Object> request = new HashMap<>();
            request.put("model", model);
            request.put("messages", Collections.singletonList(
                    Map.of("role", "user", "content", prompt)
            ));
            request.put("temperature", 0.7);

            // 调用OpenAI API
            String result = webClient.post()
                    .uri("/chat/completions")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(String.class)
                    .block(Duration.ofMillis(timeout));

            // 解析响应(实际项目建议用POJO解析)
            JSONObject json = JSON.parseObject(result);
            String content = json.getJSONArray("choices")
                    .getJSONObject(0)
                    .getJSONObject("message")
                    .getString("content");

            response.setContent(content);
            response.setModel(model);
            response.setDuration(System.currentTimeMillis() - startTime);
            response.setCode("0000");
        } catch (Exception e) {
            log.error("OpenAI调用失败", e);
            response.setCode("9999");
            response.setErrorMsg(e.getMessage());
            response.setDuration(System.currentTimeMillis() - startTime);
        }
        return response;
    }

    @Override
    public Flux<LlmStreamResponse> streamChat(String prompt) {
        // 构建流式请求参数
        Map<String, Object> request = new HashMap<>();
        request.put("model", model);
        request.put("messages", Collections.singletonList(
                Map.of("role", "user", "content", prompt)
        ));
        request.put("stream", true);

        return webClient.post()
                .uri("/chat/completions")
                .bodyValue(request)
                .retrieve()
                .bodyToFlux(String.class)
                .map(line -> {
                    LlmStreamResponse streamResponse = new LlmStreamResponse();
                    if (line.startsWith("data: ")) {
                        String data = line.substring(6);
                        if ("[DONE]".equals(data)) {
                            streamResponse.setFinish(true);
                            return streamResponse;
                        }
                        try {
                            JSONObject json = JSON.parseObject(data);
                            String content = json.getJSONArray("choices")
                                    .getJSONObject(0)
                                    .getJSONObject("delta")
                                    .getString("content");
                            streamResponse.setContent(content);
                        } catch (Exception e) {
                            log.warn("解析流式响应失败", e);
                        }
                    }
                    return streamResponse;
                })
                .onErrorResume(e -> {
                    log.error("流式调用失败", e);
                    LlmStreamResponse errorResponse = new LlmStreamResponse();
                    errorResponse.setContent("调用失败:" + e.getMessage());
                    errorResponse.setFinish(true);
                    return Flux.just(errorResponse);
                });
    }

    // getter/setter
}

同理,可实现BaiduLlmClientAliLlmClient等适配器,隔离不同厂商的 API 差异。

(3)封装核心服务

创建LlmService,统一调度不同厂商的适配器:

@Service
@Slf4j
public class LlmService {
    @Value("${llm.default-provider:openai}")
    private String defaultProvider;

    // 注入所有LlmClient实现(Spring自动注入Map<bean名称, 实例>)
    @Autowired
    private Map<String, LlmClient> llmClientMap;

    /**
     * 统一调用入口
     * @param prompt 提示词
     * @param provider 厂商(可选,默认使用配置的厂商)
     * @return 响应结果
     */
    public LlmResponse chat(String prompt, String provider) {
        // 校验参数
        if (StringUtils.isBlank(prompt)) {
            LlmResponse response = new LlmResponse();
            response.setCode("1001");
            response.setErrorMsg("提示词不能为空");
            return response;
        }
        // 选择厂商
        String targetProvider = StringUtils.isNotBlank(provider) ? provider : defaultProvider;
        LlmClient llmClient = llmClientMap.get(targetProvider + "LlmClient");
        if (llmClient == null) {
            LlmResponse response = new LlmResponse();
            response.setCode("1002");
            response.setErrorMsg("不支持的厂商:" + targetProvider);
            return response;
        }

        // 熔断降级(Sentinel)
        try (Entry entry = SphU.entry("llm_chat")) {
            return llmClient.chat(prompt);
        } catch (BlockException e) {
            log.warn("大模型调用触发限流", e);
            LlmResponse response = new LlmResponse();
            response.setCode("1003");
            response.setErrorMsg("当前调用量过大,请稍后再试");
            return response;
        } catch (Exception e) {
            log.error("大模型调用异常", e);
            LlmResponse response = new LlmResponse();
            response.setCode("9999");
            response.setErrorMsg("调用失败:" + e.getMessage());
            return response;
        }
    }

    /**
     * 流式调用入口
     */
    public Flux<LlmStreamResponse> streamChat(String prompt, String provider) {
        if (StringUtils.isBlank(prompt)) {
            LlmStreamResponse error = new LlmStreamResponse();
            error.setContent("提示词不能为空");
            error.setFinish(true);
            return Flux.just(error);
        }
        String targetProvider = StringUtils.isNotBlank(provider) ? provider : defaultProvider;
        LlmClient llmClient = llmClientMap.get(targetProvider + "LlmClient");
        if (llmClient == null) {
            LlmStreamResponse error = new LlmStreamResponse();
            error.setContent("不支持的厂商:" + targetProvider);
            error.setFinish(true);
            return Flux.just(error);
        }
        return llmClient.streamChat(prompt);
    }
}

3. 接入层实现(对外 API)

创建 Controller,提供 RESTful 接口:

@RestController
@RequestMapping("/api/v1/llm")
@Slf4j
public class LlmController {
    @Autowired
    private LlmService llmService;

    /**
     * 同步调用接口
     */
    @PostMapping("/chat")
    public ResponseEntity<LlmResponse> chat(
            @RequestParam String prompt,
            @RequestParam(required = false) String provider) {
        LlmResponse response = llmService.chat(prompt, provider);
        return ResponseEntity.ok(response);
    }

    /**
     * 流式调用接口(SSE)
     */
    @GetMapping(value = "/stream/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<LlmStreamResponse>> streamChat(
            @RequestParam String prompt,
            @RequestParam(required = false) String provider) {
        return llmService.streamChat(prompt, provider)
                .map(res -> ServerSentEvent.<LlmStreamResponse>builder()
                        .data(res)
                        .build())
                .doOnError(e -> log.error("流式接口异常", e));
    }
}

4. 增强能力

(1)鉴权

集成 Spring Security 实现 API 鉴权(如 API Key、JWT):

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/v1/llm/**").authenticated()
                        .anyRequest().permitAll()
                )
                .addFilterBefore(new ApiKeyFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}
(2)缓存

对高频相同 prompt 的响应结果进行缓存:

@Cacheable(value = "llm_cache", key = "#prompt + '_' + #provider")
public LlmResponse chat(String prompt, String provider) {
    // 原有逻辑
}
(3)日志与监控

添加全链路日志追踪,集成 Prometheus 监控调用指标:

// 日志增强(MDC追踪)
MDC.put("traceId", UUID.randomUUID().toString());
try {
    // 调用逻辑
} finally {
    MDC.clear();
}

// 监控指标(自定义Metrics)
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCustomizer() {
    return registry -> registry.counter("llm.call.count", "provider", "openai");
}

四、部署与优化

1. 部署建议

  • 容器化:通过 Docker 打包,配合 K8s 实现弹性扩缩容;
  • 配置中心:使用 Nacos/Apollo 管理大模型密钥、参数,避免硬编码;
  • 密钥管理:敏感信息(如 API Key)通过 K8s Secret / 阿里云 KMS 加密存储;
  • 多环境隔离:开发 / 测试 / 生产环境使用不同的大模型密钥和配置。

2. 性能优化

  • 线程池优化:为大模型调用配置独立线程池,避免主线程阻塞;
  • 连接池复用:优化 WebClient 的连接池参数,减少 TCP 握手开销;
  • 超时控制:针对不同厂商设置合理的超时时间,避免长时间阻塞;
  • 熔断降级:通过 Sentinel/Hystrix 设置熔断规则,避免雪崩效应;
  • 预加载:预热大模型客户端连接,减少首次调用耗时。

3. 扩展性设计

  • 插件化:新增大模型厂商时,只需实现LlmClient接口,无需修改核心逻辑;
  • 多模型路由:根据业务场景(如创作、问答)自动选择最优模型;
  • 上下文管理:支持多轮对话的上下文缓存,避免重复传递历史信息;
  • Prompt 模板:内置通用 Prompt 模板,支持业务自定义模板。

五、总结

基于 Spring Boot 封装大模型 API,不仅能解决多厂商接口适配的问题,还能结合 Java 生态的成熟组件,打造安全、稳定、可扩展的企业级大模型后端服务。本文提供的架构和代码示例,可作为基础框架,根据实际业务需求扩展上下文管理、Prompt 工程、知识库对接等能力。

在实际落地过程中,需重点关注:

  1. 大模型调用的成本控制(按量计费);
  2. 敏感信息的过滤与合规;
  3. 高并发场景下的性能与稳定性;
  4. 多模型的效果对比与路由策略。

通过这套方案,企业可以快速将大模型能力融入自有业务,同时保障系统的可维护性和可扩展性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

canjun_wen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值