[Spring AI] #3-2. ChatService 분석 – buildRequestPrompt()로 요청 준비하기
※ 인프런 강의 Spring AI 실전 가이드: RAG 챗봇 만들기를 실습하는 내용입니다.
※ 해당 강의 코드를 코틀린 → 자바로 언어를 바꿔 진행하기 때문에 일부 코드 및 구현부가 다를 수 있습니다.
※ 실습이지만 코드를 직접 까보는 내용을 기록하는 포스팅이므로 강의 내용과 상이할 수 있습니다.
[Spring AI] #1. LLM 호출 실습 시작 – 구조 먼저 실행해보기
[Spring AI] #2. AiConfig 분석 – OpenAI API 연결 설정 방법
[Spring AI] #3-1. ChatService 분석 – 메시지 생성과 모델 호출 흐름
[Spring AI] #3-2. ChatService 분석 – buildRequestPrompt()로 요청 준비하기
[Spring AI] #3-3. ChatService 분석 – internalCall()로 요청과 응답 재구성 흐름 파악하기
[Spring AI] #4. ChatController 분석 – 요청 처리와 응답 흐름 정리 (포스팅 예정)
이전 포스팅에서의 내용
이전 포스팅에서의 내용은 아래 코드까지 였으며, 현재 포스팅은 buildRequestPrompt(promt)에 대한 포스팅입니다.
1. bulidRequestPrompt(prompt) 파악하기 - runtimeOptions 셋팅
아래 코드 부분을 먼저 살펴볼텐데, 주석에 써잇듯이 런타임 옵션과 기본 옵션을 병합하여 최종적으로 요청 프롬프트를 생성하는 과정입니다. 즉, 실제 요청을 하기 전 최종적으로 병합 및 검증하는 과정입니다.
// Before moving any further, build the final request Prompt,
// merging runtime and default options.
Prompt requestPrompt = buildRequestPrompt(prompt);
buildRequestPrompt(Prompt) 메서드의 경우 코드가 길기 때문에 부분 부분 조각화하여 살펴보도록 하겠습니다.
가장 첫번째로 만나는 코드는 런타입 옵션을 추출하는 부분입니다. 우선 메서드 내부에서 사용할 runtimeOptions를 null로 선언해두고 prompt.getOptions()를 호출하여 인스턴스 타입이 해당하는지를 확인 후 객체 타입에 따라 OpenAiChatOptions 타입으로 변환 하는 부분입니다.
※ prompt.getOptions() 메서드는 위에서 prompt 객체를 생성할 때 사용했던 chatOptions의 getter 메서드 입니다.
OpenAiChatOptions runtimeOptions = null;
if (prompt.getOptions() != null) {
if (prompt.getOptions() instanceof ToolCallingChatOptions toolCallingChatOptions) {
runtimeOptions = ModelOptionsUtils.copyToTarget(toolCallingChatOptions, ToolCallingChatOptions.class,
OpenAiChatOptions.class);
}
else if (prompt.getOptions() instanceof FunctionCallingOptions functionCallingOptions) {
runtimeOptions = ModelOptionsUtils.copyToTarget(functionCallingOptions, FunctionCallingOptions.class,
OpenAiChatOptions.class);
}
else {
runtimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(), ChatOptions.class,
OpenAiChatOptions.class);
}
}
2. bulidRequestPrompt(prompt) 파악하기 - requestOptions 셋팅
그 다음으로 만나볼 수 있는 코드는 아래와 같습니다. 런타입 옵션()과 기본 옵션을 합치는 부분입니다.
5-1에서 만들어두었던 runtimeOptions 객체와 기본 옵션(this.defaultOptions)을 OpenAiChatOptions 객체로 병합 및 생성하는 과정입니다. ModelOptionsUtils.merge() 메서드의 경우 간단히 살펴보니 내부적으로 source(runtimeOptions)의 값이 null이 아닐 경우 복사하는 로직을 가지고 있습니다.
// Define request options by merging runtime options and default options
OpenAiChatOptions requestOptions = ModelOptionsUtils.merge(runtimeOptions, this.defaultOptions,
OpenAiChatOptions.class);
이젠 아래의 코드를 볼 수 있는데 5-1에서 구했던 runtimeOptions가 null이 아니고 runtimeOptions의 값이 없다면 기본 값(this.defaultOptions)를 사용합니다. 즉, 설정하지 않은 옵션들에 대해서는 기본 값(this.defaultOptions의 getter 메서드들은 @JsonIgnore 필드를 반환)을 수동으로 적용시키는 과정입니다.
주석에서도 알 수 있듯이 Jackson에 의해 @JsonIgnore가 지정된 옵션들은 무시되므로 수동 병합하는 과정입니다.
// Merge @JsonIgnore-annotated options explicitly since they are ignored by
// Jackson, used by ModelOptionsUtils.
if (runtimeOptions != null) {
requestOptions.setHttpHeaders(
mergeHttpHeaders(runtimeOptions.getHttpHeaders(), this.defaultOptions.getHttpHeaders()));
requestOptions.setInternalToolExecutionEnabled(
ModelOptionsUtils.mergeOption(runtimeOptions.isInternalToolExecutionEnabled(),
this.defaultOptions.isInternalToolExecutionEnabled()));
requestOptions.setToolNames(ToolCallingChatOptions.mergeToolNames(runtimeOptions.getToolNames(),
this.defaultOptions.getToolNames()));
requestOptions.setToolCallbacks(ToolCallingChatOptions.mergeToolCallbacks(runtimeOptions.getToolCallbacks(),
this.defaultOptions.getToolCallbacks()));
requestOptions.setToolContext(ToolCallingChatOptions.mergeToolContext(runtimeOptions.getToolContext(),
this.defaultOptions.getToolContext()));
}
else {
requestOptions.setHttpHeaders(this.defaultOptions.getHttpHeaders());
requestOptions.setInternalToolExecutionEnabled(this.defaultOptions.isInternalToolExecutionEnabled());
requestOptions.setToolNames(this.defaultOptions.getToolNames());
requestOptions.setToolCallbacks(this.defaultOptions.getToolCallbacks());
requestOptions.setToolContext(this.defaultOptions.getToolContext());
}
3. bulidRequestPrompt(prompt) 파악하기 - validateToolCallBack() 유효성 검증
그 다음으로 만나볼 수 있는 코드는 아래와 같습니다. validateToolCallbacks 메서드는 위의 과정들이 정상적으로 설정되었는지 마지막으로 유효성 검사를 실시하는 부분입니다.
ToolCallingChatOptions.validateToolCallbacks(requestOptions.getToolCallbacks());
내부적으로 이러한 로직을 가지고 있습니다. toolCallBacks는 List 형태이므로 중복되는 병합하는 과정에서 의도치 않은 toolName이 중복되는 것을 고려하여 중복되는 것이 있는지 검사하는 메서드 입니다.
만약, 중복되는것이 있다면 IllegalStateException을 발생시키며 중복된 toolName을 반환합니다.
3. buildRequestPrompt(prompt) 파악하기 - prompt 객체 반환
마지막으로 여태 만든 정보(requestOptions)를 기반으로 prompt 객체를 반환 합니다. getInstructions() 메서드는 이전 포스팅의 "3. 프롬프트 생성 파악하기"에서 넣었던 message를 반환합니다.
new Prompt(prompt.getInstructions(), requestOptions);
