3장. LangChain 기본 구조와 LCEL
다양한 프롬프트 1: 역할·형식·제약 지시
꽃집에 꽃다발을 주문할 때 "꽃다발 하나요"라고만 하면 꽃집 주인의 취향대로 나옵니다. "어머니 생신 선물이에요(누구를 위해), 손에 들기 좋은 작은 크기로요(어떤 꼴로), 백합은 빼 주세요(하지 말 것)"라고 하면 원하는 꽃다발에 훨씬 가까워집니다. 프롬프트도 같습니다. 질문만 던지는 것보다 역할, 형식, 제약을 적어 주면 답이 원하는 꼴에 가까워집니다.
세 상자가 시스템 메시지에 적는 세 가지 지시입니다. 역할은 "누구로서 답하나", 형식은 "어떤 꼴로 답하나", 제약은 "하지 말 것과 경계"입니다. 상자 아래 줄이 이 장에서 쓰는 예입니다. 여기서 "어머니 생신 선물"이 역할, "작은 크기"가 형식, "백합은 빼고"가 제약입니다. 비유와 다른 점은, 꽃집 주인은 지시를 어기면 다시 만들어 주지만 모델은 지시를 가끔 어기므로 우리가 결과를 확인해야 한다는 것입니다.
세 가지 지시
| 지시 | 묻는 것 | 예 | 없으면 |
|---|---|---|---|
| 역할(role) | 누구로서 답하나 | 너는 구름다리여행의 친절한 상담원이다 | 아무에게나 하는 일반적인 답 |
| 형식(format) | 어떤 꼴로 답하나 | 글머리표(-) 세 줄 이내 | 길이와 꼴이 그때그때 다르다 |
| 제약(constraint) | 하지 말 것, 모를 때 | 가격은 지어내지 않는다. 모르면 상담원 연결 | 그럴듯한 숫자를 지어낼 수 있다 |
constraint 는 "제한, 묶어 두는 것"이라는 뜻입니다. 세 지시는 모두 시스템 메시지에 적고, 손님의 질문은 human 줄에 따로 둡니다.
짧은 예시
세 지시를 변수로 따로 적어 두고 이어 붙여 시스템 메시지를 만듭니다. 지시를 따로 두면 하나만 바꿔 가며 견주기 쉽습니다.
from langchain_core.prompts import ChatPromptTemplate
ROLE = "너는 구름다리여행의 친절한 상담원이다."
FORMAT = "답은 글머리표(-) 세 줄 이내로 쓴다."
LIMIT = "모르는 것은 '상담원에게 연결해 드릴게요'라고 답하고, 가격은 지어내지 않는다."
# 세 지시를 줄바꿈으로 이어 시스템 메시지 하나로 만든다
prompt = ChatPromptTemplate.from_messages([("system", "\n".join([ROLE, FORMAT, LIMIT])), ("human", "{question}")])
messages = prompt.invoke({"question": "제주도 3박 4일 얼마예요?"}).to_messages()
print(messages[0].content)
print("---")
print(messages[1].content)너는 구름다리여행의 친절한 상담원이다.
답은 글머리표(-) 세 줄 이내로 쓴다.
모르는 것은 '상담원에게 연결해 드릴게요'라고 답하고, 가격은 지어내지 않는다.
---
제주도 3박 4일 얼마예요?손님은 가격을 물었지만 FAQ 에는 가격이 없습니다. 제약 지시가 없으면 모델이 그럴듯한 금액을 지어낼 수 있습니다. 1장의 "환각과 지식 마감일" 절에서 본 처방 "모르면 모른다고 답하라"를 시스템 메시지에 넣은 것입니다. 실습 4에서 지시가 있을 때와 없을 때 답의 꼴을 견줍니다.
코드가 의미하는 것
이 코드는 꽃다발 주문서에 "누구를 위해, 어떤 꼴로, 무엇은 빼고"를 한 줄씩 적어 넣는 일을 흉내 낸 것입니다. 지시 셋을 따로 적고, 이어 붙여 시스템 메시지로 만들고, 손님 질문은 다른 줄에 넣습니다.
| 코드 | 하는 일 | 챗봇에서 뜻하는 것 |
|---|---|---|
ROLE, FORMAT, LIMIT | 역할, 형식, 제약 지시를 변수 셋에 따로 둔다 | 챗봇의 성격, 답의 꼴, 하지 말 것을 나눠 두면 하나만 바꿔 가며 견주기 쉽습니다 |
"\n".join([ROLE, FORMAT, LIMIT]) | 세 지시를 줄바꿈으로 이어 글 하나로 만든다 | 세 지시가 시스템 메시지 하나에 들어갑니다 |
("human", "{question}") | 손님 질문을 human 줄에 따로 둔다 | 손님 글이 규칙과 섞이지 않게 합니다 |
messages[0].content, messages[1].content | 채운 두 메시지의 글을 꺼낸다 | 모델에게 가는 규칙과 질문을 따로 확인합니다 |
결과에서 읽을 것은 두 가지입니다.
- 시스템 메시지가 지시 세 줄입니다.
---위의 세 줄이 역할, 형식, 제약 차례로 들어갔습니다. - 손님 질문은 따로 갑니다.
---아래의 가격 질문은 human 메시지에 있습니다. 이 질문에 제약 지시가 어떻게 작동하는지는 실습 4에서 봅니다.
주의할 점
- 지시는 "~하지 마라"보다 "~한다"로 적는 편이 잘 지켜집니다. "길게 쓰지 마라" 대신 "세 줄 이내로 쓴다"처럼 무엇을 할지 적습니다.
- 지시가 서로 부딪치지 않게 합니다. "자세히 설명한다"와 "세 줄 이내로 쓴다"를 함께 적으면 모델이 어느 쪽을 따를지 그때그때 달라집니다.