업그레이드 노트

업그레이드 노트 (Upgrade Notes)

Spring AI를 새 버전으로 올릴 때 어떤 코드가 깨질 수 있는지, 그리고 어떻게 수정하면 되는지를 정리해 둔 문서예요. 2.0.1과 2.0.0을 중심으로 호환성 변경(breaking change)들이 아주 많아서, 이 문서를 따라가며 하나씩 고쳐 나가면 부담 없이 마이그레이션할 수 있을 거예요. 코드와 클래스 이름, 메서드 시그니처는 원문 그대로 보존했으니 참고해서 옮기면 돼요.

출처: 문서

본문

2.0.1로 업그레이드하기 (Upgrading to 2.0.1)

Redis 채팅 메모리 저장소 자동구성 모듈 이름 변경

Redis 채팅 메모리 자동구성 모듈의 이름이 spring-ai-autoconfigure-model-chat-memory-repository-redis로 바뀌었어요.

영향 (Impact)

Redis 채팅 메모리 자동구성 모듈의 아티팩트 ID와 구성 프로퍼티가 변경됐어요:

  • 아티팩트: spring-ai-autoconfigure-model-chat-memory-redis → spring-ai-autoconfigure-model-chat-memory-repository-redis
  • 구성 프로퍼티: spring.ai.chat.memory.redis. → spring.ai.chat.memory.repository.redis.

마이그레이션 방법 (Migration)

  • Maven/Gradle 의존성을 새 아티팩트 ID로 업데이트해요:
<!-- Before -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-model-chat-memory-redis</artifactId>
</dependency>

<!-- After -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-model-chat-memory-repository-redis</artifactId>
</dependency>
  • 구성 프로퍼티의 접두사를 새 것으로 업데이트해요:
# Before
spring.ai.chat.memory.redis.*=xxx

# After
spring.ai.chat.memory.repository.redis.*=xxx

부드러운 전환 (Smooth Transition)

새 모듈은 deprecated 모듈에서 부드럽게 전환할 수 있게 해줘요:

  • deprecated spring-ai-autoconfigure-model-chat-memory-redis 모듈은 여전히 존재하고, 전이 의존성(transitive dependency)으로 새 모듈을 의존하므로 기존 의존성은 변경 없이 계속 동작해요.
  • deprecated RedisChatMemoryProperties(spring.ai.chat.memory.redis)는 모든 프로퍼티를 현재의 RedisChatMemoryRepositoryProperties(spring.ai.chat.memory.repository.redis)로 위임해요. 이건 이 페이지 아래쪽에서 설명하는 .options deprecation과 동일한 위임 메커니즘이라, 기존 프로퍼티 설정도 그대로 동작해요.

참고 deprecated 모듈과 레거시 구성 접두사 모두 향후 릴리스에서 제거될 예정이에요.

Mistral AI Moderation 카테고리 재구성

Mistral AI moderation 모델이 mistral-moderation-2411(2026년 6월 30일 폐기)에서 mistral-moderation-2603으로 마이그레이션됐어요. 이 마이그레이션의 일환으로, 단일 카테고리였던 dangerousAndCriminalContent가 dangerous와 criminal이라는 두 개의 별도 카테고리로 분리됐어요. 새로운 jailbreaking 카테고리도 추가됐어요.

영향 (Impact)

Categories와 CategoryScores의 다음 메서드들은 deprecate 되었고 향후 릴리스에서 제거될 예정이에요:

  • Categories.isDangerousAndCriminalContent()
  • Categories.Builder.dangerousAndCriminalContent(boolean)
  • CategoryScores.getDangerousAndCriminalContent()
  • CategoryScores.Builder.dangerousAndCriminalContent(double)

마이그레이션 방법 (Migration)

deprecated dangerousAndCriminalContent 메서드들을 새 criminal과 dangerous에 해당하는 메서드로 교체해요:

// Before
boolean flagged = categories.isDangerousAndCriminalContent();
double score = categoryScores.getDangerousAndCriminalContent();

Categories categories = Categories.builder()
    .dangerousAndCriminalContent(true)
    .build();

// After
boolean flagged = categories.isCriminal() || categories.isDangerous();
double score = categoryScores.getCriminal() + categoryScores.getDangerous();

Categories categories = Categories.builder()
    .criminal(true)
    .dangerous(true)
    .build();

이전에 해당하는 게 없던 새 jailbreaking 카테고리도 확인할 수 있어요:

boolean jailbreakAttempt = categories.isJailbreaking();
double jailbreakScore = categoryScores.getJailbreaking();

Mistral AI 채팅 모델 열거형 업데이트

MistralAiApi.ChatModel이 Mistral AI의 현재 모델 라인업을 추적하도록 업데이트됐어요. 여러 상수가 Mistral AI가 폐기한 모델을 가리키고 있었거든요.

영향 (Impact)

  • ChatModel.MAGISTRAL_MEDIUM, ChatModel.MAGISTRAL_SMALL, ChatModel.DEVSTRAL, ChatModel.OPEN_MISTRAL_NEMO 상수가 제거됐어요. 이들이 가리키던 모델(magistral-medium-latest, magistral-small-latest, devstral-latest, open-mistral-nemo)은 모두 폐기 시점을 지났기 때문에, 이 상수를 참조하는 코드는 더 이상 컴파일되지 않아요.
  • ChatModel.MISTRAL_SMALL은 이제 mistral-small-2603을 가리켜요(이전에는 mistral-small-latest).
  • ChatModel.MISTRAL_MEDIUM은 이제 mistral-medium-3-5를 가리켜요(이전에는 mistral-medium-latest).
  • ChatModel.MISTRAL_LARGE도 이제 mistral-medium-3-5를 가리켜요. Mistral Large는 폐기됐고, Mistral AI가 Mistral Medium 3.5를 대체 모델로 권장해요. MISTRAL_LARGE는 하위 호환성을 위해 별칭으로만 유지돼요.

마이그레이션 방법 (Migration)

제거된 상수에 대한 참조를 지원되는 모델로 교체해요:

// Before
String model = MistralAiApi.ChatModel.MAGISTRAL_MEDIUM.getValue();

// After
String model = MistralAiApi.ChatModel.MISTRAL_MEDIUM.getValue();

Magistral의 추론("thinking") 출력에 의존했다면, MistralAiApi.ChatCompletionRequest.ReasoningEffort를 MISTRAL_SMALL 또는 MISTRAL_MEDIUM과 함께 사용해요.

MCP 도구 예외 처리

변경됨: @McpTool 예외 처리가 @Tool을 따르게 됨

@McpTool 어노테이션이 붙은 서버 메서드의 예외 처리 계약이 @Tool과 일치하도록 정렬됐어요. 이전에는 @McpTool 메서드가 던지는 모든 예외(선언된 checked exception, Error 하위 타입, 프로토콜 수준 McpError 포함)가 잡혀서 조용히 error CallToolResult로 변환되어 모델에 전달됐어요. 2.0.1부터 예외 전달(dispatch)은 @Tool이 사용하는 것과 동일한 타입 기반 규칙을 따릅니다:

예외 타입 동작
RuntimeException(McpError 아님) error CallToolResult로 변환되어 모델에 전달됨
선언된 checked exception UndeclaredThrowableException으로 전파됨 — 하드 실패, 절대 모델에 도달하지 않음
Error 하위 타입 변경 없이 전파됨 — 하드 실패
McpError 변경 없이 전파됨 — 하드 실패(프로토콜 수준 신호, 예: URL elicitation)
영향 (Impact)

문제를 알리기 위해 의도적으로 checked exception이나 Error를 던져서, 그 예외가 오류 메시지로 모델에 전달되길 기대했던 @McpTool 메서드는 이제 예외를 전파하게 돼요.

마이그레이션 방법 (Migration)

@McpTool 메서드가 checked exception이나 Error를 던지는데 모델이 오류 메시지를 보길 원한다면, 메서드 본문에서 던지기 전에 RuntimeException(또는 하위 클래스)으로 감싸요:

// Before — checked exception이 조용히 error result로 변환됨
@McpTool(description = "Look up an order")
public String lookupOrder(String orderId) throws NotFoundException {
    throw new NotFoundException("Order not found: " + orderId);
}

// After — RuntimeException으로 감싸서 오류를 모델에 전달하거나,
//          checked exception 선언을 제거하고 전파되게 함
@McpTool(description = "Look up an order")
public String lookupOrder(String orderId) {
    throw new RuntimeException("Order not found: " + orderId); // error result로 모델에 도달
}

checked exception이 하드 실패가 되길 원한다면 변경이 필요 없어요 — 기본적으로 UndeclaredThrowableException으로 전파되거든요.

Deprecated: toolCallExceptionClass 생성자 파라미터

다음 클래스들의 toolCallExceptionClass 파라미터는 deprecate 되었고 2.1.0에서 제거될 예정이에요. 런타임에 무시돼요 — 예외 전달은 이제 클래스가 아니라 타입 기반이에요.

  • SyncMcpToolMethodCallback(ReturnMode, Method, Object, Class<? extends Throwable>)
  • AsyncMcpToolMethodCallback(ReturnMode, Method, Object, Class<? extends Throwable>)
  • SyncStatelessMcpToolMethodCallback(ReturnMode, Method, Object, Class<? extends Throwable>)
  • AsyncStatelessMcpToolMethodCallback(ReturnMode, Method, Object, Class<? extends Throwable>)
  • AbstractMcpToolProvider.doGetToolCallException()
마이그레이션 방법 (Migration)

3-인자 생성자를 사용해요. toolCallExceptionClass 인자는 받아들여지지만 더 이상 아무 효과가 없어요:

// Before
new SyncMcpToolMethodCallback(ReturnMode.TEXT, method, provider, Exception.class);

// After
new SyncMcpToolMethodCallback(ReturnMode.TEXT, method, provider);

도구 호출 (Tool Calling)

새로 추가됨: 도구 호출 한도

DefaultToolCallingManager가 이제 턴당 도구 호출 수를 제한해요 — 툴당 DefaultToolCallingManager.DEFAULT_MAX_CALLS_PER_TOOL(40), 전체 DEFAULT_MAX_TOTAL_TOOL_CALLS(150). 이전에는 제한이 전혀 없었어요. 대부분의 애플리케이션은 눈치채지 못할 거예요. 이런 기본값이 넉넉하니까요. 정당한 워크로드가 이를 초과한다면 ToolCallingManager.builder().maxCallsPerTool(…) / .unlimitedCallsPerTool()(또는 spring.ai.tools.limits.* 프로퍼티)로 한도를 올리거나 해제할 수 있어요. 자세한 내용은 도구 호출 한도를 참고해요.

변경됨: ToolCallingAdvisor 사용량이 이제 루프 전체에 누적됨

ToolCallingAdvisor는 이제 도구 호출 루프에서 발생한 모든 내부 모델 호출에 걸친 토큰 사용량을 합산해요. 예전에는 마지막 호출의 Usage만 최종 ChatClientResponse에 노출했어요. 도구 호출 교환 후 정확한 Usage/토큰 수를 검증하는 코드나 테스트는 이제 더 높고 누적된 값을 보게 될 거예요. API 변경은 필요 없어요.

OpenAI

변경됨: 도구 호출 strict 모드가 기본 false가 됨

OpenAiChatModel이 더 이상 도구 스키마를 strict(true)로 기본 설정하지 않아요. JsonSchemaGenerator는 OpenAI strict 모드가 요구하는 nullable 타입 패턴 대신 required에서 선택 파라미터를 생략해요. 그래서 선택 파라미터가 있는 모든 도구는 예전 기본값에서 OpenAI에 의해 400 오류로 거부됐어요.

영향 (Impact)

암시적 strict(true) 기본값에 의존하던 도구는 명시적으로 요청하지 않는 한 non-strict로 전송돼요. 이건 OpenAI 자체의 스키마 준수 검사에만 영향을 미쳐요. 도구 실행과 결과는 변함없어요.

마이그레이션 방법 (Migration)

OpenAiChatOptions#strict(true)로 다시 선택할 수 있어요. 활성화하면 선택 파라미터가 자동으로 nullable 타입으로 넓혀지고 required에 다시 채워져서 스키마가 OpenAI strict 모드 계약을 여전히 만족해요:

OpenAiChatOptions options = OpenAiChatOptions.builder()
    .strict(true)
    .build();

변경됨: OpenAiAudioSpeechModel 스트리밍이 이제 실제 오디오 청크를 방출함

OpenAiAudioSpeechModel.stream(TextToSpeechPrompt)는 이전에 전체 응답을 버퍼링해서 단일 Flux 요소로 방출했어요. 이제 OpenAI에 stream_format=audio를 요청하고 청크가 도착하는 대로 방출해요.

영향 (Impact)

Flux의 첫 번째 요소만 소비하던(예: blockFirst() 사용) 코드는, 전체 오디오를 담고 있다고 가정했다면, 이제 첫 번째 청크만 얻게 돼요.

마이그레이션 방법 (Migration)

첫 번째 요소를 취하는 대신 방출된 모든 청크를 연결해요. 전체 예시는 스트리밍 응답 연결하기를 참고해요.

TranscriptionModel이 이제 StreamingTranscriptionModel을 확장함

TranscriptionModel이 이제 StreamingTranscriptionModel을 확장해요. 그래서 스트리밍 전사가 모든 전사 모델의 일급 기능이 돼요. StreamingTranscriptionModel은 Flux.error(new UnsupportedOperationException(…))을 반환하는 기본 stream(AudioTranscriptionPrompt) 구현을 제공하므로, 기존의 커스텀 TranscriptionModel 구현은 변경 없이 계속 컴파일돼요.

영향 (Impact)

오버라이드하지 않는 커스텀 구현에서 stream()을 호출하면 UnsupportedOperationException을 방출하는 Flux를 반환하게 돼요.

마이그레이션 방법 (Migration)

커스텀 TranscriptionModel이 스트리밍 전사를 지원해야 한다면 stream(AudioTranscriptionPrompt) 메서드를 오버라이드해요. 네이티브 스트리밍 구현을 제공하거나, 동기 call() 결과를 단일 요소 Flux로 감쌀 수 있어요:

public class CustomTranscriptionModel implements TranscriptionModel {

    @Override
    public AudioTranscriptionResponse call(AudioTranscriptionPrompt prompt) {
        // ...
    }

    @Override
    public Flux<AudioTranscriptionResponse> stream(AudioTranscriptionPrompt prompt) {
        // 동기 call 결과를 단일 요소 Flux로 감싸기
        return Flux.just(call(prompt));
    }
}

스트리밍이 필요 없다면 별도 조치가 필요 없어요 — 기본 구현이 자동으로 상속되니까요.

Media 빌더: 타입화된 data() 오버로드가 data(Object)를 대체함

Media.Builder.data(Object)가 타입화된 오버로드로 대체됐어요: data(byte[]), data(String), data(URI), data(URL), data(Resource).

영향 (Impact)

정적 타입이 Object인 인자로 .data(…)를 호출하는 코드는 더 이상 컴파일되지 않아요 — 일치하는 오버로드가 없거든요.

마이그레이션 방법 (Migration)

.data(…)를 호출하기 전에 구체 타입으로 좁혀요:

// Before
Object data = getMediaData();
Media.builder().mimeType(mimeType).data(data).build();

// After
Object data = getMediaData();
if (data instanceof byte[] bytes) {
    Media.builder().mimeType(mimeType).data(bytes).build();
}
else if (data instanceof String s) {
    Media.builder().mimeType(mimeType).data(s).build();
}

도구 해석 폴백(fallback)이 기본적으로 비활성화됨

빈(bean)으로 등록됐지만 요청에 연결되지 않은 도구를 해석하고 실행하는 폴백이 기본적으로 비활성화됐어요. 이전에는 모델이 요청에 연결되지 않은 도구에 대한 도구 호출을 반환하면, DefaultToolCallingManager가 애플리케이션 전체의 ToolCallbackResolver(애플리케이션 컨텍스트의 모든 ToolCallback 빈을 기반으로 함)로 폴백해서 해석된 도구를 실행했어요. 2.0.1부터는 .tools(…), .defaultTools(…), 또는 ToolCallingChatOptions.toolCallbacks(…)로 요청에 연결된 도구만 그 요청에서 실행될 수 있어요. 이전 동작을 복원하려면 해석 폴백을 다시 활성화해요:

spring.ai.tools.resolution.fallback.enabled=true

ToolCallingManager를 직접 구성할 때는 빌더 스위치를 사용해요:

ToolCallingManager toolCallingManager = ToolCallingManager.builder()
    .toolCallbackResolver(toolCallbackResolver)
    .resolutionFallbackEnabled(true) // default is false
    .build();

자세한 내용은 도구 해석을 참고해요.

2.0.0으로 업그레이드하기 (Upgrading to 2.0.0)

이 섹션은 Spring AI 1.1.x에서 2.0.0으로 업그레이드할 때의 모든 호환성 변경과 마이그레이션 단계를 다룹니다.

어드바이저 (Advisors)

spring-ai-advisors-vector-store가 spring-ai-vector-store-advisor로 이름 변경됨

spring-ai-advisors-vector-store 모듈이 다른 Spring AI 모듈의 명명 규칙과 더 잘 맞도록 spring-ai-vector-store-advisor로 이름이 바뀌었어요.

새로 추가됨: ToolSearchToolCallingAdvisor 자동구성과 스타터

Spring Boot 자동구성(spring-ai-autoconfigure-tool-search-advisor)과 그에 맞는 스타터(spring-ai-starter-tool-search-advisor)가 이제 제공돼요. 활성화하면 ToolSearchToolCallingAdvisor가 자동구성된 ChatClient에서 기본 ToolCallingAdvisor를 대체해서, LLM에 보내는 도구 정의를 호출당 가장 관련 있는 것들로만 제한해요(키워드 또는 시맨틱 검색으로). 스타터를 추가하고 프로퍼티를 설정해서 선택해요:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-tool-search-advisor</artifactId>
</dependency>
spring.ai.chat.client.tool-search-advisor.enabled=true

# 인덱스 타입 선택: regex (default), lucene, or vector
spring.ai.chat.client.tool-search-advisor.tool-index-type=regex

regex 인덱스는 추가 의존성이 필요 없어요. lucene 인덱스는 클래스패스에 org.apache.lucene:lucene-core가 필요해요. vector 인덱스는 VectorStore 빈(클래스패스의 spring-ai-vector-store)이 필요해요.

