본문 바로가기
Spring

Chat Client API - Spring AI Practice

by AlbertIm 2025. 9. 16.

개요

ChatClient는 AI Model과 통신하는 fluent API를 제공합니다. 여기서 fluent API란 메서드 체이닝을 통해 직관적이고 가독성 높은 코드를 작성할 수 있는 스타일을 의미합니다.

본문

Creating a ChatClient

ChatClient.Builder

ChatClient는 ChatClient.Builder 객체를 통해 생성할 수 있습니다. Builder는 API 키, 모델, 옵션 등을 설정하는 메서드를 제공합니다. Spring AI는 기본 자동 설정은 ChatClient.Builder 빈을 제공합니다.

@Configuration
class AiConfig {

    @Bean
    fun chatClient(chatClientBuilder: ChatClient.Builder): ChatClient {
        return chatClientBuilder
            .defaultSystem("사용자 질문에 대해 한국어로 답변을 해야 합니다.")
            .defaultOptions(
                ChatOptions.builder()
                    .model("gpt-4o-mini")
                    .temperature(0.3)
                    .maxTokens(1000)
                    .build()
            )
            .build()
    }
}

@Service
class AiService(private val chatClientBuilder: ChatClient.Builder) {

    private val chatClient = chatClientBuilder
        .defaultSystem("사용자 질문에 대해 한국어로 답변을 해야 합니다.")
        .defaultOptions(
            ChatOptions.builder()
                .model("gpt-4o-mini")
                .temperature(0.3)
                .maxTokens(1000)
                .build()
        )
        .build()

    fun askQuestion(question: String): String {
        return chatClient.prompt()
            .user(question)
            .call()
            .content() ?: "No response generated."
    }
}

Working with Multiple Chat Models

단일 애플리케이션에서 여러 Chat Model을 사용하는 경우도 있습니다. Spring AI에서는 이 문제도 간단하게 해결할 수 있습니다.

먼저 설정을 통해 기본 ChatClient 빈 생성을 비활성화 해야 합니다. 그렇지 않으면 동일한 타입의 빈이 두 개 이상 존재하게 되어 애플리케이션 컨텍스트 로딩에 실패합니다.
spring.ai.chat.client.enabled=false

 

여러 ChatClient 빈을 생성하는 예시는 다음과 같습니다:

// gradle build.gradle.kts
dependencies {
    // ...
    implementation("org.springframework.ai:spring-ai-starter-model-openai")
    implementation("org.springframework.ai:spring-ai-starter-model-anthropic")
    // ...
}

// application.properties
spring.ai.chat.client.enabled = false
spring.ai.model.openai.api - key = your - openai - api - key
spring.ai.model.anthropic.api - key = your - anthropic - api - key

// AiConfig.kt
@Configuration
class AiConfig {
    @Bean
    fun openAi(openAiModel: OpenAiChatModel): ChatClient {
        return ChatClient.create(openAiModel)
    }

    @Bean
    fun authropicChatClient(authropicChatModel: AnthropicChatModel): ChatClient {
        return ChatClient.create(authropicChatModel)
    }
}

// Service.kt
@Service
class AiService(
    @Qualifier("openAi") private val openAiChatClient: ChatClient,
    @Qualifier("authropicChatClient") private val authropicChatClient: ChatClient
) {
    // ...
}

ChatClient Fluent API

ChatClient는 fluent API를 제공하여 직관적이고 가독성 높은 코드를 작성할 수 있습니다. 주로 prompt 빌더를 통해 메시지를 구성합니다.

  • prompt(): 새로운 프롬프트 빌더를 생성합니다.
  • prompt(prompt: Prompt): 기존 프롬프트를 사용합니다.
  • prompt(String content): 사용자 메시지로 시작하는 프롬프트를 생성합니다.

ChatClient Responses

ChatClient는 다양한 응답 형식을 지원합니다:

  • ChatResponse: 기본 응답 형식
  • Entity: 엔티티 추출 응답 형식
  • Streaming Responses: 스트리밍 응답 형식
val chatResponse = chatClient.prompt()
    .user(question)
    .call()
    .chatResponse()

val entity = chatClient.prompt()
    .user(question)
    .call()
    .entity(CustomMessage::class.java)

val streamingResponses = chatClient.prompt()
    .user(question)
    .stream()
    .content()

Prompt Templates

ChatClient Fluent API는 프롬프트 템플릿 기능을 제공합니다.

chatClient.prompt()
    .user { u ->
        u
            .text("{동적변수}를 포함한 질문입니다.")
            .param("동적변수", variableExample)
    }
    .call()
    .content()

 

