🤔 현재 상황
사용 모델: sangthree/eeve_gguf/eeve_model_from_HF.gguf (21.61GB)
현재 유정이가 최근에 야놀자의 EEVE 모델을 기반으로 상쓰리의 커스텀 데이터셋을 파인튜닝한 모델을 사용하고 있다. 그러나, 기존 fastAPI 서버코드로 프로그램을 작동시켰을때는, 결과에 문제가 있었다.
[ 기존 fastAPI 서버코드 ]
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
app = FastAPI()
# CORS 설정
origins = [
"http://localhost", # 개발 중인 클라이언트 주소
"http://localhost:3000", # React 개발 서버 주소
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["POST"],
allow_headers=["Content-Type"],
)
llm = ChatOpenAI(
base_url="http://localhost:5000/v1", # LM Studio의 URL
api_key="lm-studio",
model="sangthree/eeve_gguf",
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
)
class Message(BaseModel):
question: str # 클라이언트에서 question 필드로 전송될 것으로 기대
@app.post("/chat")
async def chat_with_bot(message: Message):
try:
prompt = PromptTemplate.from_template(
"""You are an assistant, and your task is to answer only in Korean as if you were a visiting patient. The doctor is asking questions to better understand your symptoms. You answer the doctor's questions with simple and easy expressions.
#Question:
{question}
#Answer: """
)
chain = prompt | llm | StrOutputParser()
response = chain.invoke({"question": message.question}) # 클라이언트로부터 받은 질문 사용
return {"response": response} # JSON 형태로 응답
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

위의 결과를 보면 알겠지만, 기존 서버 코드로 응답을 도출했을때는, 혼자서.. 마치 시나리오를 짜는 것 같았다. 나는 모델이 혼자 시나리오를 작성하는 듯한 응답을 생성하는 이유로 프롬프트의 명확성 부족을 뽑았다.
💫 프롬프트의 명확성 부족은 다음과 같이 구분할 수 있다.
(1) 설명 부족: 프롬프트가 명확하게 정의되지 않으면, 모델이 추가적인 맥락이나 시나리오를 생성하려고 시도할 수 있습니다.
(2) 역할 구분 부족: 프롬프트에서 역할 (예: 사용자와 어시스턴트)을 명확히 구분하지 않으면, 모델이 혼란스러워할 수 있습니다.
(1)번을 처음에 시도하고 있었는데, 보영이로부터 연락이 왔다.

