반응형
Spring AI에서는 AI 모델과 통신하기 위해 Prompt와 Message라는 객체를 사용하고 있습니다. 오늘은 Chat Client API에서 요청을 보낼 때 사용되는 Prompt와 Message라는 객체에 대해 알아보려고 합니다.
Prompt란?
- AI 모델에게 사용자 의도를 가이드해 주는 문장을 말 하며, 사용자의 입력과 직간접적으로 연결되어 있기 때문에 Prompt를 잘 작성하는 것이 AI 모델의 응답을 보다 더 정확하게 추출할 수 있습니다.
- 간단히 예를 들면, 사용자가 "사과에 대해 알려줄래?"라고 AI 모델에 요청했을 때, AI 모델은 "사과"라는 단어에 대해 "과일(물체)로서의 사과"와 "행위(감정)으로서의 사과" 이 두 가지 의미를 명확하게 인지하지 못하기 때문에 사용자가 원하는 답변이 나오지 않을 수 있습니다.
Prompt 내부
- 내부는 다음과 같이 String으로 사용자 입력을 직접 받을 수도 있고, Message라는 객체를 통해 받을 수도 있습니다.
- 또한, ChatOptions라는 객체를 Nullable하게 받을 수도 있는데, 이는 AI 모델에 대한 옵션 설정을 요청 단계에서도 커스터마이징 하여 사용할 수 있게끔 의도한 것으로 보이네요.
- ChatOptions의 경우, 보통 모델 설정 Bean을 등록할 때 설정하기 때문에 Nullable 하게 사용하지 않았을까 합니다.
public class Prompt implements ModelRequest<List<Message>> {
private final List<Message> messages;
@Nullable
private ChatOptions chatOptions;
...
public Prompt(String contents) {
this((Message)(new UserMessage(contents)));
}
public Prompt(Message message) {
this(Collections.singletonList(message));
}
public Prompt(List<Message> messages) {
this((List)messages, (ChatOptions)null);
}
public Prompt(String contents, @Nullable ChatOptions chatOptions) {
this((Message)(new UserMessage(contents)), chatOptions);
}
public Prompt(Message message, @Nullable ChatOptions chatOptions) {
this(Collections.singletonList(message), chatOptions);
}
public Prompt(List<Message> messages, @Nullable ChatOptions chatOptions) {
Assert.notNull(messages, "messages cannot be null");
Assert.noNullElements(messages, "messages cannot contain null elements");
this.messages = messages;
this.chatOptions = chatOptions;
}
}
Message란?
- Prompt 생성자에 매개변수로 받는 객체로, 다음과 같이 Message Interface를 구현한 세 가지 클래스를 통해 AI 모델에 Message를 전달합니다.
- UserMessage: 사용자가 입력한 메시지
- SystemMessage: 개발자가 AI 모델에게 가이드하기 위한 메시지
- ToolResponseMessage: Spring AI 기능중 하나인, Tool 기능에서 사용되는 Message 타입입니다.
public interface Message extends Content {
MessageType getMessageType();
}
// SystemMessage
public class SystemMessage extends AbstractMessage {
public SystemMessage(String textContent) {
this(textContent, Map.of());
}
public SystemMessage(Resource resource) {
this(MessageUtils.readResource(resource), Map.of());
}
private SystemMessage(String textContent, Map<String, Object> metadata) {
super(MessageType.SYSTEM, textContent, metadata);
}
}
// UserMessage
public class UserMessage extends AbstractMessage implements MediaContent {
protected final List<Media> media;
public UserMessage(String textContent) {
this(textContent, new ArrayList(), Map.of());
}
private UserMessage(String textContent, Collection<Media> media, Map<String, Object> metadata) {
super(MessageType.USER, textContent, metadata);
Assert.notNull(media, "media cannot be null");
Assert.noNullElements(media, "media cannot have null elements");
this.media = new ArrayList(media);
}
public UserMessage(Resource resource) {
this(MessageUtils.readResource(resource));
}
}
// ToolResponseMessage
public class ToolResponseMessage extends AbstractMessage {
protected final List<ToolResponse> responses;
public ToolResponseMessage(List<ToolResponse> responses) {
this(responses, Map.of());
}
public ToolResponseMessage(List<ToolResponse> responses, Map<String, Object> metadata) {
super(MessageType.TOOL, "", metadata);
this.responses = responses;
}
}
- Spring AI에서 공식적으로 제공하는 Message API에 대한 클래스 도식화는 다음과 같습니다.
Role(MessageType)
- 위 Message 구현체를 살펴보면, 4가지 타입의 구현체가 있는 것을 확인할 수 있습니다. (UserMessage, SystemMessage, ToolResponseMessage, AssistantMessage)
- 이는 모두 AbstractMessage라는 추상 클래스를 상속하고 있는데, 해당 추상 클래스에서 MessageType에 따라 각각 다른 구현체를 만들어 냄을 확인할 수 있습니다.
- 각 MessageType의 주요 역할은 다음과 같습니다.
- USER: 일반적인 사용자 입력 메시지
- SYSTEM: 사용자 메시지를 AI에게 가이드하기 위해 파라미터나 규칙을 정의하는 메시지 타입으로, AI 모델의 성격이나 말투, 제한사항 등을 제어하기 위해 사용
- ASSISTANT: 사용자 메시지에 대한 AI 응답으로, 단순한 답변을 넘어서, 대화 흐름 유지, 기억 유지, 기능 수행 요청의 역할까지 수행. 또한, Tool 호출이 있는 경우, 이를 가이드 해주는 메시지 타입
- TOOL: AssistantMessage로 부터 Tool Calling에 대한 실행 결과 값을 보여주는 메시지 타입
Prompt와 Message를 적용한 예제
- 위에서 살펴 본, Prompt와 Message를 사용하여 Chat Client API에 요청하는 세 가지 방식에 대한 예제입니다.
@SpringBootTest
class ChatClientTest {
@Autowired
lateinit var chatClientBuilder: ChatClient.Builder
@DisplayName("Prompt 생성자에 String을 넣은 입력")
@Test
fun stringPromptTest() {
val chatClient: ChatClient = chatClientBuilder.build()
val prompt = Prompt("Spring AI에 대해서 알려줄래?")
val response = chatClient.prompt(prompt)
.call()
.content();
println(response)
}
@DisplayName("Prompt 생성자에 Message 넣은 입력")
@Test
fun messagePromptTest() {
val chatClient: ChatClient = chatClientBuilder.build()
val userMessage: Message = UserMessage("Spring AI에 대해서 알려줄래?")
val systemMessage: Message = SystemMessage("당신은 Java 및 Spring에 대한 전문가입니다. 사용자가 물어볼 때, 다른 프로젝트와 어떻게 다른지 비교하여 분석해주면 더 좋을 것 같습니다. ")
val prompt = Prompt(listOf(userMessage, systemMessage))
val response = chatClient.prompt(prompt)
.call()
.content();
println(response)
}
@DisplayName("Prompt 생성자에 ChatOptions를 넣은 입력")
@Test
fun chatOptionsPromptTest() {
val chatClient: ChatClient = chatClientBuilder.build()
val userMessage: Message = UserMessage("Spring AI에 대해서 알려줄래?")
val systemMessage: Message = SystemMessage("당신은 Java 및 Spring에 대한 전문가입니다. 사용자가 물어볼 때, 다른 프로젝트와 어떻게 다른지 비교하여 분석해주면 더 좋을 것 같습니다. ")
val chatOption = OpenAiChatOptions.builder()
.model("gpt-3.5-turbo")
.temperature(0.7)
.maxTokens(1024)
.build()
val prompt = Prompt(listOf(userMessage, systemMessage), chatOption)
val response = chatClient.prompt(prompt)
.call()
.content();
println(response)
}
}
PromptTemplate
- AI 모델에 전달할 프롬프트 메시지에 대한 템플릿을 지정할 수 있는 클래스입니다. PromptTemplate은 ChatClient.prompt()에 인자로 넣어서도 사용할 수 있는데요, PromptTemplate에서 제공하는 메서드에 대한 특징은 다음과 같습니다.
- render(): PromptTemplateStringAction 인터페이스에서 제공해 주는 메서드로, 매개변수가 없는 메서드와 Map<String, Object> 형식의 매개변수를 받는 메서드로 구성되어 있습니다. render() 메서드는 구분자로 들어온 키워드를 기반으로 한 동적인 String 값을 만들어 반환해주는 메서드입니다.
- create(): 템플릿 + 매개변수(map 또는 context)를 주어서 Message 객체 또는 Prompt 객체로 생성하는 메서드입니다.
PromtTemplate 클래스 구조
- PromptTemplate의 클래스 구조는 아래 사진과 같습니다.
PromptTemplate에 대한 예제
- 예제를 보면, render()를 사용하는 경우, PrompteTemplate.builder()를 사용하지만, create()을 사용하는 경우, PromptTemplate() 방식의 생성자를 통해 먼저 생성한 후에 사용하는 차이점이 있습니다.
@SpringBootTest
class PromptTemplateTest {
@Autowired
lateinit var chatClientBuilder: ChatClient.Builder
@DisplayName("PromptTemplate render() 메서드를 사용한 예제")
@Test
fun promptTemplateRenderTest() {
val chatClient: ChatClient = chatClientBuilder.build()
val promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder()
.startDelimiterToken('<')
.endDelimiterToken('>').build()
)
.template("""
Spring AI의 <chapter> 대해서 알려줄래?
""".trimIndent())
.build()
val prompt = promptTemplate.render(mapOf("chapter" to "chatClient API"))
val response = chatClient.prompt(prompt)
.call()
.content();
println(response)
}
@DisplayName("PromptTemplate create() 메서드를 사용한 예제")
@Test
fun promptTemplateCreateTest() {
val chatClient: ChatClient = chatClientBuilder.build()
val promptTemplate = PromptTemplate("Spring AI에 대해서 알려줄래?")
val prompt = promptTemplate.create()
val response = chatClient.prompt(prompt)
.call()
.content();
println(response)
}
}
반응형
LIST
'Spring Framework > AI' 카테고리의 다른 글
Spring AI - Advisor API (0) | 2025.09.30 |
---|---|
Spring AI - Structured Output Converter (0) | 2025.09.27 |
Spring AI - ChatClient API 살펴보기 (0) | 2025.09.21 |
Spring AI - Chat Client API 시작하기 (0) | 2025.09.20 |
Spring AI - 이제 Spring에서도 AI 연동이 가능합니다. (0) | 2025.09.14 |