ChatClient에서 직접 구성한 TemplateRenderer는 ChatClient 빌더 체인에 직접 정의한 `prompt` 콘텐츠에만 적용됩니다. `QuestionAnswerAdivisor`와 같은 사전 정의된 템플릿에는 적용되지 않습니다.

 

ChatClient에 다른 템플릿 엔진을 사용하려면 TemplateRenderer 인터페이스의 사용자 지정 구현을 제공할 수 있습니다.

// 예시: prompt에 JSON을 포함되고 싶고 JSON 구문과 충돌을 피하고 싶다면 다음과 같이 기본 구분 기호 `{}` 대신 `<` 및 `>`를 사용할 수 있습니다.
chatClient.prompt()
    .user { u ->
        u
            .text("<variable>를 포함한 질문입니다.")
            .param("variable", variableExample)
    }
    .templateRenderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
    .call()
    .content() ?: NO_RESPONSE

call() return values

call() 메서드는 다양한 형식의 응답을 반환합니다.

  • String content(): 응답의 텍스트 콘텐츠를 반환합니다.
  • ChatResponse chatResponse(): 여러 생성 결과와 메타데이터(토큰 사용량 등)를 포함하는 ChatResponse 객체를 반환합니다.
  • ChatClientResponse chatClientResponse(): ChatResponse 객체와 ChatClient 실행 컨텍스트를 포함하는 ChatClientResponse 객체를 반환하여
    advisors 실행 중에 사용되는 추가 데이터(예: RAG flow의 문서 등)에 접근할 수 있습니다.
  • ResponseEntity<?> responseEntity(): HTTP 응답 엔티티를 반환합니다.
  • entity(Class<T> clazz): 지정된 클래스 유형의 엔티티를 반환합니다.

stream() return values

stream() 메서드는 스트리밍 응답을 반환합니다.

  • Flux<String> content(): 응답의 텍스트 콘텐츠를 스트리밍합니다.
  • Flux<ChatResponse> chatResponse(): 추가 메타데이터(토큰 사용량 등)를 포함하는 ChatResponse 객체를 스트리밍합니다.
  • Flux<ChatClientResponse> chatClientResponse(): chatClientResponse 객체를 스트리밍합니다.

Using defaults

Configuration 클래스에서 ChatClient.Builder 빈을 구성할 때 기본 시스템 메시지와 옵션을 설정할 수 있습니다.

  • defaultSystem(String systemMessage): 모든 프롬프트에 적용할 기본 시스템 메시지를 설정합니다.
  • defaultOptions(ChatOptions options): 모든 프롬프트에 적용할 기본 옵션을 설정합니다.
  • defaultFunction(String name, String description, java.util.function.Function<I, O> function)
    • name: 함수 이름
    • description: 함수 설명
    • function: 함수 구현
  • defaultFunctions(String… functionNames): 애플리케이션 컨텍스트에 정의된 java.util.Function 빈의 이름을 사용하여 함수를 등록합니다.
  • defaultUser(String text), defaultUser(Resource text), defaultUser(Consumer<UserSpec> userSpecConsumer): 사용자 메시지를
    설정합니다.
  • defaultAdvisors(Advisor… advisor): 어드바이저를 추가합니다.
  • defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer): AdvisorSpec을 사용하여 어드바이저를 추가합니다.
@Configuration
class AiConfig {

    @Bean
    fun chatClient(builder: ChatClient.Builder): ChatClient {
        return builder.defaultSystem("한국어로 대답해줘").build()
    }
}

Advisors

Advisors API는 Spring AI에서 가로채고 수정하고 확장할 수 있는 강력한 메커니즘을 제공합니다. RAG(Retrieval-Augmented Generation) 패턴 구현, 로깅, 프롬프트 보강 등
다양한 용도로 사용할 수 있습니다.

사용자 텍스트로 AI 모델을 호출할 때 일반적인 패턴은 프롬포트에 데이터를 추가하거나 보강하는 것입니다. 이러한 데이터는 일반적으로 다음과 같은 형태를 취합니다:

  • 단독 데이터: AI 모델이 학습하지 않은 데이터
  • 대화 기록: 채팅 모델의 API는 stateless이므로 이전 대화 내용이 포함되지 않습니다. 따라서 이전 대화 내용을 프롬프트에 추가할 필요가 있을 수 있습니다.

마무리

ChatClient API를 사용하면 Fluent API를 통해 직관적이고 가독성 높은 코드를 작성할 수 있습니다.

Reference Documentation

- Spring AI Reference Documentation

댓글