그래서 ChatPromptTemplate을 알아보기 시작했다ㅎㅎ
✏️ PromptTemplate VS. ChatPromptTemplate
일단 기존에 사용했던 PromptTemplate과 ChatPromptTemplate의 차이에 대해 살펴보자.
[ PromptTemplate ]
- 정의:
PromptTemplate은 일반적인 프롬프트 템플릿을 생성하는 데 사용됩니다. 이는 대화형 요소가 필요 없는 표준 언어 모델 작업에 적합합니다.
- 용도: 변수 자리 표시자를 포함한 템플릿을 정의할 수 있으며, 특정 값으로 채워져 프롬프트가 사용됩니다. 주로 간단한 질문이나 요청을 생성할 때 유용합니다.
[ ChatPromptTemplate ]
- 정의:
ChatPromptTemplate은 대화형 언어 모델을 위해 특별히 설계된 템플릿입니다. 시스템, 인간, AI등 다양한 역할 간의 상호작용을 포함하는 프롬프트를 구성하는 데 사용됩니다.
- 용도: 역할별 메시지를 포함할 수 있으며, 대화의 구조를 관리하는 데 유리합니다.
from_template()및 from_messages()와 같은 메서드를 사용하여 대화 상호작용에 맞춘 프롬프트를 쉽게 생성할 수 있습니다.
위의 내용에 따르면, 우리는 역할놀이 서비스이므로 대화형 모델을 사용하는데, ChatPromptTemplate은 대화형 환경에서의 역할과 대화 흐름을 관리하는 데 특화되어있기 때문에, 우리의 프로젝트에는 ChatPromptTemplate이 적합함을 알 수 있었다!!
🥲 시행착오 (명시적 방법 --> from_messages()사용)
처음에는 보영이가 말해준대로, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate을 명시적으로 사용하려고 했으나ㅠㅠㅠ 정말 많은 시도를 해도 실패했다 (자꾸 시도만 하면 500번 에러가 발생하였다.)
물론, 해결할 수 있는 방법이 있을 수 있지만, 계속 시도끝에 안되서 명시적으로 말고, 찾아보니 from_messages()와 같은 메서드를 사용하여 프롬프트를 쉽게 생성할 수 있다고 하여, 그 방법을 시도해보기로 하였다.
=> 아하 그런데 지금보니깐, 보영이가 참고하라고 보내준 colab코드에 from_messages()를 사용한 걸 확인할 수 있었다.
✏️ from_messages()의 작동 방식 설명
-
역할과 내용 짝짓기: from_messages() 메서드에 전달되는 각 튜플은 역할**(예: "system", "human", "ai")**과 해당 역할에 대한 메시지 내용을 포함합니다. 예를 들어, ("system", "You are a helpful AI.")는 AI에게 주어진 시스템 메시지를 정의합니다.
-
자동 역할 처리: 메서드는 각 튜플의 역할을 인식하고, 프롬프트를 적절하게 구성합니다. 이를 통해 대화의 각 메시지가 어떤 역할에 속하는지를 자동으로 이해합니다. 예를 들어, 시스템 명령, 사용자 질문, AI 응답 등을 구분합니다.
-
동적 메시지 삽입: 메시지 내용에 자리 표시자를 사용할 수 있으며, 이는 프롬프트가 호출될 때 실제 데이터로 채워질 수 있습니다. 이를 통해 유연하고 상황에 맞는 프롬프트 생성을 지원합니다.
- 자리표시자 사용 예시
- (step1) 프롬프트 템플릿에서 {}를 사용하여 자리 표시자를 정의합니다. 예를 들어, ("human", "안녕하세요, {name}님. 어떻게 도와드릴까요?")처럼 사용할 수 있습니다.
- (step2) 프롬프트가 호출될 때, 자리 표시자에 실제 값을 전달하여 채웁니다. 예를 들어, {"name": "홍길동"}이라는 데이터를 전달하면, 최종 프롬프트는 "안녕하세요, 홍길동님. 어떻게 도와드릴까요?"가 됩니다.
- (step3) 유연한 프롬프트 생성: 이렇게 하면, 동일한 템플릿을 다양한 상황에 맞게 재사용할 수 있습니다. 예를 들어, 사용자 이름이나 특정 상황에 따라 프롬프트 내용을 동적으로 변경할 수 있습니다.
-
AI 모델과의 통합: 이렇게 생성된 구조화된 프롬프트는 대화형 AI 모델과 직접 사용될 수 있습니다. 모델은 명확하게 정의된 입력을 받아, 기대하는 대화 맥락에 맞는 적절한 응답을 생성할 수 있습니다.
🤔 코드 변경 시행착오
어쨌든 ChatPromptTemplate방식으로 바꿨는데, 시행착오가 있었다.
[ 초반 프롬프트 템플릿 ]
prompt = ChatPromptTemplate.from_messages([
("system", "You are an assistant, and your task is to answer only in Korean as if you were a visiting patient. The doctor is asking questions to better understand your symptoms. You answer the doctor's questions with simple and easy expressions."),
("human", message.question),
("assistant", "")
])
그러나, LM Studio의 화면에서 다음과 같은 에러가 발생했다.

