개요
이전 문서를 통해 Spring AI에서 OpenAI 통합과 함수 호출(Function Call) 방법을 익혔다면, 이제 보다 심화된 기능들을 살펴보겠습니다. 특히 이전 대화 내역을 어떻게 전달하고, 모델의 동작을 제어하는 다양한 파라미터를 설정하는지에 중점을 두고 설명합니다.
핵심 개념
Spring AI란?
Spring AI는 자바 기반 AI 애플리케이션 개발을 간소화하기 위해 설계된 스프링 생태계 프로젝트입니다. 주요 특징은 다음과 같습니다:
- OpenAI, Azure, Amazon, Google 등 주요 클라우드 제공업체 지원
- "채팅" 및 "텍스트 → 이미지" 모델 타입 지원
- 여러 AI 제공자 간 일관성 있는 추상화된 API 제공
- 동기 및 스트리밍 방식 모두 지원
- 모델별 특수 기능 접근 가능
- 모델 출력을 POJO로 직접 매핑
필수 준비 사항
JDK 버전
JDK 17 이상 사용 필요
Maven 프로젝트 구성
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<relativePath/>
</parent>
필요한 의존성
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.24</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.0</version>
</dependency>
</dependencies>
구현 방법
API 키 설정
spring:
ai:
openai:
api-key: YOUR_API_KEY_HERE
대화 이력 관리
이전 대화 내용을 유지하면서 새로운 질문을 처리하려면 아래 두 가지 방식을 사용할 수 있습니다.
- UserMessage와 AssistantMessage 사용: 명시적으로 역할 구분
- ChatMessage 사용: 메시지 타입으로 구분
모델 파라미터 설정
AI 모델의 동작 방식을 세밀하게 조절하려면 다음 두 가지 방법 중 하나를 선택할 수 있습니다.
- 설정 파일에서 지정: application.yml 또는 properties 파일 이용
- 코드 내에서 지정: OpenAiChatOptions 빌더 패턴 활용
실제 예제
세 번의 연속된 질의응답 과정에서 AI가 위치 정보를 기억하고 관련 답변을 생성하는 것을 확인할 수 있었습니다.
| 순서 | 사용자 입력 | AI 응답 |
|---|---|---|
| 1회차 | 안녕하세요 | 안녕하세요! 도움을 드릴 수 있어 정말 기쁩니다. 궁금한 점이 있으신가요? |
| 2회차 | 저는 광주에 살아요. 당신은요? | 저는 인공지능 어시스턴트라 실제 거주지는 없어요. 하지만 질문에 답해드릴게요! |
| 3회차 | 그럼 저희 집엔 어떤 특산품이 있을까요? | [광주 특산품 목록 반환] |
추가 참고 자료
모델 옵션 상세 설정
더 많은 파라미터 설정은 공식 문서 참조: OpenAI Chat Options
샘플 코드
@Slf4j
@RestController
@RequestMapping("/chat")
public class ChatHistoryController {
private final OpenAiChatClient chatClient;
public ChatHistoryController(OpenAiChatClient client) {
this.chatClient = client;
}
@GetMapping("/contextual")
public SseEmitter getContextualResponse() {
SseEmitter emitter = new SseEmitter();
// 시스템 프롬프트 정의
var systemMsg = new SystemPromptTemplate("{role}")
.createMessage(Map.of("role", "친근한 도우미"));
// 이전 대화 이력 구성
List<Message> contextMessages = Arrays.asList(
new UserMessage("안녕하세요"),
new AssistantMessage("안녕하세요! 무엇을 도와드릴까요?"),
new UserMessage("저는 부산 사람입니다."),
new AssistantMessage("부산은 맛있는 음식이 많죠! 어떤 걸 알려드릴까요?")
);
// 모델 옵션 설정
var options = OpenAiChatOptions.builder()
.withModel("gpt-3.5-turbo")
.withTemperature(0.7f)
.withMaxTokens(2048)
.withTopP(0.9f)
.build();
// 전체 프롬프트 구성
var prompt = new Prompt(contextMessages, options);
// 스트림 방식으로 결과 전송
chatClient.stream(prompt).subscribe(data -> {
try {
String content = data.getResults().get(0).getOutput().getContent();
if (content != null && !content.isEmpty()) {
emitter.send(content);
} else {
emitter.complete();
}
} catch (IOException e) {
log.error("SSE 전송 오류", e);
emitter.completeWithError(e);
}
});
return emitter;
}
}