概要
Spring AIにおけるリクエスト送信とレスポンス処理のメカニズムを解説します。Spring Boot 3環境での実装を前提とし、JDK 17が必須となります。
プロジェクト設定
Mavenプロジェクトにおける依存関係の設定例を示します。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M2</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
基本的な利用方法
ブロッキング方式でのAI応答取得の実装例です。
@PostMapping("/query")
public ResponseData generateResponse(@RequestParam("input") String userInput) {
String result = chatClient.prompt()
.user(userInput)
.call()
.content();
return ResponseData.builder()
.type("text")
.content(ContentData.builder().text(result).build())
.build();
}
内部処理の流れ
リクエスト構築からAIサービス呼び出しまでの主要な処理ステップを説明します。
1. リクエスト構築処理
private ChatResponse processChatRequest(RequestSpecification requestSpec, String formatOption) {
Map<String, Object> contextData = new ConcurrentHashMap<>();
contextData.putAll(requestSpec.getAdvisorParameters());
RequestSpecification processedRequest = RequestSpecification.processRequest(requestSpec, contextData);
String processedText = StringUtils.hasText(formatOption)
? processedRequest.getUserText() + System.lineSeparator() + "{format_spec}"
: processedRequest.getUserText();
Map<String, Object> userParameters = new HashMap<>(processedRequest.getUserParams());
if (StringUtils.hasText(formatOption)) {
userParameters.put("format_spec", formatOption);
}
List<Message> messageList = new ArrayList<>(processedRequest.getMessages());
boolean hasValidText = StringUtils.hasText(processedText)
|| StringUtils.hasText(processedRequest.getSystemText());
if (hasValidText) {
if (StringUtils.hasText(processedRequest.getSystemText())
|| !processedRequest.getSystemParams().isEmpty()) {
SystemMessage systemMsg = new SystemMessage(
new PromptTemplate(processedRequest.getSystemText(),
processedRequest.getSystemParams()).render());
messageList.add(systemMsg);
}
UserMessage userMsg = !userParameters.isEmpty()
? new UserMessage(new PromptTemplate(processedText, userParameters).render(),
processedRequest.getMedia())
: new UserMessage(processedText, processedRequest.getMedia());
messageList.add(userMsg);
}
Prompt prompt = new Prompt(messageList, processedRequest.getChatOptions());
return this.chatModel.call(prompt);
}
2. AIサービス呼び出し
public ChatResponse executePrompt(Prompt prompt) {
CompletionRequest apiRequest = createApiRequest(prompt, false);
ObservationContext observationCtx = ObservationContext.builder()
.prompt(prompt)
.provider("openai")
.requestOptions(buildRequestOptions(apiRequest))
.build();
return ObservationDocumentation.CHAT_OPERATION
.observation(this.observationConfig, DEFAULT_OBSERVATION_CONFIG,
() -> observationCtx, this.observationRegistry)
.observe(() -> {
ResponseEntity<CompletionResponse> responseEntity = this.retryTemplate
.execute(ctx -> this.apiClient.completion(apiRequest,
getAdditionalHeaders(prompt)));
CompletionResponse completion = responseEntity.getBody();
if (completion == null) {
return new ChatResponse(List.of());
}
List<Generation> generations = completion.choices().stream()
.map(choice -> {
Map<String, Object> metadata = Map.of(
"id", completion.id() != null ? completion.id() : "",
"role", choice.message().role() != null
? choice.message().role().name() : "",
"index", choice.index(),
"finishReason", choice.finishReason() != null
? choice.finishReason().name() : ""
);
return createGeneration(choice, metadata);
}).toList();
return new ChatResponse(generations,
buildResponseMetadata(completion, responseEntity));
});
}
3. リトライメカニズム
Spring AIでは組み込みのリトライ機能を提供しており、spring.ai.retryプレフィックスで設定可能です。
@Configuration
@ConditionalOnClass(RetryTemplate.class)
@EnableConfigurationProperties(RetryProperties.class)
public class RetryAutoConfig {
// リトライ設定の自動構成
}