변경됨: Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER 기본값

Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER가 Ordered.HIGHEST_PRECEDENCE + 1000에서 Ordered.HIGHEST_PRECEDENCE + 200으로 바뀌어서, 메모리 어드바이저가 기본 순서로 ToolCallingAdvisor(HIGHEST_PRECEDENCE + 300)의 _바깥_에 배치돼요. ToolCallingAdvisor는 이제 기본적으로 도구 호출 반복 사이의 대화 기록을 내부에서 관리해요. 메모리 어드바이저는 최종 사용자/어시스턴트 교환만 저장하고, 도구 호출 메시지는 ChatMemoryRepository에 절대 쓰지 않아요. 대부분의 저장소 구현이 그런 메시지 타입을 지원하지 않기 때문에 이게 올바른 기본값이에요. 루프 안 에 메모리가 필요하다면(예: InMemoryChatMemoryRepository 사용 시), 어드바이저 순서를 ToolCallingAdvisor.DEFAULT_ORDER보다 위로 명시적으로 설정하고 .disableInternalConversationHistory()를 호출해요:

var toolCallingAdvisor = ToolCallingAdvisor.builder()
    .disableInternalConversationHistory()
    .build();
var chatMemoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory)
    .advisorOrder(Ordered.HIGHEST_PRECEDENCE + 400)
    .build();

참고 spring-ai-session 커뮤니티 프로젝트가 도구 호출 메시지를 완전히 지원하는 세션 인식 메모리 구현을 제공하며, 어떤 백엔드에서든 도구 호출 루프 안에서 안전하게 사용할 수 있어요. Spring AI 2.1에서 ChatMemory를 대체할 계획이에요. 자세한 내용은 spring-ai-session 문서를 참고해요.

도구 호출 (Tool Calling)

제거됨: ToolCallingChatOptions의 internalToolExecutionEnabled

internalToolExecutionEnabled 옵션이 ToolCallingChatOptions와 모든 프로바이더별 ChatOptions 클래스(예: OpenAiChatOptions, AnthropicChatOptions)에서 제거됐어요. 그에 대응하는 Spring Boot 구성 프로퍼티 spring.ai.<provider>.chat.internal-tool-execution-enabled도 제거됐어요.

영향 (Impact)

사용자 제어 도구 실행을 선택하기 위해 .internalToolExecutionEnabled(false)를 설정하는 코드는 더 이상 컴파일되지 않아요. .internalToolExecutionEnabled(true)(내부 루프를 명시적으로 활성화)를 설정하는 코드도 더 이상 컴파일되지 않으며, 그것이 의미했던 동작도 더 이상 존재하지 않아요 — 모든 ChatModel 구현에서 모델별 내부 도구 실행이 제거됐거든요.

마이그레이션 방법 (Migration)

.internalToolExecutionEnabled(…) 호출을 모두 제거해요. 권장하는 방법은:

  • ChatClient를 통한 ToolCallingAdvisor(권장) — 도구가 있으면 자동 등록됨. 플래그 불필요.
  • ChatModel을 직접 쓰는 사용자 제어 루프 — ToolCallingAdvisor 없이 ChatModel만 호출. 도구 호출이 자동 실행되지 않으니 chatResponse.hasToolCalls()를 직접 확인하고 ToolCallingManager로 루프를 돌려요.
// Before
ChatOptions options = ToolCallingChatOptions.builder()
    .toolCallbacks(ToolCallbacks.from(new MyTools()))
    .internalToolExecutionEnabled(false)   // <-- remove this line
    .build();

// After
ChatOptions options = ToolCallingChatOptions.builder()
    .toolCallbacks(ToolCallbacks.from(new MyTools()))
    .build();

제거됨: ToolExecutionEligibilityPredicate와 DefaultToolExecutionEligibilityPredicate

ToolExecutionEligibilityPredicate(2.0.0부터 deprecated)와 그 기본 구현인 DefaultToolExecutionEligibilityPredicate가 제거됐어요. 이 인터페이스들은 옵션 기반 정책 검사(internalToolExecutionEnabled)와 응답 검사(hasToolCalls())를 결합했어요. 옵션 기반 플래그가 사라졌으므로 ChatModel 수준에는 대체물이 없어요.

마이그레이션 방법 (Migration)

도구 실행 시점을 커스터마이즈하기 위해 ToolExecutionEligibilityPredicate를 구현했다면, ToolExecutionEligibilityChecker(아래 참조)로 마이그레이션하고 ToolCallingAdvisor에 공급해요.

새로 추가됨: ToolCallingAdvisor의 ToolExecutionEligibilityChecker

ToolExecutionEligibilityChecker(Function<ChatResponse, Boolean>)를 ToolCallingAdvisor.Builder에 설정해서 도구 호출 루프가 언제 반복될지 커스터마이즈할 수 있어요. 기본값은 chatResponse → chatResponse != null && chatResponse.hasToolCalls()이에요. 이것이 프로바이더별 중지 이유(stop-reason) 로직을 위한 확장 지점이에요(도구 호출 존재 외에 finish reason도 확인하는 등):

ToolCallingAdvisor advisor = ToolCallingAdvisor.builder()
    .toolExecutionEligibilityChecker(response ->
        response != null && response.hasToolCalls()
            && !"stop".equals(response.getResult().getMetadata().getFinishReason()))
    .build();

Spring Boot 사용자는 ToolExecutionEligibilityChecker 빈을 제공할 수 있어요. 자동구성된 ToolCallingAdvisor.Builder가 자동으로 가져다 써요:

@Bean
ToolExecutionEligibilityChecker myChecker() {
    return response -> response != null && response.hasToolCalls();
}

새로 추가됨: spring.ai.chat.client.tool-calling.enabled 프로퍼티

새로운 spring.ai.chat.client.tool-calling.enabled 프로퍼티(기본값 true)가 자동구성된 ChatClient가 ToolCallingAdvisor를 자동 등록할지 제어해요. false로 설정하면 모든 호출에 대해 자동 도구 실행을 비활성화해요 — 도구는 여전히 정의로 AI 모델에 전송되지만, 도구 호출 응답은 자동으로 실행되지 않아요.

spring.ai.chat.client.tool-calling.enabled=false

이것은 모든 개별 호출에 AdvisorParams.toolCallingAdvisorAutoRegister(false)를 쓰는 것의 전역 대안이에요.

제거됨: 어드바이저 빌더와 자동구성의 streamToolCallResponses

streamToolCallResponses 옵션이 ToolCallingAdvisor.Builder, ToolCallAdvisor.Builder, ToolSearchToolCallingAdvisor.Builder에서, 그리고 그에 대응하는 Spring Boot 자동구성 프로퍼티에서 제거됐어요:

  • spring.ai.chat.client.tool-calling.stream-tool-call-responses
  • spring.ai.chat.client.tool-search-advisor.stream-tool-call-responses
이유 (Why)

streamToolCallResponses=true일 때 중간 도구 호출 요청 청크는 다운스트림으로 스트리밍됐지만, 그에 짝을 이루는 ToolResponseMessage(LLM으로 다시 전송됨)는 스트리밍되지 않았어요. 그 청크들을 기록하던 다운스트림 메모리 어드바이저는 대응하는 도구 응답 없이 도구 호출 요청을 받게 되어, 손상된 대화 기록이 생성됐어요. 이 설계 결함은 ChatClientResponse에 호환성 변경을 도입하지 않고는 고칠 수 없어서, 옵션이 완전히 제거됐어요.

영향 (Impact)
  • 어드바이저 빌더에서 .streamToolCallResponses(true) 또는 .streamToolCallResponses(false)를 호출하는 코드는 컴파일되지 않아요.
  • 제거된 프로퍼티에 대한 application.properties 또는 application.yml 항목은 조용히 무시돼요.
마이그레이션 방법 (Migration)

빌더 체인에서 .streamToolCallResponses(…) 호출을 모두 제거해요:

// Before
var advisor = ToolCallingAdvisor.builder()
    .streamToolCallResponses(true)   // <-- remove this line
    .build();

// After
var advisor = ToolCallingAdvisor.builder().build();

도구 호출 루프의 각 반복에 대한 가시성(즉 streamToolCallResponses=true가 해결하려던 사용 사례)을 얻으려면, 자동 등록 어드바이저를 선택 해제하고 AdvisorParams.toolCallingAdvisorAutoRegister(false)로 루프를 수동으로 돌려요:

ToolCallingManager toolCallingManager = ToolCallingManager.builder().build();
ToolCallback[] tools = ToolCallbacks.from(new WeatherTools());
ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(tools)
    .build();

String question = "What is the weather in Amsterdam and Paris?";
Prompt prompt = new Prompt(List.of(new UserMessage(question)), chatOptions);

ChatClientResponse response = chatClient.prompt()
    .user(question)
    .options(chatOptions)
    .advisors(AdvisorParams.toolCallingAdvisorAutoRegister(false))
    .call()
    .chatClientResponse();

while (response.chatResponse() != null && response.chatResponse().hasToolCalls()) {
    // 실행 전에 도구 호출 청크를 여기서 검사하거나 전달
    ToolExecutionResult result = toolCallingManager.executeToolCalls(prompt, response.chatResponse());
    prompt = new Prompt(result.conversationHistory(), chatOptions);
    response = chatClient.prompt()
        .messages(result.conversationHistory())
        .options(chatOptions)
        .advisors(AdvisorParams.toolCallingAdvisorAutoRegister(false))
        .call()
        .chatClientResponse();
}
// 최종 답변을 여기서 전달하거나 처리

스트리밍 경로를 포함한 완전한 예시는 사용자 제어 도구 실행 — ChatClient와 함께를 참고해요.

제거됨: ChatModel의 내부 도구 실행 루프

Spring AI 1.x에서 모든 ChatModel 구현은 자체 내부 도구 실행 루프를 갖고 있었어요. chatModel.call(prompt)를 도구와 함께 호출하면 응답의 도구 호출을 실행하고 모델을 자동으로 다시 호출해서, 모델이 도구 호출이 아닌 응답을 생성할 때까지 루프를 돌았어요. 2.0에서는 이 내부 루프가 모든 ChatModel 구현(OpenAI, Anthropic, Ollama, DeepSeek, Bedrock, MiniMax, Mistral, Google GenAI)에서 제거됐어요. ChatModel.call(prompt)와 ChatModel.stream(prompt)는 이제 모델의 원시 응답을 반환해요 — 도구 호출은 자동으로 실행되지 않아요.

영향 (Impact)

ChatModel을 도구와 함께 직접 호출하고 자동 실행에 의존하던 코드는, 이제 실행되지 않은 모델의 도구 호출 요청을 응답에서 받게 돼요.

마이그레이션 방법 (Migration)

대부분의 애플리케이션은 ChatClient로 전환해요. 자동 등록된 ToolCallingAdvisor가 루프를 실행해줘요:

ChatClient.create(chatModel)
    .prompt(question)
    .tools(new MyTools())
    .call()
    .content();

ChatModel을 정말로 직접 써야 할 때(커스텀 오케스트레이터, 인프라 코드)는 ToolCallingManager로 루프를 수동으로 돌려요. 자세한 내용은 ChatModel 도구 호출: 루프를 수동으로 돌리기를 참고해요.

제거됨: SpringBeanToolCallbackResolver와 toolNames()

SpringBeanToolCallbackResolver와 toolNames() API(이름으로 Function/Supplier/Consumer 빈에서 도구를 해석하던 것)가 제거됐어요. 이제 도구는 명시적 ToolCallback 인스턴스 또는 @Tool 어노테이션이 붙은 객체로 등록해야 해요.

영향 (Impact)

Function 빈을 선언하고 toolNames(…)로 이름 참조하던 코드는 더 이상 컴파일되거나 해석되지 않아요.

마이그레이션 방법 (Migration)

ToolCallback 빈을 직접 선언해요(함수 스타일 도구라면 FunctionToolCallback.builder() 사용):

// Before (1.x) — 이름으로 해석되는 bare Function 빈
@Bean
@Description("Get the weather in location")
Function<WeatherRequest, WeatherResponse> currentWeather() {
    return weatherService::getWeather;
}
chatClient.prompt().toolNames("currentWeather"); // no longer exists

// After (2.0) — 명시적 ToolCallback 빈
@Bean
ToolCallback currentWeather() {
    return FunctionToolCallback.builder("currentWeather", weatherService::getWeather)
        .description("Get the weather in location")
        .inputType(WeatherRequest.class)
        .build();
}

@Autowired ToolCallback currentWeather;

chatClient.prompt()
    .user("What's the weather in Copenhagen?")
    .tools(currentWeather)
    .call()
    .content();

메서드 기반 도구는 @Tool로도 선언할 수 있어요:

class WeatherTools {
    @Tool(description = "Get the weather in location")
    WeatherResponse currentWeather(WeatherRequest request) {
        return weatherService.getWeather(request);
    }
}

chatClient.prompt().tools(new WeatherTools()).call().content();

이름 변경됨: ToolCallAdvisor → ToolCallingAdvisor

재귀적 도구 호출 어드바이저 클래스가 정확성을 위해 이름이 바뀌었어요. ToolCallAdvisor를 직접 참조했다면(예: 커스텀 인스턴스 등록 또는 확장) ToolCallingAdvisor로 참조를 업데이트해요.

// Before (1.x)
var advisor = ToolCallAdvisor.builder()...;

// After (2.0)
var advisor = ToolCallingAdvisor.builder()...;

이름 변경됨: FunctionCallback API → ToolCallback API

Spring AI 1.0이 이전 FunctionCallback API의 대체로 ToolCallback을 도입했어요. 2.0은 deprecated FunctionCallback 타입과 메서드를 완전히 제거해요. 이제 용어가 일관됩니다 — 어디서나 "tools", 어디에도 "functions"가 없어요. 레거시 FunctionCallback API를 여전히 사용하던 버전에서 업그레이드한다면:

Before (legacy) After (2.0)
FunctionCallback ToolCallback
FunctionCallback.builder().function(name, fn) FunctionToolCallback.builder(name, fn)
FunctionCallback.builder().method(…) ToolDefinition을 쓰는 MethodToolCallback.builder()
FunctionCallingOptions ToolCallingChatOptions
FunctionCallingOptions.builder().functionCallbacks(…) ToolCallingChatOptions.builder().toolCallbacks(…)
ChatClient.Builder#defaultFunctions(…) ChatClient.Builder#defaultTools(…)
ChatClient.prompt().functions(…) ChatClient.prompt().tools(…)

예시:

// Before — 함수 스타일 콜백
FunctionCallback.builder()
    .function("getCurrentWeather", new MockWeatherService())
    .description("Get the weather in location")
    .inputType(MockWeatherService.Request.class)
    .build();

// After — FunctionToolCallback을 통한 명시적 ToolCallback
FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
    .description("Get the weather in location")
    .inputType(MockWeatherService.Request.class)
    .build();
// Before — 빌더의 defaultFunctions
ChatClient.builder(chatModel)
    .defaultFunctions(
        FunctionCallback.builder()
            .function("WeatherInfo", new MockWeatherService())
            .description("Get the current weather")
            .inputType(MockWeatherService.Request.class)
            .build())
    .build();

// After — 빌더의 defaultTools
ChatClient.builder(chatModel)
    .defaultTools(
        FunctionToolCallback.builder("WeatherInfo", new MockWeatherService())
            .description("Get the current weather")
            .inputType(MockWeatherService.Request.class)
            .build())
    .build();
// Before — 호출별 functions()
chatClient.prompt()
    .user("...")
    .functions(callback)
    .call();

// After — 호출별 tools()
chatClient.prompt()
    .user("...")
    .tools(callback)
    .call();

새 코드에서는 선언적 @Tool 어노테이션을 선호해요 — 도구 정의를 참고해요.

JDBC 채팅 메모리 sequence_id 컬럼

JdbcChatMemoryRepository 스키마가 대화 내 메시지 순서를 결정하는 sequence_id BIGINT 컬럼을 추가해요. 이전에는 순서가 timestamp 컬럼에 의존했는데, TIMESTAMP 정밀도가 데이터베이스마다 달라요. MySQL과 MariaDB에서는 기본 정밀도가 1초라서, 같은 초 안에 저장된 메시지가 비결정적 순서로 반환됐어요. 전용 정수 시퀀스는 지원되는 모든 데이터베이스에서 동일하게 순서를 정해줘요. timestamp 컬럼은 유지돼요. 이제 메시지 생성 시간을 저장하며 JdbcChatMemoryRepository.CONVERSATION_TS 키(java.time.Instant)로 메시지 메타데이터에 노출돼서, 애플리케이션이 메시지 생성 시점을 표시할 수 있어요. timestamp는 저장 간에 보존돼요: 대화를 다시 저장하면 각 기존 메시지의 원래 생성 시간이 유지돼요.

영향 (Impact)

  • SPRING_AI_CHAT_MEMORY 테이블에 sequence_id 컬럼(BIGINT, Oracle에서는 NUMBER(19), SQLite에서는 INTEGER)과 SPRING_AI_CHAT_MEMORY_CONVERSATION_ID_SEQUENCE_ID_IDX 인덱스가 추가돼요. timestamp 컬럼과 그 인덱스는 변함없어요.
  • Spring AI 1.x가 만든 기존 테이블은 새 컬럼이 있어야 동작해요. 이제 메시지가 sequence_id로 정렬되거든요.
  • 저장소에서 읽은 메시지가 이제 메타데이터에 생성 타임스탬프를 담고 있어요. 메타데이터가 다르기 때문에, 가져온 메시지는 코드에서 구성한 동일한 메시지와 더 이상 같지 않아요(Message.equals). 값을 기준으로 메시지를 비교하거나, 집합이나 맵에서 메시지 동일성에 의존하는 코드는 이를 고려해야 해요.

마이그레이션 방법 (Migration)

변경은 추가(additive)적이에요. 새 컬럼을 추가하고 기존 대화별 순서로 백필하면 돼요. 데이터는 손실되지 않고 timestamp 컬럼도 건드리지 않아요. PostgreSQL의 경우:

ALTER TABLE SPRING_AI_CHAT_MEMORY ADD COLUMN sequence_id BIGINT;

WITH ordered AS (
    SELECT ctid, ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY "timestamp") - 1 AS seq
    FROM SPRING_AI_CHAT_MEMORY
)
UPDATE SPRING_AI_CHAT_MEMORY t
SET sequence_id = o.seq
FROM ordered o
WHERE t.ctid = o.ctid;

ALTER TABLE SPRING_AI_CHAT_MEMORY ALTER COLUMN sequence_id SET NOT NULL;

CREATE INDEX SPRING_AI_CHAT_MEMORY_CONVERSATION_ID_SEQUENCE_ID_IDX
ON SPRING_AI_CHAT_MEMORY(conversation_id, sequence_id);

식별자 따옴표와 행 동일성 표현(위의 ctid)을 데이터베이스에 맞게 조정해요. ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY <timestamp>) 패턴은 지원되는 모든 엔진에서 기존 대화별 순서를 재현해요. 또는 채팅 메모리는 영구 기록이 아니라 최근 대화 컨텍스트를 담고 있으므로, 업데이트된 schema-<platform>.sql 스크립트에서 테이블을 삭제하고 다시 만들 수도 있어요.

옵션 불변성과 기본값 (Options Immutability and Default Values)

옵션의 엄격한 불변성

