Spring Boot 集成國內AI,包含文心一言、通義千問和訊飛星火平臺實戰教程
- 一、項目結構
- 二、添加Maven依賴
- 三、配置API密鑰 (application.yml)
- 四、配置類
- 1. AI配置類 (AiProperties.java)
- 2. 啟用配置類 (AiConfig.java)
- 五、服務層實現
- 1. 文心一言服務 (WenxinService.java)
- 2. 通義千問服務 (QianwenService.java)
- 3. 訊飛星火服務 (XinghuoService.java)
- 六、統一控制器 (AiController.java)
- 七、安全增強配置
- 1. 添加API密鑰保護(自定義Filter)
- 2. 添加Rate Limiting(使用Resilience4j)
- 八、應用入口 (AiIntegrationApplication.java)
- 九、測試示例
- 十、最佳實踐建議
Spring Boot集成國內主流AI平臺的詳細實現方案,包含文心一言、通義千問和訊飛星火的對接代碼,助力快速構建智能應用。
一、項目結構
ai-integration-demo/
├── src/main/java
│ ├── com/example/ai
│ │ ├── config # 配置類
│ │ ├── controller # API控制器
│ │ ├── service # 服務層
│ │ │ ├── impl # 服務實現
│ │ ├── dto # 數據傳輸對象
├── resources
│ ├── application.yml # 配置文件
二、添加Maven依賴
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- HTTP客戶端 --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.11.0</version></dependency><!-- JSON處理 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><!-- 配置處理 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>
</dependencies>
三、配置API密鑰 (application.yml)
ai:wenxin:api-key: YOUR_WENXIN_API_KEYsecret-key: YOUR_WENXIN_SECRET_KEYapi-url: https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completionsqianwen:api-key: YOUR_QIANWEN_API_KEYapi-url: https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generationxinghuo:api-key: YOUR_XINGHUO_API_KEYsecret: YOUR_XINGHUO_SECRETappid: YOUR_XINGHUO_APPIDapi-url: https://spark-api.xf-yun.com/v3.5/chat
四、配置類
1. AI配置類 (AiProperties.java)
@ConfigurationProperties(prefix = "ai")
@Data
public class AiProperties {private Wenxin wenxin;private Qianwen qianwen;private Xinghuo xinghuo;@Datapublic static class Wenxin {private String apiKey;private String secretKey;private String apiUrl;}@Datapublic static class Qianwen {private String apiKey;private String apiUrl;}@Datapublic static class Xinghuo {private String apiKey;private String secret;private String appid;private String apiUrl;}
}
2. 啟用配置類 (AiConfig.java)
@Configuration
@EnableConfigurationProperties(AiProperties.class)
public class AiConfig {@Beanpublic OkHttpClient okHttpClient() {return new OkHttpClient();}
}
五、服務層實現
1. 文心一言服務 (WenxinService.java)
@Service
@RequiredArgsConstructor
public class WenxinService {private final AiProperties aiProperties;private final OkHttpClient okHttpClient;// 獲取AccessTokenprivate String getAccessToken() {String url = "https://aip.baidubce.com/oauth/2.0/token?"+ "grant_type=client_credentials"+ "&client_id=" + aiProperties.getWenxin().getApiKey()+ "&client_secret=" + aiProperties.getWenxin().getSecretKey();Request request = new Request.Builder().url(url).get().build();try (Response response = okHttpClient.newCall(request).execute()) {String responseBody = response.body().string();ObjectMapper objectMapper = new ObjectMapper();JsonNode rootNode = objectMapper.readTree(responseBody);return rootNode.get("access_token").asText();} catch (Exception e) {throw new RuntimeException("獲取文心一言Token失敗", e);}}public String chatCompletion(String prompt) {String accessToken = getAccessToken();String url = aiProperties.getWenxin().getApiUrl() + "?access_token=" + accessToken;JSONObject body = new JSONObject();body.put("messages", new JSONArray().put(new JSONObject().put("role", "user").put("content", prompt)));Request request = new Request.Builder().url(url).post(RequestBody.create(body.toString(), MediaType.get("application/json"))).build();try (Response response = okHttpClient.newCall(request).execute()) {if (!response.isSuccessful()) {throw new RuntimeException("文心一言API請求失敗: " + response);}String responseBody = response.body().string();JSONObject jsonResponse = new JSONObject(responseBody);return jsonResponse.getJSONObject("result").getString("content");} catch (Exception e) {throw new RuntimeException("調用文心一言API出錯", e);}}
}
2. 通義千問服務 (QianwenService.java)
@Service
@RequiredArgsConstructor
public class QianwenService {private final AiProperties aiProperties;private final OkHttpClient okHttpClient;public String generateText(String prompt) {JSONObject body = new JSONObject();body.put("model", "qwen-turbo");JSONObject input = new JSONObject();input.put("prompt", prompt);body.put("input", input);JSONObject parameters = new JSONObject();parameters.put("temperature", 0.85);parameters.put("top_p", 0.8);parameters.put("max_tokens", 1500);body.put("parameters", parameters);Request request = new Request.Builder().url(aiProperties.getQianwen().getApiUrl()).header("Authorization", "Bearer " + aiProperties.getQianwen().getApiKey()).post(RequestBody.create(body.toString(), MediaType.get("application/json"))).build();try (Response response = okHttpClient.newCall(request).execute()) {if (!response.isSuccessful()) {throw new RuntimeException("通義千問API請求失敗: " + response);}String responseBody = response.body().string();JSONObject jsonResponse = new JSONObject(responseBody);return jsonResponse.getJSONObject("output").getString("text");} catch (Exception e) {throw new RuntimeException("調用通義千問API出錯", e);}}
}
3. 訊飛星火服務 (XinghuoService.java)
@Service
@RequiredArgsConstructor
public class XinghuoService {private final AiProperties aiProperties;private final OkHttpClient okHttpClient;public String chat(String prompt) {try {// 構造鑒權URLString authUrl = generateAuthUrl();// 構造請求體JSONObject body = new JSONObject();JSONObject header = new JSONObject();header.put("app_id", aiProperties.getXinghuo().getAppid());JSONObject parameter = new JSONObject();JSONObject chat = new JSONObject();chat.put("domain", "generalv3.5");chat.put("temperature", 0.5);chat.put("max_tokens", 4096);parameter.put("chat", chat);JSONObject payload = new JSONObject();JSONObject message = new JSONObject();JSONArray text = new JSONArray();text.put(new JSONObject().put("role", "user").put("content", prompt));message.put("text", text);payload.put("message", message);body.put("header", header);body.put("parameter", parameter);body.put("payload", payload);// 發送請求Request request = new Request.Builder().url(authUrl).post(RequestBody.create(body.toString(), MediaType.get("application/json"))).build();try (Response response = okHttpClient.newCall(request).execute()) {if (!response.isSuccessful()) {throw new RuntimeException("訊飛星火API請求失敗: " + response);}String responseBody = response.body().string();JSONObject jsonResponse = new JSONObject(responseBody);return extractContent(jsonResponse);}} catch (Exception e) {throw new RuntimeException("調用訊飛星火API出錯", e);}}// 生成帶鑒權信息的URLprivate String generateAuthUrl() throws ParseException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {String apiUrl = aiProperties.getXinghuo().getApiUrl();String host = new URL(apiUrl).getHost();String path = new URL(apiUrl).getPath();// 創建日期對象SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);sdf.setTimeZone(TimeZone.getTimeZone("GMT"));String date = sdf.format(new Date());// 構造簽名String signatureOrigin = "host: " + host + "\n";signatureOrigin += "date: " + date + "\n";signatureOrigin += "POST " + path + " HTTP/1.1";Mac mac = Mac.getInstance("hmacsha256");SecretKeySpec secretKeySpec = new SecretKeySpec(aiProperties.getXinghuo().getSecret().getBytes("UTF-8"), "hmacsha256");mac.init(secretKeySpec);byte[] signatureSha = mac.doFinal(signatureOrigin.getBytes("UTF-8"));String signature = Base64.getEncoder().encodeToString(signatureSha);// 構造授權頭String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"",aiProperties.getXinghuo().getApiKey(), "hmac-sha256", "host date request-line", signature);return apiUrl + "?authorization=" + Base64.getEncoder().encodeToString(authorization.getBytes("UTF-8"))+ "&date=" + URLEncoder.encode(date, "UTF-8")+ "&host=" + URLEncoder.encode(host, "UTF-8");}// 提取響應內容private String extractContent(JSONObject response) {JSONObject payload = response.getJSONObject("payload");JSONObject message = payload.getJSONObject("message");JSONArray text = message.getJSONArray("text");StringBuilder result = new StringBuilder();for (int i = 0; i < text.length(); i++) {JSONObject textObj = text.getJSONObject(i);if (textObj.has("content")) {result.append(textObj.getString("content"));}}return result.toString();}
}
六、統一控制器 (AiController.java)
@RestController
@RequestMapping("/api/ai")
@RequiredArgsConstructor
public class AiController {private final WenxinService wenxinService;private final QianwenService qianwenService;private final XinghuoService xinghuoService;@PostMapping("/wenxin")public ResponseEntity<String> wenxinChat(@RequestBody @Valid AiRequest request) {return ResponseEntity.ok(wenxinService.chatCompletion(request.getPrompt()));}@PostMapping("/qianwen")public ResponseEntity<String> qianwenGenerate(@RequestBody @Valid AiRequest request) {return ResponseEntity.ok(qianwenService.generateText(request.getPrompt()));}@PostMapping("/xinghuo")public ResponseEntity<String> xinghuoChat(@RequestBody @Valid AiRequest request) {return ResponseEntity.ok(xinghuoService.chat(request.getPrompt()));}@Datastatic class AiRequest {@NotBlank(message = "提示語不能為空")private String prompt;}
}
七、安全增強配置
1. 添加API密鑰保護(自定義Filter)
@Component
@RequiredArgsConstructor
public class ApiKeyFilter extends OncePerRequestFilter {private final AiProperties aiProperties;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String clientId = request.getHeader("X-API-CLIENT-ID");String clientSecret = request.getHeader("X-API-CLIENT-SECRET");// 驗證客戶端憑證if (!isValidCredentials(clientId, clientSecret)) {response.sendError(HttpStatus.UNAUTHORIZED.value(), "無效的API憑證");return;}filterChain.doFilter(request, response);}private boolean isValidCredentials(String clientId, String clientSecret) {// 這里應該是從數據庫或配置中讀取驗證信息// 簡化示例:使用配置中的文心API密鑰做演示return clientId != null && clientSecret != null && clientId.equals("demo-app") && clientSecret.equals(aiProperties.getWenxin().getApiKey());}
}
2. 添加Rate Limiting(使用Resilience4j)
@Configuration
public class RateLimiterConfig {@Beanpublic RateLimiter wenxinRateLimiter() {return RateLimiter.of("wenxin-limiter", RateLimiterConfig.custom().limitRefreshPeriod(Duration.ofSeconds(10)).limitForPeriod(5).timeoutDuration(Duration.ofSeconds(5)).build());}
}// 在控制器中使用
@RestController
@RequestMapping("/api/ai")
public class AiController {private final RateLimiter wenxinRateLimiter;@PostMapping("/wenxin")@RateLimiter(name = "wenxin-limiter")public ResponseEntity<String> wenxinChat(@RequestBody AiRequest request) {// ...}
}
八、應用入口 (AiIntegrationApplication.java)
@SpringBootApplication
public class AiIntegrationApplication {public static void main(String[] args) {SpringApplication.run(AiIntegrationApplication.class, args);}
}
九、測試示例
使用cURL測試:
# 通義千問測試
curl -X POST http://localhost:8080/api/ai/qianwen \-H "Content-Type: application/json" \-d '{"prompt": "用100字介紹Spring Boot"}'# 文心一言測試
curl -X POST http://localhost:8080/api/ai/wenxin \-H "Content-Type: application/json" \-d '{"prompt": "用Java寫一個快速排序算法"}'# 訊飛星火測試
curl -X POST http://localhost:8080/api/ai/xinghuo \-H "Content-Type: application/json" \-d '{"prompt": "如何做好電商運營"}'
十、最佳實踐建議
- 異步處理:使用@Async注解異步調用AI接口,避免阻塞
- 緩存結果:對常見問題的結果進行緩存,減少API調用
- 錯誤重試:實現指數退避重試機制處理臨時錯誤
- 流量控制:針對不同AI平臺設置不同的QPS限制
- 統一接口:創建統一的AI門面服務,提供平臺無關的調用
@Service
public class AiFacadeService {private enum AiProvider { WENXIN, QIANWEN, XINGHUO }private final WenxinService wenxinService;private final QianwenService qianwenService;private final XinghuoService xinghuoService;public String unifiedChat(String prompt) {// 簡單輪詢策略AiProvider[] providers = AiProvider.values();AiProvider provider = providers[(int)(System.currentTimeMillis() % providers.length)];switch (provider) {case WENXIN: return wenxinService.chatCompletion(prompt);case QIANWEN: return qianwenService.generateText(prompt);case XINGHUO: return xinghuoService.chat(prompt);default: throw new IllegalStateException("未知的AI提供商");}}
}
本項目提供了完整的企業級Spring Boot集成國內主流AI平臺的實現方案,可根據實際需求進行擴展和優化。