에러 메시지
{"title":"'messages' array must only contain objects with a 'content' field that is not empty"}
LM Studio에서 발생한 오류메시지를 보면, messages배열에 포함된 객체 중 하나가 빈 content필드를 가지고 있어 문제가 발생한 것으로 보였습니다. ChatPromptTemplate에서 assistant역할의 메시지를 빈 문자열로 설정했기 때문에 발생한 문제입니다.
이를 해결하기 위해서는 assistant 역할의 메시지를 빈 문자열로 설정하지 않고, 대신 해당 메시지를 생략하거나 적절한 초기 메시지를 설정해야 합니다.
일단은 당장은 생략했는데, 내 생각에 나중에 AI가 먼저 "안녕~ 우리 지금부터 병원놀이를 시작해보자~"이런식으로 먼저 말을 걸 때 이 방법을 쓰면 구현할 수 있을 것 같다는 생각이 든다.
[ assistant 역할의 메시지에 문자열을 직접 넣는 경우 ]
-
초기 응답 제공
예시: AI가 대화의 시작 부분에서 특정한 초기 응답을 제공해야 할 때, assistant 역할에 문자열을 넣을 수 있습니다. 이는 사용자가 대화를 시작하기 전에 AI가 먼저 정보를 제공하거나, 특정한 안내를 해야 하는 경우에 유용합니다.
-
고정된 응답
예시: 특정 질문에 대해 항상 동일한 응답을 제공해야 하는 경우, assistant 역할에 고정된 응답을 미리 설정할 수 있습니다. 예를 들어, "안녕하세요, 무엇을 도와드릴까요?"와 같은 인사말을 설정할 수 있습니다.
- 환영 메시지: AI가 대화의 시작 부분에서 "안녕하세요! 무엇을 도와드릴까요?"와 같은 인사말을 제공할 수 있습니다.
- 지침 제공: AI가 대화의 목적이나 사용 방법에 대해 설명할 수 있습니다. 예를 들어, "저는 건강 관련 질문에 답변할 수 있는 AI입니다. 어떤 증상이 있으신가요?"와 같은 메시지를 통해 사용자가 어떤 질문을 할 수 있는지 안내할 수 있습니다.
-
대화 흐름 제어
예시: 대화의 흐름을 특정 방향으로 유도하기 위해 AI가 특정 메시지를 미리 설정할 수 있습니다. 이는 대화의 맥락을 설정하거나, 사용자가 특정 작업을 수행하도록 유도하는 데 사용될 수 있습니다.
어쨌든, 일단은 assistant를 생략했습니다.
👋 최종적으로 변경된 fastAPI 서버 코드
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
app = FastAPI()
# CORS 설정
origins = [
"http://localhost",
"http://localhost:3000",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["POST"],
allow_headers=["Content-Type"],
)
llm = ChatOpenAI(
base_url="http://localhost:5000/v1",
api_key="lm-studio",
model="sangthree/eeve_gguf",
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
)
class Message(BaseModel):
question: str
@app.post("/chat")
async def chat_with_bot(message: Message):
try:
# 'assistant' 역할의 빈 메시지를 제거
prompt = ChatPromptTemplate.from_messages([
("system", "You are an assistant, and your task is to answer only in Korean as if you were a visiting patient. The doctor is asking questions to better understand your symptoms. You answer the doctor's questions with simple and easy expressions."),
("human", message.question)
])
chain = prompt | llm | StrOutputParser()
response = chain.invoke({"question": message.question})
return {"response": response}
except Exception as e:
print(f"Error: {e}")
raise HTTPException(status_code=500, detail="Internal Server Error")
위의 코드대로 작동시켰을때 결과이다.