옵션 클래스(ChatOptions, EmbeddingOptions 등)가 이제 엄격하게 불변(immutable)이에요. 옵션 내의 컬렉션(toolCallbacks, stopSequences, customHeaders 등)은 이제 수정 불가 컬렉션으로 저장돼요. 또한, 값의 부재를 더 잘 표현하기 위해 빈 컬렉션 대신 nullable 컬렉션이 사용돼요.

영향 (Impact)
  • 옵션이 이제 엄격하게 불변이므로 ChatOptions#copy()와 *Options#fromOptions(*Options) 메서드가 제거됐어요.
  • 옵션 getter가 반환한 컬렉션을 수정하면 UnsupportedOperationException이 발생해요.
마이그레이션 방법 (Migration)

옵션 인스턴스의 수정된 복사본을 만들려면 copy()나 fromOptions() 대신 mutate() 메서드를 사용해요:

// Before
OllamaChatOptions options = originalOptions.copy();
options.setFoo("...");

// After
OllamaChatOptions options = originalOptions.mutate()
        .foo("...")
        .build();

기본값이 옵션 생성자로 이동됨

옵션의 기본값(기본 모델 이름, 온도 등)이 Model 구현과 *Properties 구성 클래스에서 옵션 생성자 자체로 이동됐어요. *Properties 클래스의 기본 구성 프로퍼티는 더 이상 필요하지 않아 제거됐고, 옵션 수준 기본값과 일관성이 있어야 해요.

영향 (Impact)
  • ChatModel#getDefaultOptions()가 ChatModel#getOptions()를 선호해 deprecate 됐어요.
  • 기본값이 더 이상 *Properties 클래스에 중복되지 않아요.
마이그레이션 방법 (Migration)

모델의 기본 옵션을 가져올 때 getDefaultOptions() 대신 getOptions()를 사용해요:

// Before
ChatOptions options = chatModel.getDefaultOptions();

// After
ChatOptions options = chatModel.getOptions();

구성 프로퍼티 평탄화

위에서 설명한 채팅 구성 변경에 더해, 다른 모든 모델(Embedding, Image, Audio, Moderation, OCR 등)의 구성 프로퍼티가 더 이상 .options 접두사를 사용하지 않아요. 예를 들어 기존 spring.ai.openai.embedding.options.model는 이제 spring.ai.openai.embedding.model이에요. .options 변형에는 smoother 마이그레이션을 위해 deprecated 구성 프로퍼티가 제공돼요.

영향 (Impact)
  • *Properties 클래스의 getOptions() 메서드가 deprecate 됐어요.
  • *Properties 내부의 중첩 Options 클래스가 deprecate 됐어요.
  • toOptions() 메서드가 중첩 Options 클래스에서 루트 *Properties 클래스로 이동했어요.
마이그레이션 방법 (Migration)

application.properties 또는 application.yml에서 프로퍼티 키의 .options 세그먼트를 제거해요.

# Before
spring.ai.openai.embedding.options.model=text-embedding-3-small

# After
spring.ai.openai.embedding.model=text-embedding-3-small

Java 코드에서는 루트 프로퍼티 클래스를 직접 사용해 옵션에 접근하거나 toOptions()를 호출해요:

// Before
String model = properties.getOptions().getModel();
OpenAiEmbeddingOptions options = properties.getOptions().toOptions();

// After
String model = properties.getModel();
OpenAiEmbeddingOptions options = properties.toOptions();

옵션 빌더에서 N()이 n()으로 이름 변경됨

여러 *Options 및 구성 프로퍼티 클래스의 N() 빌더 메서드가 Java 명명 규칙에 맞추어 n()으로 이름이 바뀌었어요.

영향 (Impact)
  • 이 빌더들에서 .N(value)를 호출하는 Java 코드는 컴파일되지 않아요.
마이그레이션 방법 (Migration)

코드를 .n(value)를 호출하도록 업데이트해요.

// Before
OpenAiChatOptions.builder().N(1).build();

// After
OpenAiChatOptions.builder().n(1).build();

Ollama

이름 변경됨: spring.ai.ollama.chat.think-option → spring.ai.ollama.chat.think

spring.ai.ollama.chat.think-option 구성 프로퍼티가 spring.ai.ollama.chat.think로 이름이 바뀌었어요. application.properties 또는 application.yml을 그에 맞게 업데이트해요:

# Before
spring.ai.ollama.chat.think-option=true

# After
spring.ai.ollama.chat.think=true

Minimax 전용 지원이 Anthropic 지원으로 대체됨

MiniMax 스스로 권장한 대로, Minimax 전용 지원이 제거되고 Anthropic 지원을 사용하는 것으로 바뀌었어요. Spring AI Anthropic 지원을 https://api.minimax.io/anthropic base URL과 MiniMax 모델로 구성해서 사용해요. Embedding은 더 이상 지원되지 않아요.

JSON 유틸리티 리팩터링

새로 추가됨: JsonHelper와 업데이트된 JacksonUtils

spring-ai-commons에 JSON 직렬화/역직렬화를 수행하는 표준 방법인 새 JsonHelper 클래스가 도입됐어요. 인스턴스화할 수 있고 커스텀 JsonMapper를 받아들이므로, 사용 사례별로 JSON 동작을 쉽게 커스터마이즈할 수 있어요. JacksonUtils에 공유되고 미리 구성된 JsonMapper 인스턴스를 반환하는 새 getDefaultJsonMapper() 정적 메서드가 추가됐어요. JsonHelper는 이전에 JsonParser, ModelOptionsUtils, McpJsonParser에 흩어져 있던 JSON 작업을 중앙집중화해요. 기본 생성자는 JacksonUtils.getDefaultJsonMapper()의 공유 매퍼를 사용해요. 다른 직렬화 설정이 필요하면 커스텀 JsonMapper를 주입해요:

// Default — JacksonUtils의 공유 JsonMapper 사용
JsonHelper jsonHelper = new JsonHelper();

// Custom — 자신만의 JsonMapper 공급
JsonMapper myMapper = JsonMapper.builder()
    .addModule(new JavaTimeModule())
    .build();
JsonHelper customHelper = new JsonHelper(myMapper);

Deprecated: JsonParser

JsonParser(org.springframework.ai.util.json에 있음)가 제거 예정으로 deprecate 됐어요. 모든 메서드가 이제 JsonHelper에 위임해요.

영향 (Impact)

JsonParser 메서드를 호출하는 코드는 컴파일 시 deprecation 경고를 보게 돼요.

마이그레이션 방법 (Migration)
Before After
JsonParser.getJsonMapper() JacksonUtils.getDefaultJsonMapper()
JsonParser.fromJson(json, MyType.class) jsonHelper.fromJson(json, MyType.class)
JsonParser.fromJson(json, type) jsonHelper.fromJson(json, type)
JsonParser.toJson(object) jsonHelper.toJson(object)
JsonParser.toTypedObject(value, type) jsonHelper.convertToTypedObject(value, type)

제거됨: ModelOptionsUtils의 JSON 메서드

다음 ModelOptionsUtils 멤버가 제거됐어요. 모델 옵션이 더 이상 Jackson에 직접 의존하지 않기 때문이에요.

제거된 멤버 대체
ModelOptionsUtils.JSON_MAPPER JacksonUtils.getDefaultJsonMapper()
ModelOptionsUtils.jsonToMap(String) jsonHelper.fromJsonToMap(json)
ModelOptionsUtils.jsonToObject(String, Class<T>) jsonHelper.fromJson(json, type)
ModelOptionsUtils.toJsonString(Object) jsonHelper.toJson(object)
ModelOptionsUtils.toJsonStringPrettyPrinter(Object) JacksonUtils.getDefaultJsonMapper().writerWithDefaultPrettyPrinter().writeValueAsString(object)
ModelOptionsUtils.getJsonSchema(Type, boolean) JsonSchemaUtils.getJsonSchema(type, toUpperCase)
ModelOptionsUtils.getJsonSchema(Type) JsonSchemaUtils.getJsonSchema(type)
ModelOptionsUtils.toUpperCaseTypeValues(ObjectNode) JsonSchemaUtils.toUpperCaseTypeValues(node)
영향 (Impact)

제거된 필드나 메서드를 참조하는 코드는 컴파일되지 않아요.

마이그레이션 방법 (Migration)
// Before
import org.springframework.ai.model.ModelOptionsUtils;

Map<String, Object> map = ModelOptionsUtils.jsonToMap(jsonString);
String json = ModelOptionsUtils.toJsonString(myObject);
JsonMapper mapper = ModelOptionsUtils.JSON_MAPPER;

// After
import org.springframework.ai.util.JsonHelper;
import org.springframework.ai.util.JacksonUtils;

private static final JsonHelper jsonHelper = new JsonHelper();

Map<String, Object> map = jsonHelper.fromJsonToMap(jsonString);
String json = jsonHelper.toJson(myObject);
JsonMapper mapper = JacksonUtils.getDefaultJsonMapper();

제거됨: McpJsonParser

McpJsonParser(org.springframework.ai.mcp.annotation.method.tool.utils에 있음)가 삭제됐어요. 그 기능은 이제 JsonHelper가 담당해요.

마이그레이션 방법 (Migration)
Before After
McpJsonParser.toMap(object) jsonHelper.convertToMap(object)
McpJsonParser.fromMap(map, MyType.class) jsonHelper.convertFromMap(map, MyType.class)
McpJsonParser.fromMap(map, typeReference) jsonHelper.convertFromMap(map, parameterizedTypeReference)

MCP Elicitation API: TypeReference가 ParameterizedTypeReference로 대체됨

McpAsyncRequestContext와 McpSyncRequestContext에서 이전에 tools.jackson.core.type.TypeReference<T>를 받던 elicit(…) 오버로드가 이제 org.springframework.core.ParameterizedTypeReference<T>를 받아요.

영향 (Impact)

Jackson TypeReference로 이 메서드들을 호출하는 코드는 컴파일되지 않아요. 영향을 받는 메서드 시그니처:

  • McpAsyncRequestContext.elicit(TypeReference<T>)
  • McpAsyncRequestContext.elicit(Consumer<ElicitationSpec>, TypeReference<T>)
  • McpSyncRequestContext.elicit(TypeReference<T>)
  • McpSyncRequestContext.elicit(Consumer<ElicitationSpec>, TypeReference<T>)

마이그레이션 방법 (Migration)

Jackson TypeReference 익명 클래스를 Spring ParameterizedTypeReference 익명 클래스로 교체해요:

// Before
import tools.jackson.core.type.TypeReference;

Mono<StructuredElicitResult<Map<String, Object>>> result =
    context.elicit(e -> e.message("Please fill in the form"),
        new TypeReference<Map<String, Object>>() {});

// After
import org.springframework.core.ParameterizedTypeReference;

Mono<StructuredElicitResult<Map<String, Object>>> result =
    context.elicit(e -> e.message("Please fill in the form"),
        new ParameterizedTypeReference<Map<String, Object>>() {});

동일한 변경이 McpSyncRequestContext에도 적용돼요:

// Before
StructuredElicitResult<Person> result =
    context.elicit(e -> e.message("Provide your details"),
        new TypeReference<Person>() {});

// After
StructuredElicitResult<Person> result =
    context.elicit(e -> e.message("Provide your details"),
        new ParameterizedTypeReference<Person>() {});

Google GenAI Embedding: GoogleGenAiEmbeddingConnectionDetails 패키지 변경

GoogleGenAiEmbeddingConnectionDetails가 org.springframework.ai.google.genai에서 org.springframework.ai.google.genai.embedding으로 이동했어요.

영향 (Impact)

GoogleGenAiEmbeddingConnectionDetails를 직접 import하는 코드는 컴파일되지 않아요.

마이그레이션 방법 (Migration)

import 문을 업데이트해요:

// Before
import org.springframework.ai.google.genai.GoogleGenAiEmbeddingConnectionDetails;

// After
import org.springframework.ai.google.genai.embedding.GoogleGenAiEmbeddingConnectionDetails;

ChatClient 도구 호출

자동 ToolCallingAdvisor 등록

ChatClient가 이제 항상 어드바이저 체인에 ToolCallingAdvisor를 자동 등록해요(명시적으로 비활성화하지 않는 한). 그래서 모델이 요청한 도구 호출은, 도구가 정적으로 구성됐든 런타임에 다른 어드바이저가 주입했든 상관없이 자동으로 처리돼요.

영향 (Impact)

이미 ToolCallingAdvisor를 명시적으로 추가하던 코드는 체인에 어드바이저가 두 번 있게 돼요 — 명시적 추가 한 번, 자동 등록 한 번.

마이그레이션 방법 (Migration)

체인에서 명시적 ToolCallingAdvisor를 제거하고 자동 등록에 맡겨요. 커스텀 구성이 필요하다면, ChatClient 구성 시점에 미리 구성된 ToolCallingAdvisor.Builder를 공급하거나(기본 ToolCallingAdvisor 커스터마이징 섹션 참조), 자동 등록을 비활성화하고 어드바이저를 수동으로 등록해요:

// Before — 수동 등록
chatClient.prompt("What's the weather?")
    .tools(weatherTool)
    .advisors(ToolCallingAdvisor.builder().build())
    .call().content();

// After — 자동 등록이 처리, 명시적 어드바이저 불필요
chatClient.prompt("What's the weather?")
    .tools(weatherTool)
    .call().content();

// 또는 완전한 제어를 유지하려면 자동 등록을 명시적으로 비활성화
chatClient.prompt("What's the weather?")
    .tools(weatherTool)
    .advisors(
        AdvisorParams.toolCallingAdvisorAutoRegister(false),
        ToolCallingAdvisor.builder().build()
    )
    .call().content();

새로 추가됨: ToolAdvisor 마커 인터페이스

ToolAdvisor 마커 인터페이스가 추가됐어요. ToolCallingAdvisor가 이를 구현해요. DefaultChatClient는 이 마커를 확인해서 자동으로 ToolCallingAdvisor를 등록할지 결정해요 — 체인의 어드바이저 중 하나라도 이미 ToolAdvisor를 구현하면 자동 등록은 건너뜁니다. 도구 호출 수명주기를 소유하는 커스텀 어드바이저는 중복 ToolCallingAdvisor가 자동 추가되는 것을 막기 위해 ToolAdvisor를 구현해야 해요.

새로 추가됨: MemoryAdvisor 마커 인터페이스

MemoryAdvisor 마커 인터페이스가 추가됐어요. BaseChatMemoryAdvisor가 이제 이를 확장해요. DefaultChatClient는 이 마커로 다운스트림 메모리 어드바이더를 감지하고, 있으면 ToolCallingAdvisor의 내부 대화 기록을 비활성화해요. BaseChatMemoryAdvisor를 확장하지 않지만 도구 호출 자동 등록 로직과 통합해야 하는 커스텀 메모리 어드바이더는 MemoryAdvisor를 구현해야 해요.

새로 추가됨: 커스텀 ToolCallingAdvisor 구성을 위한 ChatClient.builder() 오버로드

새 5-인자 ChatClient.builder() 정적 메서드와 그에 맞는 DefaultChatClientBuilder 생성자가 자동 등록 중 사용할 어드바이저를 구성하는 ToolCallingAdvisor.Builder<?>를 받아요:

ChatClient chatClient = ChatClient
    .builder(chatModel, observationRegistry, null, null,
            ToolCallingAdvisor.builder().toolCallingManager(myToolCallingManager))
    .build();

Spring Boot 사용자는 대신 다음 중 하나를 선호해요:

  • spring.ai.chat.client.tool-calling.advisor-order를 설정해서 체인 내 어드바이저 위치를 제어.
  • ToolCallingAdvisor.Builder<?> 빈을 선언해서 자동 구성 빌더를 완전히 대체(@ConditionalOnMissingBean으로 억제).

제거됨: ChatClient의 ToolSpec Consumer API

tools(Consumer<ToolSpec>) / defaultTools(Consumer<ToolSpec>) API와 ChatClient.ToolSpec 인터페이스가 제거됐어요. tools(Object…) / defaultTools(Object…) 메서드가 이제 ToolCallback, ToolCallbackProvider, @Tool 어노테이션이 붙은 POJO 인스턴스, 그리고 이들 타입의 컬렉션 또는 배열을 직접 받아요. 컨텍스트는 전용 toolContext() / defaultToolContext() 메서드로 별도 설정해요.

마이그레이션 방법 (Migration)
// Before
chatClient.prompt()
    .tools(t -> t.callbacks(myCallback).context("tenantId", "acme"))
    .call().content();

// After
chatClient.prompt()
    .tools(myCallback)
    .toolContext(Map.of("tenantId", "acme"))
    .call().content();

ChatClientRequestSpec의 개별 toolCallbacks() 메서드와 ChatClient.Builder의 defaultToolCallbacks()는 tools(Object…) / defaultTools(Object…)를 선호해 deprecate 됐어요.

변경됨: MethodToolCallbackProvider가 IllegalStateException 대신 IllegalArgumentException을 던짐

MethodToolCallbackProvider가 이제 두 경우에 IllegalStateException(대신) IllegalArgumentException을 던져요:

  • 도구 객체에 @Tool 어노테이션이 붙은 메서드가 없을 때.
  • 여러 도구 객체가 중복 이름으로 콜백을 만들 때.
영향 (Impact)

MethodToolCallbackProvider(직접 또는 ToolCallbacks.from()을 통해)에서 IllegalStateException을 잡는 코드는 업데이트해야 해요.

// Before
try {
    ToolCallbacks.from(myObject);
}
catch (IllegalStateException e) { ... }

// After
try {
    ToolCallbacks.from(myObject);
}
catch (IllegalArgumentException e) { ... }

제거됨: Spring 빈 도구 해석 (SpringBeanToolCallbackResolver)

SpringBeanToolCallbackResolver와 bare Function / Supplier / Consumer 빈을 선언하고 toolNames()로 이름 참조하는 패턴이 제거됐어요. toolNames() 메서드가 모든 채팅 옵션 클래스와 ChatClient에서 제거됐어요.

마이그레이션 방법 (Migration)

원시 함수형 빈 대신 ToolCallback 빈을 직접 선언해요:

// Before — 런타임에 이름으로 해석되는 bare Function 빈
@Bean
@Description("Get the weather in location")
Function<WeatherRequest, WeatherResponse> currentWeather() {
    return weatherService::getWeather;
}

// After — 명시적 ToolCallback 빈
@Bean
ToolCallback currentWeather() {
    return FunctionToolCallback.builder("currentWeather", weatherService::getWeather)
        .description("Get the weather in location")
        .inputType(WeatherRequest.class)
        .build();
}

그런 다음 빈을 tools()로 직접 등록해요:

chatClient.prompt("What's the weather like in Copenhagen?")
    .tools(currentWeather)
    .call().content();

또는 선언적 접근을 위해 @Tool 어노테이션이 붙은 메서드를 사용해요(도구 API 참조).

Cloud Bindings

github.com/spring-cloud/spring-cloud-bindings와의 통합을 제공하던 spring-ai-spring-cloud-bindings 모듈이 제거됐어요.

