스프링 AI 연동 (3): 커스텀 대화를 위한 프롬프트 템플릿 도입

  1. 템플릿 생성

먼저 pom.xml에 유효성 검사 Starter를 추가합니다:

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

"한국 역사 문화"에 대한 AI를 정의하고, 템플릿을 간단히 구성합니다:

엔티티 정의:

package com.korea.ai.demo.model;

import jakarta.validation.constraints.NotBlank;

public record CulturalQuestion(
        @NotBlank(message = "주제를 입력해주세요") String subject,
        @NotBlank(message = "질문을 입력해주세요") String query) {
}

템플릿 파일 resources/promptTemplates/culturalPromptTemplate.st:

당신은 한국 역사 문화 전문가입니다.
해당 주제에 대해 정보가 없거나 답변을 알지 못한다면 "정보가 없습니다"라고 답변하세요.

상세한 설명과 함께 답변해주세요.

문화 분야는 {subject}입니다.

질문:
{query}
  1. 구현 로직

package com.korea.ai.demo.service.impl;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import com.korea.ai.demo.model.CulturalResponse;
import com.korea.ai.demo.model.CulturalQuestion;
import com.korea.ai.demo.service.CulturalService;

@Service
public class CulturalAIServiceImpl implements CulturalService {

    private final ChatClient chatClient;

    public CulturalAIServiceImpl(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @Value("classpath:/promptTemplates/culturalPromptTemplate.st")
    Resource culturalPromptTemplate;

    @SuppressWarnings("null")
    @Override
    public CulturalResponse ask(CulturalQuestion culturalQuestion) {
        var response = chatClient.prompt()
                .user(userSpec -> userSpec
                    .text(culturalPromptTemplate)
                    .param("subject", culturalQuestion.subject())
                    .param("query", culturalQuestion.query())
                )
                .call();
        var responseText = response.content();
        return new CulturalResponse(culturalQuestion.subject(), responseText);
    }
}
  1. 실행 결과

테스트 케이스:

curl http://localhost:8080/culture/ask \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"subject": "조선 시대", "query": "조선 시대 과거 제도의 특징과 변천 과정에 대해 설명해주세요. 특히 양반과 서인, 동인의 정치적 대립이 어떻게 과거 제도와 연결되었는지 설명하십시오."}' 

반환 결과 (일부 편집):

{
  "subject":"조선 시대",
  "answer":"조선 시대의 과거 제도는... (상세 설명)"
}

조선 시대 과거 제도에 대한 상세 설명이 반환됩니다.

  1. 추가 기능

4.1 템플릿 기반 확장

RAG를 도입하기 전, 간단한 템플릿 채우기를 통해 확장 기능을 구현할 수 있습니다. 예를 들어 cultureRules/korea.txt 파일을 추가하고 내용은 다음과 같습니다:

- 역사적 사실을 기반으로 답변할 것
- 특정 시대의 문화적 배경을 고려할 것
- 중립적인 시각을 유지할 것

이를 템플릿에 다음과 같이 적용할 수 있습니다:

당신은 한국 역사 문화 전문가입니다.
해당 주제에 대해 정보가 없거나 답변을 알지 못한다면 "정보가 없습니다"라고 답변하세요.
가능한 경우 다음 규칙을 따르세요: {rules}.

상세한 설명과 함께 답변해주세요.

문화 분야는 {subject}입니다.

질문:
{query}

4.2 AI 모델 옵션

4.2.1 모델 유형

설명 부분에서 언급된 내재로 생략합니다

4.2.2 모델 온도

temperature 파라미터는 생성 전략의 핵심 파라미터로 출력의 무작위성과 창의성에 직접적인 영향을 미칩니다.

  • 낮은 온도(0.3-0.7): 고객 서비스 로봇 등 정확한 답변이 필요한 시나리오에 적합, 오류 정보 감소
  • 중간 온도(0.7-1.2): 창의적인 글쓰기에 적합, 논리성과 다양성의 균형
  • 높은 온도(1.2-2.0): 아이디어 도구에 사용되며, 비전통적인 창의성을 자극
ChatOptions chatOptions = ChatOptions.builder()
  .temperature(0.7)
  .build();

String responseText = chatClient.prompt()
  .user(culturalQuestion.query())
  .options(chatOptions)
  .call()
  .content();

4.2.3 기타 옵션

  • topP 결과 선택 비율, 예를 들어 .topP(0.8)은 상위 80% 결과에서 선택
  • topK 결과 제외 비율, 예를 들어 .topP(0.2)은 하위 20% 결과 제외

4.3 응답 형식화

예를 들어, Prompt에서 JSON 형식의 출력을 지정하거나 특정 형식의 응답만 받을 수 있습니다. 이를 통해 인기 문화 목록 등 JSON API를 빠르게 구현할 수 있습니다.

또한 출력 형식을 스트리밍으로 설정하면 클라이언트나 웹 프론트엔드에서 SSE 프로토콜을 사용해 결과를 토큰 단위로 표시할 수 있습니다.

return chatClient.prompt()
  .system(systemSpec -> systemSpec
    .text(promptTemplate)
    .param("subject", question.subject())
    .param("rules", cultureRules))
    .user(question.query())
  .stream() // 스트리밍
  .content();

4.4 응답 메타데이터

LLM이 반환하는 내용에는 OpenAI의 경우 토큰 사용 관련 데이터(메타데이터)가 포함됩니다:

{
  "token_usage": {
    "completion_tokens": 245,
    "prompt_tokens": 32,
    "total_tokens": 277
  },
  "model_name": "gpt-4-turbo",
  "system_fingerprint": "fp_korean_culture_2024",
  "finish_reason": "stop",
  "logprobs": null
}

이를 코드에서 가져와 기록할 수 있습니다:

var responseEntity = chatClient.prompt()
  .system(systemSpec -> systemSpec
    .text(promptTemplate)
    .param("subject", question.subject())
    .param("rules", cultureRules))
    .user(question.query())
  .call()
.responseEntity(Response.class);
var response = responseEntity.response();
var metadata = response.getMetadata();
log.info(metadata.getUsage()); // 토큰 사용량 획득
return responseEntity.entity();

태그: 스프링 AI 프롬프트 템플릿 커스텀 대화 한국 문화 AI 모델 설정

6월 16일 16:41에 게시됨