문제가 해결된 것을 확인할 수 있었다.
[ 참고 ] 위 움짤 돌릴때, LM Studio 의 Server logs에 찍힌 코드
[2024-08-09 03:15:47.930] [INFO] Received POST request to /v1/chat/completions with body: {
"messages": [
{
"content": "You are an assistant, and your task is to answer only in Korean as if you were a visiting patient. The doctor is asking questions to better understand your symptoms. You answer the doctor's questions with simple and easy expressions.",
"role": "system"
},
{
"content": "안녕하세요",
"role": "user"
}
],
"model": "sangthree/eeve_gguf",
"n": 1,
"stream": true,
"temperature": 0.7
}
[2024-08-09 03:15:47.931] [INFO] [LM STUDIO SERVER] Context Overflow Policy is: Rolling Window
[2024-08-09 03:15:47.931] [INFO] [LM STUDIO SERVER] Streaming response...
[2024-08-09 03:15:49.602] [INFO] [LM STUDIO SERVER] First token generated. Continuing to stream response..
[2024-08-09 03:15:54.318] [INFO] Finished streaming response
[2024-08-09 03:16:01.178] [INFO] [LM STUDIO SERVER] Processing queued request...
[2024-08-09 03:16:01.179] [INFO] Received POST request to /v1/chat/completions with body: {
"messages": [
{
"content": "You are an assistant, and your task is to answer only in Korean as if you were a visiting patient. The doctor is asking questions to better understand your symptoms. You answer the doctor's questions with simple and easy expressions.",
"role": "system"
},
{
"content": "어디가 아파서 왔어요?",
"role": "user"
}
],
"model": "sangthree/eeve_gguf",
"n": 1,
"stream": true,
"temperature": 0.7
}
[2024-08-09 03:16:01.179] [INFO] [LM STUDIO SERVER] Context Overflow Policy is: Rolling Window
[2024-08-09 03:16:01.180] [INFO] [LM STUDIO SERVER] Streaming response...
[2024-08-09 03:16:03.741] [INFO] [LM STUDIO SERVER] First token generated. Continuing to stream response..
[2024-08-09 03:16:10.315] [INFO] Finished streaming response
[2024-08-09 03:16:18.536] [INFO] [LM STUDIO SERVER] Processing queued request...
[2024-08-09 03:16:18.537] [INFO] Received POST request to /v1/chat/completions with body: {
"messages": [
{
"content": "You are an assistant, and your task is to answer only in Korean as if you were a visiting patient. The doctor is asking questions to better understand your symptoms. You answer the doctor's questions with simple and easy expressions.",
"role": "system"
},
{
"content": "언제부터 아팠어요?",
"role": "user"
}
],
"model": "sangthree/eeve_gguf",
"n": 1,
"stream": true,
"temperature": 0.7
}
[2024-08-09 03:16:18.538] [INFO] [LM STUDIO SERVER] Context Overflow Policy is: Rolling Window
[2024-08-09 03:16:18.538] [INFO] [LM STUDIO SERVER] Streaming response...
[2024-08-09 03:16:20.961] [INFO] [LM STUDIO SERVER] First token generated. Continuing to stream response..
[2024-08-09 03:16:30.648] [INFO] Finished streaming response
🤔 현재 상황
현재 유정이가 최근에 야놀자의 EEVE 모델을 기반으로 상쓰리의 커스텀 데이터셋을 파인튜닝한 모델을 사용하고 있다. 그러나, 기존 fastAPI 서버코드로 프로그램을 작동시켰을때는, 결과에 문제가 있었다.
[ 기존 fastAPI 서버코드 ]
위의 결과를 보면 알겠지만, 기존 서버 코드로 응답을 도출했을때는, 혼자서.. 마치 시나리오를 짜는 것 같았다. 나는 모델이 혼자 시나리오를 작성하는 듯한 응답을 생성하는 이유로
프롬프트의 명확성 부족을 뽑았다.💫 프롬프트의 명확성 부족은 다음과 같이 구분할 수 있다.
(1)번을 처음에 시도하고 있었는데, 보영이로부터 연락이 왔다.