BeanOutputConverter JSON 스키마 생성

BeanOutputConverter가 이제 JSON 스키마 생성을 JsonSchemaGenerator에 위임해서, 구조화된 출력 변환을 도구 호출에 쓰이는 JSON 스키마 동작과 일치시켜요.

영향 (Impact)

  • 기본 생성자에서 선택적(optional)인 Kotlin 프로퍼티(nullable이거나 기본값이 선언된)는 더 이상 JSON 스키마 required 배열에 포함되지 않아요.
  • @JsonProperty(required = false)로 어노테이션된 프로퍼티, 그리고 required가 지정되지 않은 @JsonProperty 선언도 더 이상 required로 취급되지 않아요.
  • 생성된 스키마가 이제 원시 타입에 대해 OpenAPI 스타일 format 힌트를 포함해요(예: int는 int32, long은 int64, LocalDateTime은 date-time). 도구 호출에 쓰이는 형식 규칙과 일치해요.
  • BeanOutputConverter.postProcessSchema(JsonNode) 확장 지점이 제거됐어요. 이 메서드를 오버라이드하는 커스텀 하위 클래스는 컴파일되지 않아요.

마이그레이션 방법 (Migration)

JSON 스키마 생성을 커스터마이즈하려면 BeanOutputConverter.generateSchema()를 오버라이드해요. 하위 클래스는 super.generateSchema()에 위임해서 기본 스키마를 후처리할 수 있어요.

// Before
class CustomConverter extends BeanOutputConverter<MyType> {

    CustomConverter() {
        super(MyType.class);
    }

    @Override
    protected void postProcessSchema(JsonNode schema) {
        // mutate schema
    }
}

// After
class CustomConverter extends BeanOutputConverter<MyType> {

    CustomConverter() {
        super(MyType.class);
    }

    @Override
    protected String generateSchema() {
        String schema = super.generateSchema();
        // post-process schema
        return schema;
    }
}

Azure Cosmos DB 지원

Azure Cosmos DB 벡터 저장소(spring-ai-azure-cosmos-db-store)와 채팅 메모리 저장소(spring-ai-model-chat-memory-repository-cosmos-db) 모듈, 그리고 그에 대응하는 자동구성과 스타터가 Spring AI 프로젝트에서 제거됐어요. Azure Cosmos DB 지원은 이제 Azure Cosmos DB 팀이 유지 관리하는 외부 모듈로 제공돼요. 문서와 의존성 좌표는 azurecosmosdb.github.io/spring-ai/docs/index.html에서 확인해요.

MCP SDK 호환성 변경: 필수 필드

MCP SDK가 compact record 생성자에서 Assert.notNull()을 통해 이전에 선택적이던 필드를 구성 시점에 필수로 강제해요.

호환성 변경: CreateMessageResult — model 이제 필수

// Before
CreateMessageResult.builder()
    .content(TextContent.builder(response).build())
    .build();

// After
CreateMessageResult.builder(Role.ASSISTANT, response, modelHint)
    .build();

호환성 변경: CreateMessageRequest — maxTokens 이제 필수

// Before
CreateMessageRequest.builder()
    .messages(messages)
    .build();

// After
CreateMessageRequest.builder(messages, 500)
    .build();

Deprecated 빌더 API

다음 no-arg 생성자와 builder() 메서드는 필수 인자를 앞에 요구하는 팩토리 메서드를 선호해 deprecate 됐어요:

Deprecated Replacement
new TextContent(text) TextContent.builder(text).build()
new ReadResourceResult(contents) ReadResourceResult.builder(contents).build()
new GetPromptResult(description, messages) GetPromptResult.builder(messages).description(description).build()
new ProgressNotification(token, progress, total, message) ProgressNotification.builder(token, progress).total(total).message(message).build()
LoggingMessageNotification.builder() LoggingMessageNotification.builder(level, data)
ElicitRequest.builder() ElicitRequest.builder(message, requestedSchema)
CallToolRequest.builder() CallToolRequest.builder(name)

관측성 (Observability)

도구 호출 (Tool Calling)

도구 호출 작업에 대해 생성되는 관측(observation)이 다음과 같이 바뀌었어요:

  • 스팬 이름이 tool_call <tool-name> 대신 execute_tool <tool-name>이에요.
  • 메트릭/스팬 속성 gen_ai.operation.name의 값이 framework 대신 execute_tool이에요.
  • 도구 타입(예: function)을 캡처하는 새 spring.ai.tool.type 메트릭/스팬 속성이 도입됐어요.
  • 채팅 모델이 식별한 도구 호출 ID를 캡처하는 새 spring.ai.tool.call.id 스팬 속성이 도입됐어요.

제거된 모듈 (Removed Modules)

spring-ai-hanadb-store 모듈 제거

spring-ai-hanadb-store 모듈이 Spring AI에서 제거됐어요.

채팅 메모리 어드바이더: 대화 ID가 이제 필수

내장 메모리 어드바이더(MessageChatMemoryAdvisor와 VectorStoreChatMemoryAdvisor)에서 대화 ID가 더 이상 선택적이지 않아요. 이 어드바이더를 통한 모든 호출은 어드바이저 컨텍스트를 통해 ChatMemory.CONVERSATION_ID를 공급해야 해요. 값이 없거나 null이면 어드바이저가 즉시 IllegalArgumentException을 던져요.

제거됨: ChatMemory.DEFAULT_CONVERSATION_ID

ChatMemory 인터페이스에서 상수 ChatMemory.DEFAULT_CONVERSATION_ID(값 "default")가 제거됐어요.

영향 (Impact)

이 상수를 참조하는 코드는 컴파일되지 않아요.

마이그레이션 방법 (Migration)

ChatMemory.DEFAULT_CONVERSATION_ID에 대한 모든 참조를 명시적 대화 ID 문자열이나 세션/사용자 컨텍스트에서 파생된 값으로 교체해요.

// Before
String conversationId = ChatMemory.DEFAULT_CONVERSATION_ID;

// After
String conversationId = "my-conversation-id";

제거됨: 메모리 어드바이더의 .conversationId() 빌더 메서드

MessageChatMemoryAdvisor.Builder와 VectorStoreChatMemoryAdvisor.Builder에서 .conversationId(String) 빌더 메서드가 제거됐어요. 구성 시점에 기본 대화 ID를 설정하는 것이 더 이상 지원되지 않아요.

영향 (Impact)

이 빌더들 중 하나에서 .conversationId()를 호출하는 코드는 컴파일되지 않아요.

마이그레이션 방법 (Migration)

.conversationId() 빌더 호출을 제거하고, 항상 ChatMemory.CONVERSATION_ID를 사용해 어드바이저 컨텍스트를 통해 호출 시점에 대화 ID를 공급해요:

// Before
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory)
        .conversationId("my-session")
        .build())
    .build();

chatClient.prompt()
    .user("Hello!")
    .call()
    .content();

// After
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
    .build();

chatClient.prompt()
    .user("Hello!")
    .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, "my-session"))
    .call()
    .content();

변경됨: BaseChatMemoryAdvisor.getConversationId() 시그니처

기본 메서드 BaseChatMemoryAdvisor.getConversationId(Map, String)가 getConversationId(Map)으로 대체됐어요.

영향 (Impact)

두 인자 형태를 오버라이드하거나 호출하는 커스텀 어드바이저 구현은 컴파일되지 않아요.

마이그레이션 방법 (Migration)

오버라이드와 호출 지점을 한 인자 형태로 업데이트해요. 메서드를 호출하기 전에 컨텍스트가 항상 ChatMemory.CONVERSATION_ID를 포함하는지 확인해요.

// Before
String conversationId = getConversationId(context, this.defaultConversationId);

// After
String conversationId = getConversationId(context); // throws if CONVERSATION_ID is absent

제거됨: PromptChatMemoryAdvisor

PromptChatMemoryAdvisor가 제거됐어요. 대체로 MessageChatMemoryAdvisor를 사용해요.

영향 (Impact)

PromptChatMemoryAdvisor를 import하거나 참조하는 코드는 컴파일되지 않아요.

마이그레이션 방법 (Migration)

PromptChatMemoryAdvisor를 MessageChatMemoryAdvisor로 교체해요. 빌더 API는 동일해요. MessageChatMemoryAdvisor는 메모리를 시스템 프롬프트에 평문으로 주입하는 대신, 대화 기록을 프롬프트의 채팅 메시지로 직접 포함해요.

// Before
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build())
    .build();

// After
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
    .build();

ChatClient 옵션이 이제 빌더를 요구함

ChatClient를 사용할 때 .options() / .defaultOptions() 메서드가 이제 완전히 빌드된 ChatOptions 인스턴스가 아니라 ChatOptions.Builder(또는 프로바이더별 빌더 하위 타입)를 받아요. 이 변경은 메서드의 제네릭 타입 제약 <B extends ChatOptions.Builder<?>>에 의해 컴파일 시점에 강제돼요. 옵션 빌더는 첫 번째 어드바이저가 호출되기 전에 모델의 기본 옵션과 병합되므로, 명시적으로 설정한 필드만 기본값을 오버라이드해요.

// Before — 빌드된 ChatOptions 인스턴스 전달 (ChatClient에서 더 이상 컴파일 안 됨)
ChatOptions opts = AnthropicChatOptions.builder()
    .maxTokens(100)
    .temperature(0.7)
    .build();
String response = chatClient.prompt("Tell me a joke")
    .options(opts)
    .call().content();

// After — 빌더를 직접 전달
String response = chatClient.prompt("Tell me a joke")
    .options(AnthropicChatOptions.builder()
        .maxTokens(100)
        .temperature(0.7))
    .call().content();

참고 이 제약은 ChatClient에만 적용돼요. ChatModel.call(Prompt)를 직접 호출할 때는, 프롬프트가 모델이 기대하는 구체 타입의 완전히 빌드된 ChatOptions 인스턴스(예: AnthropicChatModel의 경우 AnthropicChatOptions)를 담고 있어야 해요. ChatModel은 옵션을 병합하지 않아요: Prompt.getOptions()가 non-null이면 그대로 사용되고, null이면 모델 자체의 기본 옵션을 사용해요. ChatModel 수준에서는 부분 병합이 발생하지 않아요.

buildRequestPrompt가 공개 API에서 제거됨

buildRequestPrompt 메서드가 더 이상 공개 API의 일부가 아니에요. 이전에 실수로 노출됐던 것으로, private 또는 package-private 가시성으로 복원됐어요.

OpenAI Java SDK 전환

Spring AI가 이제 spring-ai-openai 모듈의 모든 OpenAI 모델(Chat, Embeddings, Image, Audio Speech, Audio Transcription, Moderation)에 공식 openai-java SDK를 내부적으로 사용해요. 전환은 매끄러워야하며, spring-ai-openai 모듈의 기존 사용자에게 큰 호환성 변경은 기대되지 않아요. 모든 프로퍼티(spring.ai.openai.* 접두사), 빌더, 옵션은 완전히 그대로 유지돼요. 기존 extraBody 구성 파라미터는 openai-java SDK의 기본 additionalBodyProperties로 투명하게 매핑돼요.

spring-ai-azure-openai 모듈 제거

spring-ai-azure-openai 모듈(및 그 관련 Spring Boot 스타터 spring-ai-starter-model-azure-openai와 자동구성 spring-ai-autoconfigure-model-azure-openai)이 Spring AI에서 제거됐어요. 기존 사용자는 대신 spring-ai-openai 모듈(및 관련 스타터와 자동구성)을 사용하고, 클래스 이름(예: Azure 접두사 제거)과 구성 프로퍼티를 그에 맞게 조정해야 해요.

spring-ai-openai-sdk 모듈 제거

spring-ai-openai-sdk 모듈(및 그 관련 Spring Boot 스타터 spring-ai-starter-model-openai-sdk와 자동구성 spring-ai-autoconfigure-model-openai-sdk)이 Spring AI에서 제거됐어요. 기존 사용자는 대신 spring-ai-openai 모듈(및 관련 스타터와 자동구성)을 사용하고, 클래스 이름(예: Sdk 접미사 제거)과 구성 프로퍼티를 그에 맞게 조정해야 해요.

spring-ai-oci-genai 모듈 제거

관련 모듈은 이제 github.com/oracle/spring-cloud-oracle/tree/main/spring-ai-oracle에서 찾을 수 있어요.

MCP Java SDK가 2.0.0으로 업그레이드됨

Spring AI 2.0.0이 MCP Java SDK를 1.1.x에서 2.0.0으로 업그레이드해요. 이 릴리스는 SDK 수준에서 MCP 타입을 직접 다루는 애플리케이션에 영향을 줄 수 있는 몇 가지 호환성 변경을 도입해요.

서버 측 도구 입력 검증이 기본적으로 활성화됨

MCP 서버가 이제 도구 핸들러를 호출하기 전에 들어오는 도구 인자를 도구의 JSON 스키마에 대해 검증해요. 검증 실패는 isError=true와 설명적인 오류 메시지가 있는 CallToolResult를 생성해요.

영향 (Impact)

이전에 느슨하게 타입된 도구 인자나 누락된 인자를 받아들이던 기존 MCP 서버는, 비준수 인자를 보내는 클라이언트에게 검증 오류를 반환할 수 있어요.

마이그레이션 방법 (Migration)

도구 정의가 정확한 JSON 스키마를 담고 있고 모든 클라이언트가 준수 인자를 보내는지 확인해요. 검증을 비활성화하고 2.0 이전 동작을 복원하려면 서버 빌더에 validateToolInputs(false)를 설정해요:

McpServer.sync(transportProvider)
    .validateToolInputs(false)
    .tool(myTool, handler)
    .build();

참고 @McpTool 어노테이션을 사용할 때 Spring AI는 메서드 파라미터에서 JSON 스키마를 자동으로 생성해요. 이 스키마는 내장 검증기와 호환되며 추가 조치가 필요 없어요.

Tool.inputSchema가 JsonSchema에서 Map<String, Object>로 변경됨

McpSchema.Tool.inputSchema()(및 outputSchema())가 이전의 JsonSchema record 대신 Map<String, Object>를 반환해요. 이를 통해 임의의 JSON 스키마 방언 키워드($ref, unevaluatedProperties, 벤더 확장)가 잘리지 않고 왕복(round-trip)할 수 있어요.

영향 (Impact)

tool.inputSchema()를 JsonSchema 객체로 사용하는 코드는 컴파일되지 않아요.

마이그레이션 방법 (Migration)

Map<String, Object>로 전환해요:

// Before
McpSchema.JsonSchema schema = tool.inputSchema();

// After
Map<String, Object> schema = tool.inputSchema();

McpSchema.Tool.Builder로 도구를 구성할 때 deprecated inputSchema(JsonSchema) 헬퍼는 하위 호환성을 위해 여전히 사용 가능하지만, inputSchema(Map) 또는 inputSchema(McpJsonMapper, String)을 선호해요.

참고 @McpTool 어노테이션이나 Spring AI의 SyncMcpToolProvider / AsyncMcpToolProvider를 사용하는 애플리케이션은 영향받지 않아요 — Spring AI가 스키마 생성을 내부적으로 처리해요.

HTTP 클라이언트 전송에서 Builder.customizeRequest() 제거됨

deprecated Builder.customizeRequest() 메서드가 HttpClientSseClientTransport.Builder와 HttpClientStreamableHttpTransport.Builder에서 제거됐어요.

영향 (Impact)

이 빌더 타입에서 .customizeRequest()를 직접 호출하는 코드는 컴파일되지 않아요.

마이그레이션 방법 (Migration)

customizeRequest()를 httpRequestCustomizer()(동기) 또는 asyncHttpRequestCustomizer()(비동기)로 교체해요:

// Before
HttpClientSseClientTransport.builder(baseUrl)
    .customizeRequest(req -> req.header("Authorization", "Bearer token"))
    .build();

// After
HttpClientSseClientTransport.builder(baseUrl)
    .httpRequestCustomizer(req -> req.header("Authorization", "Bearer token"))
    .build();

참고 이미 McpClientCustomizer<B> API로 마이그레이션했다면 추가 조치가 필요 없어요.

sealed MCP 스키마 인터페이스 제거됨

다음 인터페이스는 더 이상 sealed가 아니에요: McpSchema.JSONRPCMessage, McpSchema.Request, McpSchema.Result, McpSchema.Notification, McpSchema.ResourceContents, McpSchema.CompleteReference, McpSchema.Content.

영향 (Impact)

완전성 검사를 위해 sealed 계층 구조에 의존하던 이 타입들에 대한 포괄적(exhaustive) switch 표현식은 default 분기 없이는 더 이상 컴파일되지 않아요.

마이그레이션 방법 (Migration)

이 타입들에 대한 포괄적 switch에 default 분기를 추가해요:

// Before (McpSchema.Content가 sealed일 때 default 불필요)
String text = switch (content) {
    case McpSchema.TextContent tc   -> tc.text();
    case McpSchema.ImageContent ic  -> "[image]";
    case McpSchema.EmbeddedResource er -> "[resource]";
};

// After
String text = switch (content) {
    case McpSchema.TextContent tc   -> tc.text();
    case McpSchema.ImageContent ic  -> "[image]";
    case McpSchema.EmbeddedResource er -> "[resource]";
    default -> throw new IllegalArgumentException("Unknown content type: " + content);
};

참고 이 변경은 어노테이션이나 Spring AI의 상위 수준 추상화를 통해 MCP와 상호작용하는 대부분의 Spring AI 사용자에게 영향을 줄 가능성이 낮아요.

Usage의 통합 캐시 사용량 메트릭

org.springframework.ai.chat.metadata.Usage 인터페이스가 두 개의 기본 메서드 getCacheReadInputTokens()와 getCacheWriteInputTokens()를 얻었어요. 둘 다 프로바이더가 값을 보고하지 않으면 null을 반환해요. Anthropic, Bedrock Converse, OpenAI, Google GenAI가 이를 채워요: Anthropic과 Bedrock은 양방향으로, OpenAI와 Google은 캐시 읽기에만. 네이티브 타입으로 캐스팅하거나 이 숫자에 대해 프로바이더별 메타데이터 키를 읽던 코드는 인터페이스 메서드로 전환할 수 있어요. 예시는 프롬프트 캐시 사용량 메트릭을 참고해요.

Anthropic 모듈

spring-ai-anthropic이 이제 손으로 만든 RestClient / WebClient 구현 대신 com.anthropic:anthropic-java 기반으로 만들어졌어요. 대부분의 애플리케이션에 이 변경은 투명해요. Maven 아티팩트, spring.ai.anthropic.* 구성, AnthropicChatModel / AnthropicChatOptions / ChatClient API가 보존되고, ChatClient나 ChatModel.call(Prompt)를 거치는 코드는 업데이트가 필요 없어요. org.springframework.ai.anthropic.api에서 import하거나, AnthropicApi 인자로 AnthropicChatModel을 구성하거나, 이전 maxTokens 기본값에 의존하던 코드는 주의가 필요해요.

