개요
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' 카테고리의 다른 글
Spring Boot Properties에서 System Environment Property 사용하기 (0) | 2025.09.17 |
---|---|
Prompts - Spring AI Practice (0) | 2025.09.16 |
Chat Model API - Spring AI Practice (0) | 2025.09.15 |
Social 로그인 구현 (Spring Security) (1) | 2025.01.25 |
Spring AuthorizationServer를 사용한 OAuth2 인증 서버 구축 (1) (2) | 2025.01.17 |
댓글