그래서 ChatPromptTemplate을 알아보기 시작했다ㅎㅎ
✏️ PromptTemplate VS. ChatPromptTemplate
일단 기존에 사용했던
PromptTemplate과ChatPromptTemplate의 차이에 대해 살펴보자.[ PromptTemplate ]
PromptTemplate은 일반적인 프롬프트 템플릿을 생성하는 데 사용됩니다. 이는 대화형 요소가 필요 없는 표준 언어 모델 작업에 적합합니다.[ ChatPromptTemplate ]
ChatPromptTemplate은 대화형 언어 모델을 위해 특별히 설계된 템플릿입니다. 시스템, 인간, AI등 다양한 역할 간의 상호작용을 포함하는 프롬프트를 구성하는 데 사용됩니다.from_template()및from_messages()와 같은 메서드를 사용하여 대화 상호작용에 맞춘 프롬프트를 쉽게 생성할 수 있습니다.🥲 시행착오 (명시적 방법 --> from_messages()사용)
처음에는 보영이가 말해준대로,
SystemMessagePromptTemplate,AIMessagePromptTemplate,HumanMessagePromptTemplate을 명시적으로 사용하려고 했으나ㅠㅠㅠ 정말 많은 시도를 해도 실패했다 (자꾸 시도만 하면 500번 에러가 발생하였다.)물론, 해결할 수 있는 방법이 있을 수 있지만, 계속 시도끝에 안되서 명시적으로 말고, 찾아보니
from_messages()와 같은 메서드를 사용하여 프롬프트를 쉽게 생성할 수 있다고 하여, 그 방법을 시도해보기로 하였다.=> 아하 그런데 지금보니깐, 보영이가 참고하라고 보내준 colab코드에
from_messages()를 사용한 걸 확인할 수 있었다.✏️ from_messages()의 작동 방식 설명
역할과 내용 짝짓기: from_messages() 메서드에 전달되는 각 튜플은 역할**(예: "system", "human", "ai")**과 해당 역할에 대한 메시지 내용을 포함합니다. 예를 들어, ("system", "You are a helpful AI.")는 AI에게 주어진 시스템 메시지를 정의합니다.자동 역할 처리: 메서드는 각 튜플의 역할을 인식하고, 프롬프트를 적절하게 구성합니다. 이를 통해 대화의 각 메시지가 어떤 역할에 속하는지를 자동으로 이해합니다. 예를 들어, 시스템 명령, 사용자 질문, AI 응답 등을 구분합니다.동적 메시지 삽입: 메시지 내용에 자리 표시자를 사용할 수 있으며, 이는 프롬프트가 호출될 때 실제 데이터로 채워질 수 있습니다. 이를 통해 유연하고 상황에 맞는 프롬프트 생성을 지원합니다.AI 모델과의 통합: 이렇게 생성된 구조화된 프롬프트는 대화형 AI 모델과 직접 사용될 수 있습니다. 모델은 명확하게 정의된 입력을 받아, 기대하는 대화 맥락에 맞는 적절한 응답을 생성할 수 있습니다.🤔 코드 변경 시행착오
어쨌든 ChatPromptTemplate방식으로 바꿨는데, 시행착오가 있었다.
[ 초반 프롬프트 템플릿 ]
그러나, LM Studio의 화면에서 다음과 같은 에러가 발생했다.

에러 메시지LM Studio에서 발생한 오류메시지를 보면,messages배열에 포함된 객체 중 하나가 빈content필드를 가지고 있어 문제가 발생한 것으로 보였습니다.ChatPromptTemplate에서assistant역할의 메시지를 빈 문자열로 설정했기 때문에 발생한 문제입니다.이를 해결하기 위해서는 assistant 역할의 메시지를 빈 문자열로 설정하지 않고, 대신 해당 메시지를 생략하거나 적절한 초기 메시지를 설정해야 합니다.
일단은 당장은 생략했는데, 내 생각에 나중에 AI가 먼저 "안녕~ 우리 지금부터 병원놀이를 시작해보자~"이런식으로 먼저 말을 걸 때 이 방법을 쓰면 구현할 수 있을 것 같다는 생각이 든다.
[ assistant 역할의 메시지에 문자열을 직접 넣는 경우 ]
초기 응답 제공예시: AI가 대화의 시작 부분에서 특정한 초기 응답을 제공해야 할 때, assistant 역할에 문자열을 넣을 수 있습니다. 이는 사용자가 대화를 시작하기 전에 AI가 먼저 정보를 제공하거나, 특정한 안내를 해야 하는 경우에 유용합니다.
고정된 응답예시: 특정 질문에 대해 항상 동일한 응답을 제공해야 하는 경우, assistant 역할에 고정된 응답을 미리 설정할 수 있습니다. 예를 들어, "안녕하세요, 무엇을 도와드릴까요?"와 같은 인사말을 설정할 수 있습니다.
대화 흐름 제어예시: 대화의 흐름을 특정 방향으로 유도하기 위해 AI가 특정 메시지를 미리 설정할 수 있습니다. 이는 대화의 맥락을 설정하거나, 사용자가 특정 작업을 수행하도록 유도하는 데 사용될 수 있습니다.
어쨌든, 일단은 assistant를 생략했습니다.
👋 최종적으로 변경된 fastAPI 서버 코드
위의 코드대로 작동시켰을때 결과이다.

문제가 해결된 것을 확인할 수 있었다.
[ 참고 ] 위 움짤 돌릴때, LM Studio 의 Server logs에 찍힌 코드