영향 (Impact)

  • org.springframework.ai.anthropic.api.AnthropicApi와 그 중첩 record 타입들(ChatCompletionRequest, ContentBlock, Tool, ToolChoice*, 스트리밍 이벤트 record)이 사라졌어요. 이를 참조하는 코드는 컴파일되지 않아요.
  • 공개 AnthropicChatModel(AnthropicApi, AnthropicChatOptions, …) 생성자가 사라졌어요. AnthropicChatModel.builder()를 사용해요.
  • AnthropicChatOptions#maxTokens 기본값이 500 대신 4096이에요. 이전 상한에 닿던 응답은 이제 더 길게 실행되고 그에 따라 토큰 사용량도 높아져요.
  • AnthropicCacheOptions, AnthropicCacheStrategy, AnthropicCacheTtl, CacheBreakpointTracker, CacheEligibilityResolver가 api(및 api.utils)에서 루트 org.springframework.ai.anthropic 패키지로 이동했어요.
  • AnthropicCacheType, StreamHelper, metadata.AnthropicRateLimit이 제거됐고; 동등한 SDK 타입(CacheControlEphemeral, AsyncStreamResponse<RawMessageStreamEvent>, RateLimitException)이 직접 사용돼요.
  • CitationDocument가 AnthropicCitationDocument로 이름 변경됐어요.
  • com.anthropic:anthropic-java가 com.squareup.okhttp3:okhttp를 전이적으로 클래스패스에 가져와요.

마이그레이션 방법 (Migration)

이동된 캐시 및 인용 클래스에 대한 import를 업데이트해요:

Before After
org.springframework.ai.anthropic.api.AnthropicCacheOptions org.springframework.ai.anthropic.AnthropicCacheOptions
org.springframework.ai.anthropic.api.AnthropicCacheStrategy org.springframework.ai.anthropic.AnthropicCacheStrategy
org.springframework.ai.anthropic.api.AnthropicCacheTtl org.springframework.ai.anthropic.AnthropicCacheTtl
org.springframework.ai.anthropic.api.utils.CacheBreakpointTracker org.springframework.ai.anthropic.CacheBreakpointTracker
org.springframework.ai.anthropic.api.utils.CacheEligibilityResolver org.springframework.ai.anthropic.CacheEligibilityResolver
org.springframework.ai.anthropic.api.CitationDocument org.springframework.ai.anthropic.AnthropicCitationDocument

AnthropicCacheStrategy(NONE, TOOLS_ONLY, SYSTEM_ONLY, SYSTEM_AND_TOOLS, CONVERSATION_HISTORY)와 AnthropicCacheTtl(FIVE_MINUTES, ONE_HOUR)은 동일한 enum 값을 유지해요. 직접 생성자 사용을 빌더로 교체해요:

// Before
AnthropicApi anthropicApi = new AnthropicApi(apiKey);
AnthropicChatModel chatModel = new AnthropicChatModel(anthropicApi, options);

// After
AnthropicChatModel chatModel = AnthropicChatModel.builder()
    .apiKey(apiKey)
    .defaultOptions(options)
    .build();

500-토큰 기본값으로 비용을 제한하던 상황이라면 명시적으로 설정해요:

AnthropicChatOptions.builder()
    .maxTokens(500)
    // ...
    .build();

Anthropic 모듈을 공식 Java SDK로 마이그레이션이 나머지(직접 SDK 접근, 스트리밍 동작, 프롬프트 캐싱, 인용, 제거된 타입)를 다룹니다.

새 채팅 옵션

spring-ai-anthropic에도 네 가지 새 채팅 옵션이 추가됐어요. 각각의 전체 레퍼런스는 Anthropic 채팅에 있어요. 핵심은:

옵션 추가하는 것
Thinking display thinkingEnabled / thinkingAdaptive 빌더의 Display.SUMMARIZED와 Display.OMITTED. Claude가 전체 추론을 표시하지 않고 thinking 예산을 사용할 수 있어요.
Service tier Anthropic의 우선 용량 계층을 위한 AnthropicServiceTier.AUTO / STANDARD_ONLY. 프로퍼티 spring.ai.anthropic.chat.service-tier.
내장 웹 검색 AnthropicWebSearchTool이 요청 중 Claude가 웹을 검색하게 해요. spring.ai.anthropic.chat.web-search-tool.* 아래의 프로퍼티.
Inference geo 데이터 상주 라우팅을 위한 inferenceGeo("us") 또는 inferenceGeo("eu"). 프로퍼티 spring.ai.anthropic.chat.inference-geo.

참고 1.1.x(또는 1.0.x)에서 업그레이드한다면, Anthropic 모듈을 공식 Java SDK로 마이그레이션에서 전체 RestClient → SDK 전환 가이드를 확인해요.

MCP 어노테이션이 Spring AI로 마이그레이션됨

org.springaicommunity:mcp-annotations 외부 라이브러리가 mcp-annotations 모듈의 의존성에서 제거됐어요. 그 클래스는 이제 새 패키지 구조 아래 Spring AI 자체의 일부가 돼요.

영향 (Impact)

  • 모든 MCP 어노테이션과 프로바이더 클래스에 새 정규화된 이름(full qualified name)이 있어요.
  • org.springaicommunity:mcp-annotations 아티팩트가 더 이상 Spring AI에 의해 전이적으로 제공되지 않아요.
  • org.springaicommunity.mcp.*에서 import하는 코드는 컴파일되지 않아요.

패키지 이름 변경 (Package Rename)

옛 패키지 새 패키지
org.springaicommunity.mcp.annotation.* org.springframework.ai.mcp.annotation.*
org.springaicommunity.mcp.method.* org.springframework.ai.mcp.annotation.method.*
org.springaicommunity.mcp.provider.* org.springframework.ai.mcp.annotation.provider.*

마이그레이션 방법 (Migration)

애플리케이션 코드의 모든 import를 업데이트해요:

// Before
import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpPrompt;
import org.springaicommunity.mcp.annotation.McpResource;
import org.springaicommunity.mcp.annotation.McpSampling;
import org.springaicommunity.mcp.provider.tool.SyncMcpToolProvider;
import org.springaicommunity.mcp.provider.prompt.AsyncMcpPromptProvider;

// After
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.ai.mcp.annotation.McpPrompt;
import org.springframework.ai.mcp.annotation.McpResource;
import org.springframework.ai.mcp.annotation.McpSampling;
import org.springframework.ai.mcp.annotation.provider.tool.SyncMcpToolProvider;
import org.springframework.ai.mcp.annotation.provider.prompt.AsyncMcpPromptProvider;

org.springaicommunity:mcp-annotations를 직접 Maven/Gradle 의존성으로 선언했다면 제거해요 — 클래스는 이제 Spring AI의 spring-ai-mcp-annotations 모듈이 제공해요.

OpenRewrite로 자동 마이그레이션

제공되는 OpenRewrite 레시피로 모든 import와 의존성 변경을 자동화할 수 있어요. 명령줄에서 migrate-to-2-0-0-M3.yaml 레시피를 적용해요:

mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
  -Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
  -Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpAnnotations \
  -Dmaven.compiler.failOnError=false

레시피가 두 가지 변경을 자동으로 수행해요:

  1. org.springaicommunity:mcp-annotations Maven 의존성을 제거해요.
  2. .java 파일 전체의 import 문을 새 org.springframework.ai.mcp.annotation.* 패키지를 가리키도록 다시 써요. 모든 M3 마이그레이션을 한 번에 실행하려면 총괄(umbrella) 레시피를 사용해요 — run-all-m3-migrations 참조.

MCP Spring 전송 모듈이 Spring AI로 이동됨

mcp-spring-webflux와 mcp-spring-webmvc 전송 모듈이 더 이상 MCP Java SDK에서 제공되지 않아요. Spring AI 2.0부터 이들은 Spring AI 프로젝트 자체의 일부예요.

영향 (Impact)

  • 두 아티팩트의 Maven group ID가 변경됐어요.
  • 모든 Spring 특화 전송 클래스의 Java 패키지 이름이 변경됐어요.
  • MCP Java SDK 버전 요구사항이 0.18.x에서 1.0.x로 올라갔어요.

Maven 의존성 group ID 변경

<dependency>
    <groupId>io.modelcontextprotocol.sdk</groupId>
    <artifactId>mcp-spring-webflux</artifactId>
</dependency>

<dependency>
    <groupId>io.modelcontextprotocol.sdk</groupId>
    <artifactId>mcp-spring-webmvc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>mcp-spring-webflux</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>mcp-spring-webmvc</artifactId>
</dependency>

참고 spring-ai-bom 또는 Spring AI MCP 스타터(spring-ai-starter-mcp-server-webflux, spring-ai-starter-mcp-server-webmvc, spring-ai-starter-mcp-client-webflux)를 사용할 때는 명시적 버전이 필요 없어요 — BOM이 자동으로 관리해줘요.

Java 패키지 재배치

모든 Spring 특화 전송 클래스가 org.springframework.ai 패키지로 이동했어요.

Class Old package New package
WebFluxSseServerTransportProvider io.modelcontextprotocol.server.transport org.springframework.ai.mcp.server.webflux.transport
WebFluxStreamableServerTransportProvider io.modelcontextprotocol.server.transport org.springframework.ai.mcp.server.webflux.transport
WebFluxStatelessServerTransport io.modelcontextprotocol.server.transport org.springframework.ai.mcp.server.webflux.transport
WebMvcSseServerTransportProvider io.modelcontextprotocol.server.transport org.springframework.ai.mcp.server.webmvc.transport
WebMvcStreamableServerTransportProvider io.modelcontextprotocol.server.transport org.springframework.ai.mcp.server.webmvc.transport
WebMvcStatelessServerTransport io.modelcontextprotocol.server.transport org.springframework.ai.mcp.server.webmvc.transport
Class Old package New package
WebFluxSseClientTransport io.modelcontextprotocol.client.transport org.springframework.ai.mcp.client.webflux.transport
WebClientStreamableHttpTransport io.modelcontextprotocol.client.transport org.springframework.ai.mcp.client.webflux.transport

마이그레이션 방법 (Migration)

Java import를 업데이트해요:

// Before
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;

// After
import org.springframework.ai.mcp.server.webflux.transport.WebFluxSseServerTransportProvider;
import org.springframework.ai.mcp.server.webmvc.transport.WebMvcSseServerTransportProvider;
import org.springframework.ai.mcp.client.webflux.transport.WebFluxSseClientTransport;
import org.springframework.ai.mcp.client.webflux.transport.WebClientStreamableHttpTransport;

참고 Spring AI 스타터를 통한 Spring Boot 자동구성에만 의존한다면 Java 코드 변경이 필요 없어요. 위에서 설명한 대로 pom.xml/build.gradle 의존성 좌표만 업데이트하면 돼요.

전체 MCP 전송 마이그레이션 레퍼런스는 Spring AI 2.0으로 업그레이드를 참고해요.

OpenRewrite로 자동 마이그레이션

제공되는 OpenRewrite 레시피로 모든 Maven 의존성과 Java import 변경을 자동화할 수 있어요:

mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
  -Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
  -Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpSpringTransports \
  -Dmaven.compiler.failOnError=false

참고 프로젝트가 명시적 <version> 없이(BOM으로 버전 관리되는) io.modelcontextprotocol.sdk:mcp-spring-webflux 또는 mcp-spring-webmvc를 선언하면 Maven이 pom.xml을 파싱하지 못하고 레시피가 절대 실행되지 않아요. 그런 파일들을 먼저 패치한 다음 OpenRewrite를 실행해요:

# Step 1 – groupId를 직접 패치해서 Maven이 모듈을 로드할 수 있게 함
find . -name "pom.xml" -print0 \
  | xargs -0 perl -i -0pe \
    's{<groupId>io\.modelcontextprotocol\.sdk</groupId>(\s+)<artifactId>mcp-spring-webflux</artifactId>}{<groupId>org.springframework.ai</groupId>$1<artifactId>mcp-spring-webflux</artifactId>}g;
     s{<groupId>io\.modelcontextprotocol\.sdk</groupId>(\s+)<artifactId>mcp-spring-webmvc</artifactId>}{<groupId>org.springframework.ai</groupId>$1<artifactId>mcp-spring-webmvc</artifactId>}g'

# Step 2 – OpenRewrite를 실행해 Java import와 나머지 POM 변경을 마이그레이션
mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
  -Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
  -Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpSpringTransports \
  -Dmaven.compiler.failOnError=false

모든 M3 마이그레이션을 한 번에 실행하려면 총괄 레시피를 사용해요 — run-all-m3-migrations 참조.

MCP 클라이언트 커스터마이저 API 통합

McpAsyncClientCustomizer와 McpSyncClientCustomizer가 제거되고 단일 제네릭 인터페이스 McpClientCustomizer<B>으로 대체됐어요.

영향 (Impact)

  • McpAsyncClientCustomizer가 더 이상 존재하지 않아요 — 구현하는 빈에 컴파일 오류.
  • McpSyncClientCustomizer가 더 이상 존재하지 않아요 — 구현하는 빈에 컴파일 오류.
  • McpSyncClientConfigurer와 McpAsyncClientConfigurer 생성자가 이제 옛 타입별 목록 대신 List<McpClientCustomizer<…>>을 받아요.
  • HttpClient 기반 전송 자동구성(SseHttpClientTransportAutoConfiguration, StreamableHttpHttpClientTransportAutoConfiguration)에서 SDK 수준의 McpSyncHttpClientRequestCustomizer와 McpAsyncHttpClientRequestCustomizer 빈이 더 이상 적용되지 않아요. 전송 수준 커스터마이즈는 이제 각각 McpClientCustomizer<HttpClientSseClientTransport.Builder>와 McpClientCustomizer<HttpClientStreamableHttpTransport.Builder>로 진행돼요.

마이그레이션 방법 (Migration)

커스터마이저 빈을, 필요로 하는 spec 또는 빌더 타입으로 파라미터화된 새 제네릭 인터페이스로 교체해요:

// Before
@Bean
public McpSyncClientCustomizer mySyncCustomizer() {
    return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}

@Bean
public McpAsyncClientCustomizer myAsyncCustomizer() {
    return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}

// After
@Bean
public McpClientCustomizer<McpClient.SyncSpec> mySyncCustomizer() {
    return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}

@Bean
public McpClientCustomizer<McpClient.AsyncSpec> myAsyncCustomizer() {
    return (name, spec) -> spec.requestTimeout(Duration.ofSeconds(30));
}

HttpClient 전송 커스터마이즈(이전에는 McpSyncHttpClientRequestCustomizer / McpAsyncHttpClientRequestCustomizer로 수행)의 경우:

// Before
@Bean
public McpSyncHttpClientRequestCustomizer myRequestCustomizer() {
    return requestBuilder -> requestBuilder.header("Authorization", "Bearer token");
}

// After
@Bean
public McpClientCustomizer<HttpClientSseClientTransport.Builder> mySseTransportCustomizer() {
    return (name, builder) -> builder.httpRequestCustomizer(
        req -> req.header("Authorization", "Bearer token")
    );
}

OpenRewrite로 자동 마이그레이션

제공되는 OpenRewrite 레시피로 import와 타입 변경을 자동화할 수 있어요:

mvn org.openrewrite.maven:rewrite-maven-plugin:6.32.0:run \
  -Drewrite.configLocation=https://raw.githubusercontent.com/spring-projects/spring-ai/refs/heads/main/src/rewrite/migrate-to-2-0-0-M3.yaml \
  -Drewrite.activeRecipes=org.springframework.ai.migration.M3MigrateMcpClientCustomizer \
  -Dmaven.compiler.failOnError=false

레시피가 다음 변경을 자동으로 수행해요:

  1. McpAsyncClientCustomizer와 McpSyncClientCustomizer import를 McpClientCustomizer로 교체하고 필요한 import io.modelcontextprotocol.client.McpClient;를 추가해요.
  2. implements McpAsyncClientCustomizer를 implements McpClientCustomizer<McpClient.AsyncSpec>으로 재작성해요.
  3. implements McpSyncClientCustomizer를 implements McpClientCustomizer<McpClient.SyncSpec>으로 재작성해요.
  4. 나머지 모든 사용(반환 타입, 변수 선언, 파라미터 타입)을 재작성해요.

참고 McpSyncHttpClientRequestCustomizer와 McpAsyncHttpClientRequestCustomizer 빈은 이제 전송 자동구성에 적용되지 않아요. McpClientCustomizer<TransportBuilder>로의 마이그레이션에는 수동 단계가 필요해요 — 대상 빌더 타입은 구성하는 전송(SSE vs Streamable HTTP)에 따라 달라요.

모든 M3 마이그레이션을 한 번에 실행하려면 총괄 레시피를 사용해요 — run-all-m3-migrations 참조.

MCP WebMvc 전송 헤더가 소문자로 정규화됨

WebMvcSseServerTransportProvider, WebMvcStatelessServerTransport, WebMvcStreamableServerTransportProvider에서 securityValidator.validateHeaders(headers)에 전달되는 Map<String, List<String>>의 모든 헤더 이름이 이제 소문자로 정규화돼요. 이전에는 헤더 이름이 원래 HTTP 대소문자(예: "Authorization", "Content-Type")로 전달됐어요. 이제 항상 소문자(예: "authorization", "content-type")예요.

영향 (Impact)

혼합 대소문자 이름으로 헤더를 조회하는 커스텀 ServerTransportSecurityValidator 구현은 이를 조용히 찾지 못하게 돼요.

마이그레이션 방법 (Migration)

ServerTransportSecurityValidator의 모든 헤더 이름 조회를 소문자 키로 업데이트해요:

// Before
public void validateHeaders(Map<String, List<String>> headers) {
    List<String> authHeader = headers.get("Authorization");
    // ...
}

// After
public void validateHeaders(Map<String, List<String>> headers) {
    List<String> authHeader = headers.get("authorization");
    // ...
}

대화 기록이 ToolContext에서 제거됨

대화 기록이 더 이상 ToolContext에 자동으로 추가되지 않아요. TOOL_CALL_HISTORY 상수와 getToolCallHistory() 메서드가 ToolContext 클래스에서 제거됐어요.

영향 (Impact)

  • ToolContext.TOOL_CALL_HISTORY 상수가 더 이상 존재하지 않아요
  • ToolContext.getToolCallHistory() 메서드가 더 이상 존재하지 않아요
  • 대화 기록이 더 이상 ToolContext에 자동으로 채워지지 않아요

왜 이 변경을 했나요? (Why This Change?)

  1. 메모리 효율성: 긴 대화에서 무한정 메모리 증가를 방지해요
  2. 관심사 분리: 도구는 자신의 파라미터로 동작해야 하지, 대화 상태를 관리하면 안 돼요
  3. 아키텍처 정렬: 대화 컨텍스트는 도구 실행이 아니라 어드바이저 수준에 속해요

