Java开发者必看:SmallThinker-3B-Preview在SpringBoot项目中的集成详解

张开发
2026/4/16 11:40:17 15 分钟阅读

分享文章

Java开发者必看:SmallThinker-3B-Preview在SpringBoot项目中的集成详解
Java开发者必看SmallThinker-3B-Preview在SpringBoot项目中的集成详解最近在帮团队做技术选型发现不少Java后端同学对如何把大模型能力“丝滑”地集成到自己的SpringBoot项目里还是有点犯怵。总觉得这是Python或者算法工程师的活儿Java这边就是调个API没什么好讲的。其实不然。一个稳定、可维护、易测试的AI服务集成方案对线上服务的可靠性至关重要。今天我就以SmallThinker-3B-Preview这个轻量又聪明的模型为例手把手带你走一遍在SpringBoot项目里的完整集成流程。我们会从一个真实的“智能工单分类”微服务场景出发把从环境准备、服务调用、异常处理到测试验证的每个环节都理清楚。读完这篇文章你不仅能学会怎么调通这个模型的API更能掌握一套在Java后端项目中设计和封装AI服务的通用方法论。咱们不玩虚的直接上代码看实战。1. 环境准备与项目搭建在开始写代码之前得先把“舞台”搭好。这里假设你已经有一个基础的SpringBoot项目了如果没有用Spring Initializr生成一个也很方便。1.1 模型服务访问准备首先SmallThinker-3B-Preview模型通常以API服务的形式提供。你需要确保两件事获取API访问端点这通常是一个HTTP URL比如https://api.example.com/v1/chat/completions。具体地址需要根据模型服务提供方的文档来确定。准备认证信息大部分服务都需要API Key进行鉴权。这个Key要妥善保管千万别硬编码在代码里提交到仓库。1.2 项目依赖引入我们的集成主要涉及网络调用和JSON处理SpringBoot生态里已经有很成熟的组件了。在你的pom.xml文件里确保有以下依赖dependencies !-- SpringBoot Web Starter (包含RestTemplate等) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 如果你更喜欢响应式编程可以引入WebClient -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId scopetest/scope !-- 或根据实际需要调整scope -- /dependency !-- 参数校验 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency !-- 单元测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency !-- 集成Swagger/OpenAPI文档可选但推荐 -- dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.3.0/version !-- 请使用最新稳定版 -- /dependency /dependencies1.3 配置管理把API密钥和端点地址放到配置文件里这是安全性和灵活性的基础。在application.yml中配置app: ai: smallthinker: api-base-url: https://your-model-service-endpoint.com/v1 api-key: ${SMALLTHINKER_API_KEY:} # 优先从环境变量读取 timeout-ms: 30000 # 超时时间设置为30秒然后在代码里通过ConfigurationProperties来绑定这些配置这样管理起来更清晰import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; Data Component ConfigurationProperties(prefix app.ai.smallthinker) public class SmallThinkerProperties { private String apiBaseUrl; private String apiKey; private Integer timeoutMs 30000; }2. 核心服务层设计与实现这是集成的核心部分。我们的目标是把对AI模型的调用封装成一个内部服务对业务代码来说就像调用一个普通的Service方法一样简单。2.1 定义请求与响应模型首先根据SmallThinker-3B-Preview的API文档定义我们通信用的数据结构。这能让代码更清晰也方便后续的序列化和反序列化。import lombok.Data; import java.util.List; // 模拟AI服务通用的消息格式 Data public class ChatMessage { private String role; // “system”, “user”, “assistant” private String content; } // 发送给模型API的请求体 Data public class ChatCompletionRequest { private String model “smallthinker-3b-preview”; // 指定模型 private ListChatMessage messages; private Double temperature 0.7; // 控制随机性 private Integer maxTokens 500; // 生成的最大长度 } // 接收模型API的响应体 Data public class ChatCompletionResponse { private String id; private String object; private Long created; private ListChoice choices; private Usage usage; Data public static class Choice { private Integer index; private ChatMessage message; private String finishReason; } Data public static class Usage { private Integer promptTokens; private Integer completionTokens; private Integer totalTokens; } }2.2 使用RestTemplate实现同步调用对于大多数常规的Spring MVC应用RestTemplate是个简单直接的选择。我们来封装一个服务类。import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.time.Duration; Service public class SmallThinkerRestTemplateService { private final RestTemplate restTemplate; private final SmallThinkerProperties properties; Autowired public SmallThinkerRestTemplateService(RestTemplateBuilder restTemplateBuilder, SmallThinkerProperties properties) { this.properties properties; // 配置带超时和认证头的RestTemplate this.restTemplate restTemplateBuilder .setConnectTimeout(Duration.ofMillis(properties.getTimeoutMs())) .setReadTimeout(Duration.ofMillis(properties.getTimeoutMs())) .defaultHeader(HttpHeaders.AUTHORIZATION, “Bearer “ properties.getApiKey()) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build(); } public String chatCompletionSync(ListChatMessage messages) { String url UriComponentsBuilder .fromHttpUrl(properties.getApiBaseUrl()) .path(“/chat/completions”) .build() .toUriString(); ChatCompletionRequest request new ChatCompletionRequest(); request.setMessages(messages); HttpEntityChatCompletionRequest entity new HttpEntity(request); try { ResponseEntityChatCompletionResponse response restTemplate.postForEntity( url, entity, ChatCompletionResponse.class); if (response.getStatusCode() HttpStatus.OK response.getBody() ! null) { ChatCompletionResponse body response.getBody(); if (body.getChoices() ! null !body.getChoices().isEmpty()) { return body.getChoices().get(0).getMessage().getContent(); } } // 更精细的错误处理可以在这里进行比如抛出自定义业务异常 return “模型服务返回了非预期结果。”; } catch (Exception e) { // 记录日志并抛出或返回友好的错误信息 throw new RuntimeException(“调用AI模型服务失败”, e); } } }2.3 使用WebClient实现异步/响应式调用如果你的项目是响应式架构比如用了Spring WebFlux或者你想避免阻塞当前线程WebClient是更好的选择。import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; import java.time.Duration; Service public class SmallThinkerWebClientService { private final WebClient webClient; private final SmallThinkerProperties properties; Autowired public SmallThinkerWebClientService(WebClient.Builder webClientBuilder, SmallThinkerProperties properties) { this.properties properties; this.webClient webClientBuilder .baseUrl(properties.getApiBaseUrl()) .defaultHeader(HttpHeaders.AUTHORIZATION, “Bearer “ properties.getApiKey()) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build(); } public MonoString chatCompletionAsync(ListChatMessage messages) { ChatCompletionRequest request new ChatCompletionRequest(); request.setMessages(messages); return webClient.post() .uri(“/chat/completions”) .bodyValue(request) .retrieve() .bodyToMono(ChatCompletionResponse.class) .timeout(Duration.ofMillis(properties.getTimeoutMs())) // 设置超时 .map(response - { if (response.getChoices() ! null !response.getChoices().isEmpty()) { return response.getChoices().get(0).getMessage().getContent(); } return “模型服务返回了非预期结果。”; }) .onErrorResume(e - { // 这里可以记录日志并返回一个默认值或错误信号 return Mono.just(“请求AI模型服务时发生错误: “ e.getMessage()); }); } }3. 业务场景实战智能工单分类光有服务层还不够我们得把它用到一个具体的业务场景里这样理解才深刻。假设我们有一个客服系统用户提交的工单内容五花八门需要自动分类到“技术问题”、“账单咨询”、“账号问题”等类别。3.1 设计业务服务接口首先定义一个业务层的服务接口它内部会调用我们刚才封装的AI服务。public interface TicketClassificationService { /** * 根据工单内容智能分类 * param ticketContent 工单内容 * return 分类结果如“技术问题” */ String classifyTicket(String ticketContent); }3.2 实现分类服务然后实现这个接口。核心思路是构造一个清晰的提示Prompt引导模型进行文本分类。import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; Service public class TicketClassificationServiceImpl implements TicketClassificationService { Autowired private SmallThinkerRestTemplateService aiService; // 或注入WebClient版本 // 定义我们支持的工单类别 private static final ListString SUPPORTED_CATEGORIES Arrays.asList( “技术问题”, “账单咨询”, “账号问题”, “产品建议”, “投诉”, “其他” ); Override public String classifyTicket(String ticketContent) { // 1. 构造系统指令明确告诉模型它的角色和任务 ChatMessage systemMsg new ChatMessage(); systemMsg.setRole(“system”); systemMsg.setContent(“你是一个专业的客服工单分类助手。请仔细阅读用户提交的工单内容并将其准确分类到以下类别之一“ String.join(“”, SUPPORTED_CATEGORIES) “。只返回类别名称不要返回任何其他解释或文字。”); // 2. 构造用户消息即实际的工单内容 ChatMessage userMsg new ChatMessage(); userMsg.setRole(“user”); userMsg.setContent(“工单内容“ ticketContent); // 3. 调用AI服务 ListChatMessage messages Arrays.asList(systemMsg, userMsg); String aiResponse aiService.chatCompletionSync(messages); // 4. 后处理确保返回结果是支持的类别之一 String trimmedResponse aiResponse.trim(); if (SUPPORTED_CATEGORIES.contains(trimmedResponse)) { return trimmedResponse; } else { // 如果模型返回了不在列表中的内容可以记录日志并返回一个默认类别如“其他” return “其他”; } } }3.3 创建RESTful API控制器最后暴露一个HTTP API给前端或其他服务调用。import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.NotBlank; RestController RequestMapping(“/api/ticket”) public class TicketClassificationController { Autowired private TicketClassificationService classificationService; PostMapping(“/classify”) public ApiResponseString classify(RequestBody Valid TicketClassifyRequest request) { String category classificationService.classifyTicket(request.getContent()); return ApiResponse.success(category); } // 简单的请求体 Data public static class TicketClassifyRequest { NotBlank(message “工单内容不能为空”) private String content; } // 统一的API响应格式 Data public static class ApiResponseT { private int code; private String message; private T data; public static T ApiResponseT success(T data) { ApiResponseT response new ApiResponse(); response.setCode(200); response.setMessage(“success”); response.setData(data); return response; } } }4. 增强集成异常处理、文档与测试一个健壮的集成必须考虑异常情况、文档化和可测试性。4.1 全局异常处理为AI服务调用可能出现的网络超时、服务不可用、响应格式错误等情况定义统一的异常处理机制。import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import lombok.extern.slf4j.Slf4j; Slf4j RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(RuntimeException.class) // 这里可以替换为更具体的自定义异常 ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public TicketClassificationController.ApiResponse? handleAiServiceException(RuntimeException e) { log.error(“AI服务调用异常”, e); // 返回给客户端友好的错误信息避免泄露内部细节 TicketClassificationController.ApiResponseString response new TicketClassificationController.ApiResponse(); response.setCode(500); response.setMessage(“智能分类服务暂时不可用请稍后重试或联系管理员。”); response.setData(null); return response; } }4.2 集成Swagger API文档使用springdoc-openapi自动生成API文档让前端和测试同学一目了然。import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.*; Tag(name “工单管理”, description “工单相关操作包括智能分类”) RestController RequestMapping(“/api/ticket”) public class TicketClassificationController { Operation(summary “智能工单分类”, description “根据工单文本内容自动将其分类到预设的类别中”) PostMapping(“/classify”) public ApiResponseString classify(RequestBody Valid TicketClassifyRequest request) { // ... 实现代码同上 } }启动应用后访问http://localhost:8080/swagger-ui.html就能看到漂亮的交互式文档了。4.3 编写单元测试确保服务可靠性的关键一步。我们使用Mockito来模拟AI服务调用避免在单元测试中真的发起网络请求。import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; ExtendWith(MockitoExtension.class) class TicketClassificationServiceImplTest { Mock private SmallThinkerRestTemplateService mockAiService; InjectMocks private TicketClassificationServiceImpl classificationService; Test void classifyTicket_ShouldReturnCorrectCategory() { // 准备 String ticketContent “我的账号无法登录提示密码错误。”; String expectedCategory “账号问题”; when(mockAiService.chatCompletionSync(anyList())).thenReturn(expectedCategory); // 执行 String actualCategory classificationService.classifyTicket(ticketContent); // 验证 assertEquals(expectedCategory, actualCategory); } Test void classifyTicket_WhenAiReturnsUnsupportedCategory_ShouldReturnOther() { // 准备 String ticketContent “一些非常奇怪的内容”; when(mockAiService.chatCompletionSync(anyList())).thenReturn(“一个不存在的类别”); // 执行 String actualCategory classificationService.classifyTicket(ticketContent); // 验证应返回兜底的“其他”类别 assertEquals(“其他”, actualCategory); } }5. 总结与后续思考走完这一整套流程你会发现在SpringBoot项目里集成一个像SmallThinker-3B-Preview这样的AI模型服务本质上和集成任何一个外部RESTful服务没有太大区别。核心在于良好的抽象和封装把不稳定的、细节复杂的AI API调用包装成一个稳定的、业务语义清晰的内部服务。这次我们重点用了RestTemplate它简单直接适合大多数同步场景。如果你的系统并发量很高或者本身就是响应式架构那么WebClient会是更优解它能更好地利用资源。在实际项目中你可能还需要考虑更多东西比如重试机制当AI服务偶尔抖动时、熔断降级当AI服务完全不可用时是否有备用规则引擎、调用限流控制成本以及更完善的监控和日志。最重要的是通过“智能工单分类”这个具体案例我们把技术集成落到了真实的业务需求上。你可以举一反三把同样的模式应用到内容摘要、情感分析、智能审核等各种场景。AI能力正在变成一种新的“水电煤”而我们Java后端开发者的任务就是设计好管道和开关让业务方能够安全、稳定、便捷地使用它。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章