3장. LangChain 기본 구조와 LCEL
ChatPromptTemplate
연극 대본은 "해설:", "철수:", "영희:"처럼 줄마다 누가 말하는지 적혀 있습니다. 공연마다 배우 이름이나 장소 같은 몇 군데만 바꿔 씁니다. 채팅 모델에게 보내는 프롬프트도 대본처럼 역할이 붙은 줄의 목록입니다. ChatPromptTemplate 은 이 대본의 틀입니다.
세 상자가 대본의 세 가지 줄입니다. system 줄에는 챗봇의 성격과 규칙을, human 줄에는 손님의 질문을, ai 줄에는 모델이 했다고 치는 답을 적습니다. ai 줄은 퓨샷처럼 예시 답을 보여 줄 때만 씁니다. 여기서 "해설:"이 system, 관객의 질문이 human, 배우의 대사가 ai 입니다. 1장에서 본 SystemMessage, HumanMessage, AIMessage 가 이 줄을 채운 결과입니다. 비유와 다른 점은, 대본의 배우는 대사를 그대로 읽지만 모델은 ai 줄을 "이런 꼴로 답하라"는 본보기로만 삼는다는 것입니다.
줄을 적는 법
| 줄 | 적는 꼴 | 채운 뒤 | 쓰는 곳 |
|---|---|---|---|
| system | ("system", "너는 {company}의 여행 비서다.") | SystemMessage | 성격, 말투, 규칙 |
| human | ("human", "{question}") | HumanMessage | 손님의 질문 |
| ai | ("ai", "{answer}") | AIMessage | 예시 답(퓨샷), 앞의 대화 |
from_messages 는 "메시지들로부터 만든다"는 뜻으로, 이 줄들의 목록을 받아 틀을 만듭니다. 줄마다 빈칸을 넣을 수 있고, 모든 줄의 빈칸이 한 목록으로 모입니다.
짧은 예시
from langchain_core.prompts import ChatPromptTemplate
# from_messages: (역할, 틀) 짝의 목록으로 채팅 프롬프트 틀을 만든다
prompt = ChatPromptTemplate.from_messages([
("system", "너는 {company}의 여행 비서다. {tone} 말투로 답한다."),
("human", "{question}"),
])
# 두 줄의 빈칸이 한 목록에 모인다
print(prompt.input_variables)
value = prompt.invoke({"company": "구름다리여행", "tone": "친절한", "question": "짐은 몇 kg 까지 되나요?"})
print(type(value).__name__)
# to_messages(): 프롬프트 값을 메시지 목록으로 꺼낸다
for m in value.to_messages():
print(type(m).__name__, "|", m.content)['company', 'question', 'tone']
ChatPromptValue
SystemMessage | 너는 구름다리여행의 여행 비서다. 친절한 말투로 답한다.
HumanMessage | 짐은 몇 kg 까지 되나요?채운 결과는 ChatPromptValue 이고, 안에는 1장에서 손으로 만들던 메시지 두 개가 들어 있습니다. 이제 메시지를 직접 만들지 않고 틀에 사전만 넣으면 됩니다.
코드가 의미하는 것
이 코드는 배우 이름만 비워 둔 연극 대본을 만들고, 공연 날 이름을 채워 넣는 일을 흉내 낸 것입니다. 역할이 붙은 줄 둘로 틀을 만들고, 빈칸을 보고, 채운 뒤, 메시지로 펼쳐 봅니다.
| 코드 | 하는 일 | 챗봇에서 뜻하는 것 |
|---|---|---|
ChatPromptTemplate.from_messages([("system", …), ("human", …)]) | 역할과 틀의 짝 목록으로 채팅 프롬프트 틀을 만든다 | 챗봇의 규칙(system)과 손님 질문(human)을 줄로 나눠 둔 대본입니다 |
prompt.input_variables | 모든 줄의 빈칸을 한 목록으로 모은다 | 회사 이름, 말투, 질문을 한 번에 넣으면 대본이 완성됩니다 |
prompt.invoke({...}) | 사전을 받아 ChatPromptValue 를 만든다 | 채팅 모델에 그대로 넣을 수 있는 꼴입니다 |
value.to_messages() | 프롬프트 값을 메시지 목록으로 꺼낸다 | 모델에게 실제로 가는 메시지를 눈으로 확인하는 방법입니다 |
결과에서 읽을 것은 두 가지입니다.
- 두 줄의 빈칸이 한 목록에 모였습니다. 첫 줄의
['company', 'question', 'tone']에는 system 줄의 빈칸 둘과 human 줄의 빈칸 하나가 함께 있습니다. - 채운 결과는 1차시의 메시지 객체입니다. 셋째, 넷째 줄의
SystemMessage,HumanMessage가 1장에서 손으로 만들던 것과 같은 꼴입니다. 이제 틀이 대신 만들어 줍니다.
주의할 점
- 역할 이름은
"system","human","ai"로 적습니다. OpenAI 식 이름인"user","assistant"도 받아 주지만, 한 교안 안에서는 한쪽으로 맞춥니다. 이 교안은 LangChain 식 이름을 씁니다. - 손님이 쓴 글은 human 줄의 빈칸에만 넣습니다. system 줄의 빈칸에 손님의 글을 넣으면, 손님이 "앞의 규칙은 무시해"라고 적었을 때 그 말이 규칙처럼 읽힐 수 있습니다.