마이그레이션 방법 (Migration)

애플리케이션에 대화 기록 관리가 필요하다면 ToolCallingAdvisor를 사용해요:

ChatClient chatClient = ChatClient.builder()
    .defaultAdvisors(
        ToolCallingAdvisor.builder()
            .conversationHistoryEnabled(true)  // Full history (default)
            .build()
    )
    .build();

ToolCallingAdvisor 작동 방식: ToolCallingAdvisor는 어드바이저 수준에서 대화 기록을 관리해요:

  • conversationHistoryEnabled=true(기본값): 도구 호출 반복 사이에 전체 대화 기록이 유지되고 LLM으로 전송되어, LLM이 전체 컨텍스트로 결과를 종합할 수 있어요
  • conversationHistoryEnabled=false: 가장 최근 도구 응답만 LLM으로 전송돼요(ChatMemory 어드바이저가 기록을 별도로 관리할 때 유용)

핵심 포인트: 대화 기록은 도구 자체가 아니라 LLM이 컨텍스트를 이해하고 응답을 구성하는 데 쓰여요. 도구는 자신의 입력 파라미터와 명시적으로 제공한 커스텀 컨텍스트만 받아요.

도구의 커스텀 컨텍스트: ToolContext는 애플리케이션 특화 데이터를 도구에 전달하는 용도로 계속 사용할 수 있어요:

ChatResponse response = chatClient.prompt()
    .user("What's the weather in SF?")
    .toolContext(Map.of("userId", "user123", "apiKey", "secret"))
    .call()
    .chatResponse();

예시 흐름:

  1. 사용자가 묻습니다: "SF와 LA의 날씨는 어때?"
  2. LLM이 도구 호출을 요청합니다: getWeather(SF)와 getWeather(LA)
  3. 도구는 자신의 파라미터만 가지고 실행됩니다(대화 기록 없음)
  4. ToolCallingAdvisor가 도구 결과와 대화 기록을 수집합니다
  5. LLM이 어드바이저로부터 대화 컨텍스트를 받아 종합합니다: "SF 날씨는 72°F이고 LA는 85°F야"

LLM은 ToolContext가 아니라 어드바이저 체인을 통해 전체 대화를 봅니다.

모델 내부 메서드의 접근 수준이 private으로 변경됨

  • 모든 모델 클래스의 internalCall과 internalStream 메서드가 private으로 변경됐어요.
영향 (Impact)
  • xxxModel.internalCall 또는 xxxModel.internalStream 메서드를 직접 호출하는 코드는 컴파일되지 않아요.
마이그레이션 방법 (Migration)
  • xxxModel.internalCall 호출을 모두 xxxModel.call로 교체해요.
  • xxxModel.internalStream 호출을 모두 xxxModel.stream으로 교체해요.
// Before
ChatResponse response = model.internalCall(prompt, previousChatResponse);
Flux<ChatResponse> responseFlux = model.internalStream(prompt, previousChatResponse);

// After
ChatResponse response = model.call(prompt);
Flux<ChatResponse> responseFlux = model.stream(prompt);

OpenSearch 의존성 업그레이드

OpenSearch 벡터 저장소 의존성이 더 새로운 버전으로 업그레이드됐어요:

  • OpenSearch Java Client: 2.23.0 → 3.6.0
  • OpenSearch Testcontainers: 2.0.1 → 4.1.0

배경 (Background)

이 업그레이드는 HttpClient5(org.apache.httpcomponents.client5:httpclient5) 버전 5.6을 사용하는 Spring Boot 4.1.x와의 호환성에 필요했어요. 이 HttpClient 버전에는 OpenSearch Java Client 업그레이드가 필요했던 gzip 콘텐츠 포맷터 호환성 변경이 있어요. 자세한 내용은 OpenSearch Java PR #1851을 참조해요.

영향 (Impact)

OpenSearch Java Client 3.x는 네이티브 OpenSearch 클라이언트와 직접 상호작용하는 커스텀 코드에 영향을 주는 호환성 API 변경을 도입해요.

마이그레이션 방법 (Migration)

Spring AI의 VectorStore 인터페이스를 통해 OpenSearch 벡터 저장소를 사용한다면 조치가 필요 없어요. 업그레이드는 투명해요. Testcontainers 클래스 이름 변경: OpensearchContainer → OpenSearchContainer(올바른 camelCase) Spring AI의 내부 구현은 이러한 변경을 자동으로 처리하도록 업데이트됐어요.

AbstractFilterExpressionConverter: doSingleValue가 이제 abstract

AbstractFilterExpressionConverter(벡터 저장소 필터 표현식 변환기가 사용)에서 메서드 doSingleValue(Object value, StringBuilder context)가 구체 메서드에서 abstract 메서드로 변경됐어요. AbstractFilterExpressionConverter를 확장하는 커스텀 벡터 저장소 구현은 이제 이 메서드를 명시적으로 구현해야 해요.

영향 (Impact)

  • AbstractFilterExpressionConverter를 확장하고 doSingleValue()를 오버라이드하지 않은 모든 커스텀 FilterExpressionConverter는 컴파일되지 않아요.
  • 구현은 단일 필터 값(String, Number, Boolean, Date 등)을 대상 형식으로 변환하고 제공된 StringBuilder 컨텍스트에 추가해야 해요.

마이그레이션 방법 (Migration)

커스텀 변환기에 doSingleValue(Object value, StringBuilder context)를 구현해요. 제공되는 정적 헬퍼 메서드를 사용할 수 있어요:

  • JSON 기반 필터(예: PostgreSQL JSONPath, Neo4j Cypher, Weaviate): emitJsonValue(Object value, StringBuilder context)를 사용해 적절한 따옴표와 이스케이프로 값을 직렬화해요.
  • Lucene 기반 필터(예: Elasticsearch, OpenSearch, GemFire): 문자열 값에는 emitLuceneString(String value, StringBuilder context)를 사용하고, 다른 타입(숫자, 불리언, 날짜)은 저장소의 쿼리 문법에 따라 처리해요.
  • 기타 형식: 자체 로직을 구현하고 결과를 context에 추가해요.

참고 프레임워크가 doSingleValue를 호출하기 전에 값을 정규화해요(예: ISO 날짜 문자열을 Date로 변환). 그래서 구현은 이미 정규화된 값을 받아요. 표준 흐름 밖에서 표현식을 만들 때 같은 정규화가 필요하다면 정적 헬퍼 normalizeDateString(Object)을 사용할 수 있어요.

MongoDB 채팅 메모리 메시지 순서 수정

MongoChatMemoryRepository가 메시지를 보낸 순서(오래된 것부터 최신)로 반환하도록 수정됐어요. 다른 모든 채팅 메모리 저장소 구현과 일치해요. 이전에는 메시지를 잘못된 역순(최신부터 오래된)으로 반환해서 LLM의 대화 흐름이 깨졌어요.

영향 (Impact)

MongoChatMemoryRepository를 사용하면서 잘못된 순서를 우회하고 있었다면(예: 검색 후 메시지 순서 뒤집기) 그 우회 코드를 제거해야 해요.

마이그레이션 방법 (Migration)

MongoDB 채팅 메모리에서 검색 후 메시지 순서를 뒤집는 코드를 제거해요:

// BEFORE (버그 우회 코드 포함):
List<Message> messages = chatMemoryRepository.findByConversationId(conversationId);
Collections.reverse(messages); // Remove this workaround

// AFTER (올바른 순서):
List<Message> messages = chatMemoryRepository.findByConversationId(conversationId);
// Messages are now correctly ordered chronologically

이제 모든 채팅 메모리 저장소가 메시지를 보낸 순서(오래된 것부터 최신)로 일관되게 반환해요. 이는 LLM 대화 기록의 예상되는 형식이에요.

개발 시 서비스 (Development-time Services)

  • MongoDB Atlas에 대한 Docker Compose와 Testcontainers 지원이 이제 Spring Boot MongoDB 모듈에서 네이티브로 제공돼요. 마이그레이션은 투명해야 하며 코드 변경이 필요 없어요. 의존성 관련해서는 더 이상 org.springframework.ai:spring-ai-spring-boot-testcontainers를 import할 필요가 없어요. org.springframework.boot:spring-boot-testcontainers에 대한 의존성이면 충분해요.

기본 온도 구성 제거

Spring AI가 더 이상 채팅 모델 자동구성 프로퍼티에 대한 기본 온도 값을 제공하지 않아요. 이전에는 Spring AI가 대부분의 채팅 모델에 기본 온도 0.7을 설정했어요. 이 기본값은 각 AI 프로바이더의 네이티브 기본 온도를 사용할 수 있도록 제거됐어요.

영향 (Impact)

온도 값을 명시적으로 구성하지 않고 Spring AI의 기본값 0.7에 의존하던 애플리케이션은 업그레이드 후 다른 동작을 볼 수 있어요. 실제 기본값은 이제 각 AI 프로바이더의 API가 결정하며, 다양할 수 있어요:

  • 일부 프로바이더는 기본값 1.0
  • 일부 프로바이더는 기본값 0.7
  • 일부 프로바이더는 모델별 기본값을 가짐

마이그레이션 방법 (Migration)

이전 동작을 유지하려면 구성에서 온도를 명시적으로 설정해요:

# OpenAI 예시
spring.ai.openai.chat.temperature=0.7

# Anthropic 예시
spring.ai.anthropic.chat.temperature=0.7

# Azure OpenAI 예시
spring.ai.azure.openai.chat.temperature=0.7

또는 요청을 만들 때 프로그래밍 방식으로:

ChatResponse response = chatModel.call(
    new Prompt("Your prompt here",
        OpenAiChatOptions.builder()
            .temperature(0.7)
            .build()));

1.1.0-RC1로 업그레이드하기 (Upgrading to 1.1.0-RC1)

호환성 변경 (Breaking Changes)

Text-to-Speech (TTS) API 마이그레이션

OpenAI Text-to-Speech 구현이 프로바이더 특화 클래스에서 공유 인터페이스로 마이그레이션됐어요. 이를 통해 여러 TTS 프로바이더(OpenAI, ElevenLabs, 그리고 미래의 프로바이더)에서 작동하는 이식 가능한 코드를 작성할 수 있어요.

제거된 클래스 (Removed Classes)

org.springframework.ai.openai.audio.speech 패키지에서 다음 deprecated 클래스가 제거됐어요:

  • SpeechModel → TextToSpeechModel(org.springframework.ai.audio.tts에서) 사용
  • StreamingSpeechModel → StreamingTextToSpeechModel(org.springframework.ai.audio.tts에서) 사용
  • SpeechPrompt → TextToSpeechPrompt(org.springframework.ai.audio.tts에서) 사용
  • SpeechResponse → TextToSpeechResponse(org.springframework.ai.audio.tts에서) 사용
  • SpeechMessage → TextToSpeechMessage(org.springframework.ai.audio.tts에서) 사용
  • Speech(org.springframework.ai.openai.audio.speech에 있음) → Speech(org.springframework.ai.audio.tts에서) 사용

추가로, speed 파라미터 타입이 모든 OpenAI TTS 컴포넌트에서 다른 TTS 프로바이더와의 일관성을 위해 Float에서 Double로 변경됐어요.

마이그레이션 단계 (Migration Steps)
  1. Import 업데이트: org.springframework.ai.openai.audio.speech.의 모든 import를 org.springframework.ai.audio.tts.로 교체해요.
  2. 타입 참조 업데이트: 옛 클래스 이름의 모든 발생을 새 이름으로 교체해요:
Find:    SpeechModel
Replace: TextToSpeechModel

Find:    StreamingSpeechModel
Replace: StreamingTextToSpeechModel

Find:    SpeechPrompt
Replace: TextToSpeechPrompt

Find:    SpeechResponse
Replace: TextToSpeechResponse

Find:    SpeechMessage
Replace: TextToSpeechMessage
  1. Speed 파라미터 업데이트: Float에서 Double로 변경해요:
Find:    .speed(1.0f)
Replace: .speed(1.0)

Find:    Float speed
Replace: Double speed
  1. 의존성 주입 업데이트: SpeechModel을 주입한다면 TextToSpeechModel로 업데이트해요:
// Before
public MyService(SpeechModel speechModel) { ... }

// After
public MyService(TextToSpeechModel textToSpeechModel) { ... }
이점 (Benefits)
  • 이식성: 한 번 코드를 작성하고 OpenAI, ElevenLabs 또는 다른 TTS 프로바이더 사이를 쉽게 전환
  • 일관성: ChatModel 및 다른 Spring AI 추상화와 동일한 패턴
  • 타입 안전성: 적절한 인터페이스 구현으로 향상된 타입 계층
  • 미래 대비: 새 TTS 프로바이더가 기존 코드와 자동으로 작동
추가 자료 (Additional Resources)

상세한 코드 예시가 있는 종합적 마이그레이션 가이드는 다음을 참조해요:

1.0.0-SNAPSHOT으로 업그레이드하기 (Upgrading to 1.0.0-SNAPSHOT)

개요 (Overview)

1.0.0-SNAPSHOT 버전은 아티팩트 ID, 패키지 이름, 모듈 구조에 상당한 변경을 포함해요. 이 섹션은 SNAPSHOT 버전 사용에 특화된 지침을 제공해요.

SNAPSHOT 저장소 추가

1.0.0-SNAPSHOT 버전을 사용하려면 빌드 파일에 snapshot 저장소를 추가해야 해요. 상세한 지침은 Getting Started 가이드의 Snapshots - Add Snapshot Repositories 섹션을 참조해요.

의존성 관리 업데이트

빌드 구성에서 Spring AI BOM 버전을 1.0.0-SNAPSHOT으로 업데이트해요. 의존성 관리 구성에 대한 상세한 지침은 Getting Started 가이드의 Dependency Management 섹션을 참조해요.

아티팩트 ID, 패키지, 모듈 변경

1.0.0-SNAPSHOT은 아티팩트 ID, 패키지 이름, 모듈 구조에 대한 변경을 포함해요. 자세한 내용은 다음을 참조해요:

1.0.0-RC1로 업그레이드하기 (Upgrading to 1.0.0-RC1)

OpenRewrite 레시피를 사용해 1.0.0-RC1로의 업그레이드 프로세스를 자동화할 수 있어요. 이 레시피는 이 버전에 필요한 많은 코드 변경을 적용하는 데 도움을 줘요. 레시피와 사용 지침은 Arconia Spring AI Migrations에서 찾을 수 있어요.

호환성 변경 (Breaking Changes)

Chat Client와 어드바이저

최종 사용자 코드에 영향을 주는 주요 변경은 다음과 같아요:

  • VectorStoreChatMemoryAdvisor에서:
    • 상수 CHAT_MEMORY_RETRIEVE_SIZE_KEY가 TOP_K로 이름 변경됐어요.
    • 상수 DEFAULT_CHAT_MEMORY_RESPONSE_SIZE(값: 100)가 DEFAULT_TOP_K로 이름 변경되고 새 기본값 20을 가져요.
    • 상수 CHAT_MEMORY_CONVERSATION_ID_KEY가 CONVERSATION_ID로 이름 변경되고 AbstractChatMemoryAdvisor에서 ChatMemory 인터페이스로 이동했어요. import를 org.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID를 사용하도록 업데이트해요.
어드바이저의 자체 포함 템플릿

프롬프트 증강을 수행하는 내장 어드바이더가 자체 포함(self-contained) 템플릿을 사용하도록 업데이트됐어요. 목표는 각 어드바이더가 다른 어드바이더의 템플릿과 프롬프트 결정에 영향을 주지도 받지도 않으면서 템플릿 작업을 수행할 수 있게 하는 것이에요.

다음 어드바이더에 커스텀 템플릿을 제공하고 있었다면, 기대되는 모든 플레이스홀더가 포함되도록 업데이트해야 해요.

  • QuestionAnswerAdvisor는 다음 플레이스홀더가 있는 템플릿을 기대해요(자세히):
    • 사용자 질문을 받는 query 플레이스홀더.
    • 검색된 컨텍스트를 받는 question_answer_context 플레이스홀더.
  • VectorStoreChatMemoryAdvisor는 다음 플레이스홀더가 있는 템플릿을 기대해요(자세히):
    • 원래 시스템 메시지를 받는 instructions 플레이스홀더.
    • 검색된 대화 메모리를 받는 long_term_memory 플레이스홀더.

관측성 (Observability)

  • 콘텐츠 관측을 tracing 대신 logging을 사용하도록 리팩터링 (ca843e8)
    • 콘텐츠 관측 필터를 logging 핸들러로 교체
    • 구성 프로퍼티를 목적을 더 잘 반영하도록 이름 변경:
      • include-prompt → log-prompt
      • include-completion → log-completion
      • include-query-response → log-query-response
    • trace 인식 logging을 위해 TracingAwareLoggingObservationHandler 추가
    • micrometer-tracing-bridge-otel을 micrometer-tracing으로 교체
    • 직접 logging을 선호해 이벤트 기반 tracing 제거
    • OTel SDK에 대한 직접 의존성 제거
    • 관측 프로퍼티에서 includePrompt를 logPrompt로 이름 변경 (ChatClientBuilderProperties, ChatObservationProperties, ImageObservationProperties에서)

채팅 메모리 저장소 모듈과 자동구성 이름 변경

코드베이스 전체에 repository 접미사를 추가해 채팅 메모리 컴포넌트의 명명 패턴을 표준화했어요. 이 변경은 Cassandra, JDBC, Neo4j 구현에 영향을 주며, 명확성을 위해 아티팩트 ID, Java 패키지 이름, 클래스 이름에 영향을 줍니다.

아티팩트 ID (Artifact IDs)

모든 메모리 관련 아티팩트가 이제 일관된 패턴을 따라요:

  • spring-ai-model-chat-memory- → spring-ai-model-chat-memory-repository-
  • spring-ai-autoconfigure-model-chat-memory- → spring-ai-autoconfigure-model-chat-memory-repository-
  • spring-ai-starter-model-chat-memory- → spring-ai-starter-model-chat-memory-repository-

Java 패키지 (Java Packages)

  • 패키지 경로가 이제 .repository. 세그먼트를 포함해요
  • 예시: org.springframework.ai.chat.memory.jdbc → org.springframework.ai.chat.memory.repository.jdbc

구성 클래스 (Configuration Classes)

  • 주요 자동구성 클래스가 이제 Repository 접미사를 사용해요
  • 예시: JdbcChatMemoryAutoConfiguration → JdbcChatMemoryRepositoryAutoConfiguration

프로퍼티 (Properties)

  • 구성 프로퍼티가 spring.ai.chat.memory.<storage>…에서 spring.ai.chat.memory.repository.<storage>…으로 이름 변경됐어요

필요한 마이그레이션:

  • Maven/Gradle 의존성을 새 아티팩트 ID를 사용하도록 업데이트해요.
  • 옛 패키지나 클래스 이름을 사용한 import, 클래스 참조, 구성을 업데이트해요.

메시지 애그리게이터 리팩터링

변경사항 (Changes)
  • MessageAggregator 클래스가 spring-ai-client-chat 모듈의 org.springframework.ai.chat.model 패키지에서 spring-ai-model 모듈(같은 패키지 이름)로 이동했어요
  • aggregateChatClientResponse 메서드가 MessageAggregator에서 제거되고 org.springframework.ai.chat.client 패키지의 새 클래스 ChatClientMessageAggregator로 이동했어요
마이그레이션 가이드

MessageAggregator의 aggregateChatClientResponse 메서드를 직접 사용하고 있었다면, 대신 새 ChatClientMessageAggregator 클래스를 사용해야 해요:

// Before
new MessageAggregator().aggregateChatClientResponse(chatClientResponses, aggregationHandler);

// After
new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, aggregationHandler);

적절한 import를 추가하는 것을 잊지 마세요:

import org.springframework.ai.chat.client.ChatClientMessageAggregator;

Watson

Watson AI 모델이 제거됐어요. 더 새로운 채팅 생성 모델이 있으므로 구식으로 간주되는 이전 텍스트 생성 기반이었기 때문이에요. Watson이 미래 Spring AI 버전에 다시 나타나길 바랍니다.

MoonShot와 QianFan

Moonshot과 Qianfan은 중국 밖에서 접근할 수 없어 제거됐어요. 이들은 Spring AI 커뮤니티 저장소로 이동했어요.

제거된 벡터 저장소 (Removed Vector Store)

  • HanaDB 벡터 저장소 자동구성 제거 (f3b4624)

메모리 관리 (Memory Management)

  • CassandraChatMemory 구현 제거 (11e3c8f)
  • 채팅 메모리 어드바이저 계층 단순화 및 deprecated API 제거 (848a3fd)
  • JdbcChatMemory의 deprecation 제거 (356a68f)
  • 명확성을 위해 채팅 메모리 저장소 아티팩트 리팩터링 (2d517ee)
  • 명확성을 위해 채팅 메모리 저장소 자동구성과 Spring Boot 스타터 리팩터링 (f6dba1b)

메시지와 템플릿 API (Message and Template APIs)

  • deprecated UserMessage 생성자 제거 (06edee4)
  • deprecated PromptTemplate 생성자 제거 (722c77e)
  • Media의 deprecated 메서드 제거 (228ef10)
  • StTemplateRenderer 리팩터링: supportStFunctions를 validateStFunctions로 이름 변경 (0e15197)
  • 이동 후 남은 TemplateRender 인터페이스 제거 (52675d8)

추가 클라이언트 API 변경 (Additional Client API Changes)

  • ChatClient와 어드바이더의 deprecation 제거 (4fe74d8)
  • OllamaApi와 AnthropicApi의 deprecation 제거 (46be898)

패키지 구조 변경 (Package Structure Changes)

  • spring-ai-model의 패키지 간 의존성 순환 제거 (ebfa5b9)
  • MessageAggregator를 spring-ai-model 모듈로 이동 (54e5c07)

의존성 (Dependencies)

  • spring-ai-openai에서 사용하지 않는 json-path 의존성 제거 (9de13d1)

동작 변경 (Behavior Changes)

Azure OpenAI

  • 깔끔한 자동구성으로 Azure OpenAI에 Entra ID 자격 관리 추가 (3dc86d3)

일반 정리 (General Cleanup)

1.0.0-M8로 업그레이드하기 (Upgrading to 1.0.0-M8)

OpenRewrite 레시피를 사용해 1.0.0-M8로의 업그레이드 프로세스를 자동화할 수 있어요. 이 레시피는 이 버전에 필요한 많은 코드 변경을 적용하는 데 도움을 줘요. 레시피와 사용 지침은 Arconia Spring AI Migrations에서 찾을 수 있어요.

호환성 변경 (Breaking Changes)

Spring AI 1.0 M7에서 1.0 M8로 업그레이드할 때, 이전에 도구 콜백을 등록하던 사용자에게 도구 호출 기능이 조용히 실패하는 호환성 변경이 발생해요. 이는 특히 deprecated tools() 메서드를 사용하는 코드에 영향을 줍니다.

예시 (Example)

M7에서 동작했지만 M8에서 더 이상 예상대로 동작하지 않는 코드 예시입니다:

// This worked in M7 but silently fails in M8
ChatClient chatClient = new OpenAiChatClient(api)
    .tools(List.of(
        new Tool("get_current_weather", "Get the current weather in a given location",
            new ToolSpecification.ToolParameter("location", "The city and state, e.g. San Francisco, CA", true))
    ))
    .toolCallbacks(List.of(
        new ToolCallback("get_current_weather", (toolName, params) -> {
            // Weather retrieval logic
            return Map.of("temperature", 72, "unit", "fahrenheit", "description", "Sunny");
        })
    ));

해결책 (Solution)

해결책은 deprecated tools() 메서드 대신 toolSpecifications() 메서드를 사용하는 것입니다:

// This works in M8
ChatClient chatClient = new OpenAiChatClient(api)
    .toolSpecifications(List.of(
        new Tool("get_current_weather", "Get the current weather in a given location",
            new ToolSpecification.ToolParameter("location", "The city and state, e.g. San Francisco, CA", true))
    ))
    .toolCallbacks(List.of(
        new ToolCallback("get_current_weather", (toolName, params) -> {
            // Weather retrieval logic
            return Map.of("temperature", 72, "unit", "fahrenheit", "description", "Sunny");
        })
    ));

제거된 구현과 API (Removed Implementations and APIs)

메모리 관리 (Memory Management)

  • CassandraChatMemory 구현 제거 (11e3c8f)
  • 채팅 메모리 어드바이저 계층 단순화 및 deprecated API 제거 (848a3fd)
  • JdbcChatMemory의 deprecation 제거 (356a68f)
  • 명확성을 위해 채팅 메모리 저장소 아티팩트 리팩터링 (2d517ee)
  • 명확성을 위해 채팅 메모리 저장소 자동구성과 Spring Boot 스타터 리팩터링 (f6dba1b)

클라이언트 API (Client APIs)

  • ChatClient와 어드바이더의 deprecation 제거 (4fe74d8)
  • chatclient 도구 호출의 호환성 변경 (5b7849d)
  • OllamaApi와 AnthropicApi의 deprecation 제거 (46be898)

메시지와 템플릿 API (Message and Template APIs)

  • deprecated UserMessage 생성자 제거 (06edee4)
  • deprecated PromptTemplate 생성자 제거 (722c77e)
  • Media의 deprecated 메서드 제거 (228ef10)
  • StTemplateRenderer 리팩터링: supportStFunctions를 validateStFunctions로 이름 변경 (0e15197)
  • 이동 후 남은 TemplateRender 인터페이스 제거 (52675d8)

모델 구현 (Model Implementations)

  • Watson 텍스트 생성 모델 제거 (9e71b16)
  • Qianfan 코드 제거 (bfcaad7)
  • HanaDB 벡터 저장소 자동구성 제거 (f3b4624)
  • OpenAiApi에서 deepseek 옵션 제거 (59b36d1)

패키지 구조 변경 (Package Structure Changes)

  • spring-ai-model의 패키지 간 의존성 순환 제거 (ebfa5b9)
  • MessageAggregator를 spring-ai-model 모듈로 이동 (54e5c07)

의존성 (Dependencies)

  • spring-ai-openai에서 사용하지 않는 json-path 의존성 제거 (9de13d1)

동작 변경 (Behavior Changes)

관측성 (Observability)

  • 콘텐츠 관측을 tracing 대신 logging을 사용하도록 리팩터링 (ca843e8)
    • 콘텐츠 관측 필터를 logging 핸들러로 교체
    • 구성 프로퍼티를 목적을 더 잘 반영하도록 이름 변경:
      • include-prompt → log-prompt
      • include-completion → log-completion
      • include-query-response → log-query-response
    • trace 인식 logging을 위해 TracingAwareLoggingObservationHandler 추가
    • micrometer-tracing-bridge-otel을 micrometer-tracing으로 교체
    • 직접 logging을 선호해 이벤트 기반 tracing 제거
    • OTel SDK에 대한 직접 의존성 제거
    • 관측 프로퍼티에서 includePrompt를 logPrompt로 이름 변경 (ChatClientBuilderProperties, ChatObservationProperties, ImageObservationProperties에서)

Azure OpenAI

  • 깔끔한 자동구성으로 Azure OpenAI에 Entra ID 자격 관리 추가 (3dc86d3)

일반 정리 (General Cleanup)

  • 1.0.0-M8의 모든 deprecation 제거 (76bee8c)
  • 일반 deprecation 정리 (b6ce7f3)

1.0.0-M7로 업그레이드하기 (Upgrading to 1.0.0-M7)

변경 개요 (Overview of Changes)

Spring AI 1.0.0-M7은 RC1과 GA 릴리스 전의 마지막 마일스톤 릴리스예요. 최종 릴리스에서 유지될 아티팩트 ID, 패키지 이름, 모듈 구조에 몇 가지 중요한 변경을 도입해요.

아티팩트 ID, 패키지, 모듈 변경

1.0.0-M7은 1.0.0-SNAPSHOT과 동일한 구조 변경을 포함해요. 자세한 내용은 다음을 참조해요:

MCP Java SDK를 0.9.0으로 업그레이드

Spring AI 1.0.0-M7이 이제 MCP Java SDK 버전 0.9.0을 사용하는데, 여기에는 이전 버전의 상당한 변경이 포함돼요. 애플리케이션에서 MCP를 사용한다면 이러한 변경을 수용하도록 코드를 업데이트해야 해요. 주요 변경 사항:

인터페이스 이름 변경

  • ClientMcpTransport → McpClientTransport
  • ServerMcpTransport → McpServerTransport
  • DefaultMcpSession → McpClientSession 또는 McpServerSession
  • 모든 *Registration 클래스 → *Specification 클래스

서버 생성 변경

  • ServerMcpTransport 대신 McpServerTransportProvider를 사용해요
// Before
ServerMcpTransport transport = new WebFluxSseServerTransport(objectMapper, "/mcp/message");
var server = McpServer.sync(transport)
    .serverInfo("my-server", "1.0.0")
    .build();

// After
McpServerTransportProvider transportProvider = new WebFluxSseServerTransportProvider(objectMapper, "/mcp/message");
var server = McpServer.sync(transportProvider)
    .serverInfo("my-server", "1.0.0")
    .build();

핸들러 시그니처 변경

모든 핸들러가 이제 첫 번째 인자로 exchange 파라미터를 받아요:

// Before
.tool(calculatorTool, args -> new CallToolResult("Result: " + calculate(args)))

// After
.tool(calculatorTool, (exchange, args) -> new CallToolResult("Result: " + calculate(args)))

exchange를 통한 클라이언트 상호작용

이전에 서버에서 사용할 수 있던 메서드가 이제 exchange 객체를 통해 접근돼요:

// Before
ClientCapabilities capabilities = server.getClientCapabilities();
CreateMessageResult result = server.createMessage(new CreateMessageRequest(...));

// After
ClientCapabilities capabilities = exchange.getClientCapabilities();
CreateMessageResult result = exchange.createMessage(new CreateMessageRequest(...));

루트 변경 핸들러

// Before
.rootsChangeConsumers(List.of(
    roots -> System.out.println("Roots changed: " + roots)
))

// After
.rootsChangeHandlers(List.of(
    (exchange, roots) -> System.out.println("Roots changed: " + roots)
))

MCP 코드 마이그레이션에 대한 완전한 가이드는 MCP 마이그레이션 가이드를 참조해요.

모델 자동구성 활성화/비활성화

모델 자동구성을 활성화/비활성화하는 이전 구성 프로퍼티가 제거됐어요:

  • spring.ai.<provider>.chat.enabled
  • spring.ai.<provider>.embedding.enabled
  • spring.ai.<provider>.image.enabled
  • spring.ai.<provider>.moderation.enabled

기본적으로 모델 프로바이더(예: OpenAI, Ollama)가 클래스패스에서 발견되면 관련 모델 타입(chat, embedding 등)에 대한 해당 자동구성이 활성화돼요. 같은 모델 타입에 여러 프로바이더가 있으면(예: spring-ai-openai-spring-boot-starter와 spring-ai-ollama-spring-boot-starter 둘 다), 다음 프로퍼티를 사용해 어느 프로바이더의 자동구성이 활성화될지 선택할 수 있고, 그 특정 모델 타입에 대해 다른 프로바이더를 효과적으로 비활성화해요. 프로바이더가 하나만 있어도 특정 모델 타입에 대해 자동구성을 완전히 비활성화하려면, 해당 프로퍼티를 클래스패스의 어떤 프로바이더와도 일치하지 않는 값(예: none 또는 disabled)으로 설정해요. 잘 알려진 프로바이더 값 목록은 SpringAIModels 열거형을 참조할 수 있어요.

  • spring.ai.model.audio.speech=<model-provider|none>
  • spring.ai.model.audio.transcription=<model-provider|none>
  • spring.ai.model.chat=<model-provider|none>
  • spring.ai.model.embedding=<model-provider|none>
  • spring.ai.model.embedding.multimodal=<model-provider|none>
  • spring.ai.model.embedding.text=<model-provider|none>
  • spring.ai.model.image=<model-provider|none>
  • spring.ai.model.moderation=<model-provider|none>

AI를 사용한 업그레이드 자동화

Claude Code CLI 도구와 제공된 프롬프트로 1.0.0-M7로의 업그레이드 프로세스를 자동화할 수 있어요:

  1. Claude Code CLI 도구를 다운로드해요
  2. update-to-m7.txt 파일에서 프롬프트를 복사해요
  3. 프롬프트를 Claude Code CLI에 붙여넣어요
  4. AI가 프로젝트를 분석하고 필요한 변경을 합니다

참고 자동화된 업그레이드 프롬프트는 현재 아티팩트 ID 변경, 패키지 재배치, 모듈 구조 변경을 처리하지만, MCP 0.9.0으로의 업그레이드에 대한 자동 변경은 아직 포함하지 않아요. MCP를 사용한다면 MCP Java SDK 업그레이드 섹션의 지침에 따라 코드를 수동으로 업데이트해야 해요.

버전에 걸친 공통 변경 (Common Changes Across Versions)

아티팩트 ID 변경 (Artifact ID Changes)

Spring AI 스타터 아티팩트의 명명 패턴이 변경됐어요. 다음 패턴에 따라 의존성을 업데이트해야 해요:

  • 모델 스타터: spring-ai-{model}-spring-boot-starter → spring-ai-starter-model-{model}
  • 벡터 저장소 스타터: spring-ai-{store}-store-spring-boot-starter → spring-ai-starter-vector-store-{store}
  • MCP 스타터: spring-ai-mcp-{type}-spring-boot-starter → spring-ai-starter-mcp-{type}

예시 (Examples)

  • Maven
  • Gradle
<!-- BEFORE -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

<!-- AFTER -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
// BEFORE
implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
implementation 'org.springframework.ai:spring-ai-redis-store-spring-boot-starter'

// AFTER
implementation 'org.springframework.ai:spring-ai-starter-model-openai'
implementation 'org.springframework.ai:spring-ai-starter-vector-store-redis'

Spring AI 자동구성 아티팩트 변경

Spring AI 자동구성이 단일 모놀리식 아티팩트에서 모델, 벡터 저장소 및 기타 컴포넌트별 개별 자동구성 아티팩트로 변경됐어요. 이 변경은 Google Protocol Buffers, Google RPC 등과 같은 의존 라이브러리의 서로 다른 버전이 충돌하는 영향을 최소화하기 위해 이루어졌어요. 자동구성을 컴포넌트별 아티팩트로 분리함으로써 불필요한 의존성을 끌어들이는 것을 피하고 애플리케이션의 버전 충돌 위험을 줄일 수 있어요. 원래 모놀리식 아티팩트는 더 이상 사용할 수 없어요:

<!-- NO LONGER AVAILABLE -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-spring-boot-autoconfigure</artifactId>
    <version>${project.version}</version>
</dependency>

대신 각 컴포넌트가 이제 다음 패턴을 따르는 자체 자동구성 아티팩트를 가져요:

  • 모델 자동구성: spring-ai-autoconfigure-model-{model}
  • 벡터 저장소 자동구성: spring-ai-autoconfigure-vector-store-{store}
  • MCP 자동구성: spring-ai-autoconfigure-mcp-{type}

새 자동구성 아티팩트 예시

  • Models
  • Vector Stores
  • MCP
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-model-openai</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-model-anthropic</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-vector-store-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-vector-store-pgvector</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-vector-store-chroma</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-mcp-client</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-autoconfigure-mcp-server</artifactId>
</dependency>

참고 대부분의 경우 이러한 자동구성 의존성을 명시적으로 추가할 필요는 없어요. 해당 스타터 의존성을 사용할 때 전이적으로 포함되거든요.

패키지 이름 변경 (Package Name Changes)

IDE가 새 패키지 위치로의 리팩터링을 도와줄 거예요.

  • KeywordMetadataEnricher와 SummaryMetadataEnricher가 org.springframework.ai.transformer에서 org.springframework.ai.chat.transformer로 이동했어요.
  • Content, MediaContent, Media가 org.springframework.ai.model에서 org.springframework.ai.content로 이동했어요.

모듈 구조 (Module Structure)

프로젝트가 모듈과 아티팩트 구조에 상당한 변경을 겪었어요. 이전에는 spring-ai-core가 모든 중앙 인터페이스를 포함했지만, 이제 애플리케이션의 불필요한 의존성을 줄이기 위해 특화된 도메인 모듈로 분리됐어요.

spring-ai-commons

다른 Spring AI 모듈에 대한 의존성이 없는 기본 모듈. 포함:

  • 핵심 도메인 모델(Document, TextSplitter)
  • JSON 유틸리티와 리소스 처리
  • 구조화된 로깅과 관측성 지원

spring-ai-model

AI 능력 추상화 제공:

  • ChatModel, EmbeddingModel, ImageModel 같은 인터페이스
  • 메시지 타입과 프롬프트 템플릿
  • 함수 호출 프레임워크(ToolDefinition, ToolCallback)
  • 콘텐츠 필터링과 관측 지원

spring-ai-vector-store

통합 벡터 데이터베이스 추상화:

  • 유사도 검색을 위한 VectorStore 인터페이스
  • SQL 유사 표현식으로 고급 필터링
  • 인메모리 사용을 위한 SimpleVectorStore
  • 임베딩 배치 지원

spring-ai-client-chat

고수준 대화형 AI API:

  • ChatClient 인터페이스
  • ChatMemory로 대화 영속화
  • OutputConverter로 응답 변환
  • 어드바이저 기반 인터셉션
  • 동기 및 리액티브 스트리밍 지원

spring-ai-vector-store-advisor

RAG를 위해 채팅과 벡터 저장소를 연결:

  • QuestionAnswerAdvisor: 프롬프트에 컨텍스트 주입
  • VectorStoreChatMemoryAdvisor: 대화 기록 저장/검색

spring-ai-model-chat-memory-cassandra

ChatMemory를 위한 Apache Cassandra 영속화:

  • CassandraChatMemory 구현
  • Cassandra QueryBuilder로 타입 안전한 CQL

==== spring-ai-model-chat-memory-neo4j 채팅 대화를 위한 Neo4j 그래프 데이터베이스 영속화.

spring-ai-rag

Retrieval Augmented Generation을 위한 종합 프레임워크:

  • RAG 파이프라인을 위한 모듈형 아키텍처
  • 주요 진입점으로서의 RetrievalAugmentationAdvisor
  • 구성 가능한 컴포넌트를 사용한 함수형 프로그래밍 원칙

의존성 구조 (Dependency Structure)

의존성 계층을 요약하면 다음과 같아요:

  • spring-ai-commons(기초)
  • spring-ai-model(commons에 의존)
  • spring-ai-vector-store와 spring-ai-client-chat(둘 다 model에 의존)
  • spring-ai-vector-store-advisor와 spring-ai-rag(client-chat과 vector-store 모두에 의존)
  • spring-ai-model-chat-memory-* 모듈(client-chat에 의존)

ToolContext 변경

ToolContext 클래스가 명시적 및 암시적 도구 해석을 모두 지원하도록 향상됐어요. 이제 도구는 다음일 수 있습니다:

  1. 명시적으로 포함(Explicitly Included): 프롬프트에서 명시적으로 요청되고 모델을 호출할 때 포함되는 도구.
  2. 암시적으로 사용 가능(Implicitly Available): 런타임 동적 해석을 위해 사용 가능하지만, 명시적으로 요청되지 않는 한 모델을 호출할 때 절대 포함되지 않는 도구.

1.0.0-M7부터 도구는 프롬프트에서 명시적으로 요청되거나 호출에 명시적으로 포함된 경우에만 모델을 호출할 때 포함돼요. 추가로 ToolContext 클래스가 이제 final로 표시되어 더 이상 확장할 수 없어요. 서브클래싱할 의도가 애초에 없었거든요. ToolContext를 인스턴스화할 때 Map<String, Object> 형태로 필요한 모든 컨텍스트 데이터를 추가할 수 있어요. 자세한 내용은 documentation을 확인해요.

1.0.0-M6으로 업그레이드하기 (Upgrading to 1.0.0-M6)

Usage 인터페이스와 DefaultUsage 구현 변경

Usage 인터페이스와 그 기본 구현 DefaultUsage에 다음 변경이 발생했어요:

  1. 메서드 이름 변경:
    • getGenerationTokens()가 이제 getCompletionTokens()예요
  2. 타입 변경:
    • DefaultUsage의 모든 토큰 수 필드가 Long에서 Integer로 변경됐어요:
      • promptTokens
      • completionTokens(이전 generationTokens)
      • totalTokens

필요한 작업 (Required Actions)

  • getGenerationTokens()에 대한 모든 호출을 getCompletionTokens()으로 교체해요
  • DefaultUsage 생성자 호출을 업데이트해요:
// Old (M5)
new DefaultUsage(Long promptTokens, Long generationTokens, Long totalTokens)

// New (M6)
new DefaultUsage(Integer promptTokens, Integer completionTokens, Integer totalTokens)

참고 Usage 처리에 대한 더 많은 정보는 여기를 참조해요

JSON 직렬화/역직렬화 변경

M6은 generationTokens 필드의 JSON 역직렬화에 대해 하위 호환성을 유지하지만, 이 필드는 M7에서 제거될 예정이에요. 옛 필드 이름을 사용하는 영속된 JSON 문서는 completionTokens를 사용하도록 업데이트해야 해요. 새 JSON 형식 예시:

{
  "promptTokens": 100,
  "completionTokens": 50,
  "totalTokens": 150
}

도구 호출을 위한 FunctionCallingOptions 사용 변경

각 ChatModel 인스턴스는 구성 시점에 선택적 ChatOptions 또는 FunctionCallingOptions 인스턴스를 받아, 모델을 호출할 때 사용되는 기본 도구를 구성하는 데 사용할 수 있어요. 1.0.0-M6 이전:

  • 기본 FunctionCallingOptions 인스턴스의 functions() 메서드로 전달된 모든 도구는 해당 ChatModel 인스턴스에서의 각 모델 호출에 포함됐고, 런타임 옵션에 의해 덮어써질 수 있었어요.
  • 기본 FunctionCallingOptions 인스턴스의 functionCallbacks() 메서드로 전달된 모든 도구는 런타임 동적 해석(참조: 도구 해석)에만 사용 가능했고, 명시적으로 요청되지 않는 한 어떤 모델 호출에도 포함되지 않았어요. 1.0.0-M6부터:
  • 기본 FunctionCallingOptions 인스턴스의 functions() 메서드나 functionCallbacks() 메서드로 전달된 모든 도구는 이제 같은 방식으로 처리돼요: 해당 ChatModel 인스턴스에서의 각 모델 호출에 포함되고, 런타임 옵션에 의해 덮어써질 수 있어요. 이로써 도구가 모델 호출에 포함되는 방식에 일관성이 생기고 functionCallbacks()와 다른 모든 옵션 사이의 동작 차이로 인한 혼란을 방지해요. 도구를 런타임 동적 해석에 사용 가능하게 하면서 명시적으로 요청된 경우에만 요청에 포함하고 싶다면, 도구 해석에 설명된 전략 중 하나를 사용할 수 있어요.

참고 1.0.0-M6은 도구 호출 처리를 위한 새 API를 도입했어요. 옛 API에 대한 하위 호환성은 위에서 설명한 하나를 제외한 모든 시나리오에서 유지돼요. 옛 API는 여전히 사용 가능하지만, deprecated이며 1.0.0-M7에서 제거될 예정이에요.

deprecated Amazon Bedrock 채팅 모델 제거

1.0.0-M6부터 Spring AI는 Spring AI의 모든 Chat 대화 구현에 Amazon Bedrock의 Converse API를 사용하도록 전환했어요. Cohere와 Titan의 Embedding 모델을 제외한 모든 Amazon Bedrock Chat 모델이 제거됐어요.

참고 채팅 모델 사용에 대해서는 Bedrock Converse 문서를 참조해요.

의존성 관리에 Spring Boot 3.4.2 사용 변경

Spring AI가 의존성 관리에 Spring Boot 3.4.2를 사용하도록 업데이트해요. Spring Boot 3.4.2가 관리하는 의존성은 여기를 참조할 수 있어요.

필요한 작업 (Required Actions)

  • Spring Boot 3.4.2로 업그레이드한다면 REST Client 구성에 필요한 변경에 대해 이 문서를 참조하세요. 특히 클래스패스에 HTTP 클라이언트 라이브러리가 없으면 이전에 SimpleClientHttpRequestFactory가 사용되던 곳에 JdkClientHttpRequestFactory가 사용될 가능성이 높아요. SimpleClientHttpRequestFactory를 사용하려면 spring.http.client.factory=simple을 설정해야 해요.
  • 다른 Spring Boot 버전(예: Spring Boot 3.3.x)을 사용 중이고 특정 의존성 버전이 필요하다면 빌드 구성에서 오버라이드할 수 있어요.

Vector Store API 변경

버전 1.0.0-M6에서 VectorStore 인터페이스의 delete 메서드가 Optional<Boolean>을 반환하는 대신 void 연산이 되도록 수정됐어요. 이전에 delete 연산의 반환 값을 확인했다면 이 확인을 제거해야 해요. 이제 삭제가 실패하면 예외를 던져 더 직접적인 오류 처리를 제공해요.

1.0.0-M6 이전:

Optional<Boolean> result = vectorStore.delete(ids);
if (result.isPresent() && result.get()) {
    // handle successful deletion
}

1.0.0-M6 이후:

vectorStore.delete(ids);
// deletion successful if no exception is thrown

1.0.0.M5로 업그레이드하기 (Upgrading to 1.0.0.M5)

  • 벡터 빌더가 일관성을 위해 리팩터링됐어요.
  • 현재 VectorStore 구현 생성자가 deprecated됐고, 빌더 패턴을 사용해요.
  • VectorStore 구현 패키지가 독특한 패키지 이름으로 이동해 아티팩트 간 충돌을 피했어요. 예를 들어 org.springframework.ai.vectorstore에서 org.springframework.ai.pgvector.vectorstore로.

1.0.0.RC3으로 업그레이드하기 (Upgrading to 1.0.0.RC3)

  • 이식 가능한 채팅 옵션(frequencyPenalty, presencePenalty, temperature, topP)의 타입이 Float에서 Double로 변경됐어요.

1.0.0.M2로 업그레이드하기 (Upgrading to 1.0.0.M2)

  • Chroma Vector Store의 구성 접두사가 다른 벡터 저장소의 명명 규칙에 맞추기 위해 spring.ai.vectorstore.chroma.store에서 spring.ai.vectorstore.chroma로 변경됐어요.
  • 스키마를 초기화할 수 있는 벡터 저장소의 initialize-schema 프로퍼티 기본값이 이제 false로 설정돼요. 이것은 애플리케이션이 이제 지원되는 벡터 저장소에서 스키마 초기화를 명시적으로 선택해야 한다는 뜻이에요. (스키마가 애플리케이션 시작 시 생성될 것으로 기대된다면) 모든 벡터 저장소가 이 프로퍼티를 지원하는 건 아니에요. 자세한 내용은 해당 벡터 저장소 문서를 참조해요. 현재 initialize-schema 프로퍼티를 지원하지 않는 벡터 저장소는 다음과 같아요.

Pinecone

Weaviate

  • Bedrock Jurassic 2에서 채팅 옵션 countPenalty, frequencyPenalty, presencePenalty가 countPenaltyOptions, frequencyPenaltyOptions, presencePenaltyOptions로 이름 변경됐어요. 또한, 채팅 옵션 stopSequences의 타입이 String[]에서 List<String>으로 변경됐어요.
  • Azure OpenAI에서 채팅 옵션 frequencyPenalty와 presencePenalty의 타입이 다른 모든 구현과 일관성 있게 Double에서 Float으로 변경됐어요.

1.0.0.M1으로 업그레이드하기 (Upgrading to 1.0.0.M1)

1.0.0 M1 릴리스를 향한 우리의 행진에서 여러 호환성 변경을 만들었어요. 사과드립니다, 최선을 위한 것입니다!

ChatClient 변경

'옛' ChatClient를 가져와 그 기능을 ChatModel로 옮기는 주요 변경이 있었어요. '새로운' ChatClient는 이제 ChatModel 인스턴스를 받아요. 이는 RestClient, WebClient, JdbcClient 같은 Spring 생태계의 다른 클라이언트 클래스와 유사한 스타일로 프롬프트를 생성하고 실행하기 위한 Fluent API를 지원하기 위해 이루어졌어요. Fluent API에 대한 자세한 내용은 JavaDoc을 참조해요. 정식 레퍼런스 문서는 곧 제공될 예정이에요. '옛' ModelClient를 Model로 이름 변경하고 구현 클래스도 이름을 변경했어요. 예를 들어 ImageClient는 ImageModel로 이름 변경됐어요. Model 구현은 Spring AI API와 기본 AI Model API 사이를 변환하는 이식성 계층을 나타내요. 모든 입력/출력 데이터 타입 조합에 대해 AI 모델 클라이언트를 만드는 것을 지원하는 인터페이스와 기본 클래스를 포함하는 새 패키지 model이 추가됐어요. 현재 chat과 image 모델 패키지가 이를 구현해요. 곧 embedding 패키지도 이 새 모델로 업데이트할 예정이에요. 새로운 "portable options" 설계 패턴. 서로 다른 채팅 기반 AI 모델에 걸쳐 ModelCall에서 최대한 많은 이식성을 제공하고 싶었어요. 공통 생성 옵션 세트와 모델 프로바이더 특화 옵션이 있어요. 일종의 "duck typing" 접근이 사용돼요. model 패키지의 ModelOptions는 이 클래스의 구현이 모델에 대한 옵션을 제공할 것임을 나타내는 마커 인터페이스예요. 모든 text→image ImageModel 구현에 걸쳐 이식 가능한 옵션을 정의하는 하위 인터페이스 ImageOptions를 참조해요. 그 다음 StabilityAiImageOptions와 OpenAiImageOptions가 각 모델 프로바이더에 특화된 옵션을 제공해요. 모든 옵션 클래스는 fluent API 빌더로 생성되며, 모두 이식 가능한 ImageModel API에 전달될 수 있어요. 이러한 옵션 데이터 타입은 ImageModel 구현의 자동구성/구성 프로퍼티에 사용돼요.

아티팩트 이름 변경

POM 아티팩트 이름 변경:

  • spring-ai-qdrant → spring-ai-qdrant-store
  • spring-ai-cassandra → spring-ai-cassandra-store
  • spring-ai-pinecone → spring-ai-pinecone-store
  • spring-ai-redis → spring-ai-redis-store
  • spring-ai-qdrant → spring-ai-qdrant-store
  • spring-ai-gemfire → spring-ai-gemfire-store
  • spring-ai-azure-vector-store-spring-boot-starter → spring-ai-azure-store-spring-boot-starter
  • spring-ai-redis-spring-boot-starter → spring-ai-starter-vector-store-redis

0.8.1로 업그레이드하기 (Upgrading to 0.8.1)

기존 spring-ai-vertex-ai가 spring-ai-vertex-ai-palm2로 이름 변경됐고, spring-ai-vertex-ai-spring-boot-starter가 spring-ai-vertex-ai-palm2-spring-boot-starter로 이름 변경됐어요. 그러므로 의존성을 다음에서 변경해야 해요

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-vertex-ai</artifactId>
</dependency>

다음으로

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-vertex-ai-palm2</artifactId>
</dependency>

그리고 Palm2 모델의 관련 Boot 스타터가 다음에서 변경됐어요

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-vertex-ai-spring-boot-starter</artifactId>
</dependency>

다음으로

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-vertex-ai-palm2-spring-boot-starter</artifactId>
</dependency>
  • 클래스 이름 변경(2024.03.01)
    • VertexAiApi → VertexAiPalm2Api
    • VertexAiClientChat → VertexAiPalm2ChatClient
    • VertexAiEmbeddingClient → VertexAiPalm2EmbeddingClient
    • VertexAiChatOptions → VertexAiPalm2ChatOptions

0.8.0으로 업그레이드하기 (Upgrading to 0.8.0)

2024년 1월 24일 업데이트

  • prompt, messages, metadata 패키지를 org.springframework.ai.chat의 하위 패키지로 이동
  • 새 기능은 text to image 클라이언트예요. 클래스는 OpenAiImageModel과 StabilityAiImageModel. 사용법은 통합 테스트를 참조하고, 문서는 곧 제공될 예정이에요.
  • 모든 입력/출력 데이터 타입 조합에 대해 AI 모델 클라이언트를 만드는 것을 지원하는 인터페이스와 기본 클래스를 포함하는 새 패키지 model이 추가됐어요. 현재 chat과 image 모델 패키지가 이를 구현해요. 곧 embedding 패키지도 이 새 모델로 업데이트할 예정이에요.
  • 새로운 "portable options" 설계 패턴. 서로 다른 채팅 기반 AI 모델에 걸쳐 ModelCall에서 최대한 많은 이식성을 제공하고 싶었어요. 공통 생성 옵션 세트와 모델 프로바이더 특화 옵션이 있어요. 일종의 "duck typing" 접근이 사용돼요. model 패키지의 ModelOptions는 이 클래스의 구현이 모델에 대한 옵션을 제공할 것임을 나타내는 마커 인터페이스예요. 모든 text→image ImageModel 구현에 걸쳐 이식 가능한 옵션을 정의하는 하위 인터페이스 ImageOptions를 참조해요. 그 다음 StabilityAiImageOptions와 OpenAiImageOptions가 각 모델 프로바이더에 특화된 옵션을 제공해요. 모든 옵션 클래스는 fluent API 빌더로 생성되며, 모두 이식 가능한 ImageModel API에 전달될 수 있어요. 이러한 옵션 데이터 타입은 ImageModel 구현의 자동구성/구성 프로퍼티에 사용돼요.

2024년 1월 13일 업데이트

다음 OpenAi 자동구성 채팅 프로퍼티가 변경됐어요

2023년 12월 27일 업데이트

SimplePersistentVectorStore와 InMemoryVectorStore를 SimpleVectorStore로 병합

  • InMemoryVectorStore를 SimpleVectorStore로 교체

2023년 12월 20일 업데이트

Ollama 클라이언트와 관련 클래스 및 패키지 이름 리팩터링

  • org.springframework.ai.ollama.client.OllamaClient를 org.springframework.ai.ollama.OllamaModelCall로 교체.
  • OllamaChatClient 메서드 시그니처가 변경됐어요.
  • org.springframework.ai.autoconfigure.ollama.OllamaProperties를 org.springframework.ai.model.ollama.autoconfigure.OllamaChatProperties로 이름 변경하고 접미사를 spring.ai.ollama.chat로 변경. 일부 프로퍼티도 변경됐어요.

2023년 12월 19일 업데이트

AiClient 및 관련 클래스와 패키지 이름 변경

  • AiClient를 ChatClient로 이름 변경
  • AiResponse를 ChatResponse로 이름 변경
  • AiStreamClient를 StreamingChatClient로 이름 변경
  • 패키지 org.sf.ai.client를 org.sf.ai.chat로 이름 변경 아티팩트 ID 이름 변경
  • transformers-embedding을 spring-ai-transformers로 Maven 모듈을 최상위 디렉토리와 embedding-clients 하위 디렉토리에서 모두 단일 models 디렉토리 아래로 이동.

2023년 12월 1일

프로젝트의 Group ID를 전환하고 있습니다:

  • FROM: org.springframework.experimental.ai
  • TO: org.springframework.ai 아티팩트는 아래와 같이 여전히 snapshot 저장소에서 호스팅됩니다. 메인 브랜치가 버전 0.8.0-SNAPSHOT으로 이동할 거예요. 일주일이나 이주 동안 불안정할 거예요. 최첨단을 원하지 않는다면 0.7.1-SNAPSHOT을 사용해 주세요. 이전과 같이 0.7.1-SNAPSHOT 아티팩트에 접근할 수 있고, 여전히 0.7.1-SNAPSHOT 문서에 접근할 수 있어요.

0.7.1-SNAPSHOT 의존성

  • Azure OpenAI
<dependency>
    <groupId>org.springframework.experimental.ai</groupId>
    <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
    <version>0.7.1-SNAPSHOT</version>
</dependency>
  • OpenAI
<dependency>
    <groupId>org.springframework.experimental.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <version>0.7.1-SNAPSHOT</version>
</dependency>

더 알아보기 (Learn more)