3장. LangChain 기본 구조와 LCEL
PromptTemplate
은행 창구의 입금표에는 "받는 분 ____, 금액 ____ 원"처럼 빈칸이 있습니다. 양식은 미리 인쇄해 두고, 손님마다 빈칸만 채웁니다. 프롬프트도 같습니다. "○○에서 ○일 동안 할 일을 추천해 줘"를 손님마다 새로 쓰지 않고, 빈칸이 있는 틀을 하나 만들어 두고 값만 바꿔 넣습니다.
왼쪽 상자가 중괄호로 빈칸을 표시한 틀, 가운데 상자가 넣을 값, 오른쪽 상자가 값이 채워진 완성된 글입니다. 여기서 인쇄된 입금표가 PromptTemplate, 손님이 쓰는 금액과 이름이 변수의 값입니다. 비유와 다른 점은, 입금표는 빈칸을 비워 내도 은행이 받아 주지만 프롬프트 템플릿은 빈칸 하나라도 비면 오류를 낸다는 것입니다.
틀을 다루는 방법
| 하는 일 | 코드 | 돌려주는 것 |
|---|---|---|
| 틀 만들기 | PromptTemplate.from_template("{place}에서 …") | 빈칸 있는 틀 |
| 빈칸 이름 보기 | template.input_variables | 빈칸 이름 목록(가나다·알파벳 차례) |
| 글로 채우기 | template.format(place="강릉", days=2) | 보통 글자열 |
| 부품으로 채우기 | template.invoke({"place": "경주", "days": 3}) | 프롬프트 값(StringPromptValue) |
| 일부만 먼저 채우기 | template.partial(days=2) | 빈칸이 줄어든 새 틀 |
format 은 이름표 인자로 값을 받고, invoke 는 사전 하나로 받습니다. 체인 안에서는 invoke 가 쓰이므로, 체인에 넣는 입력도 사전입니다.
짧은 예시
from langchain_core.prompts import PromptTemplate
# from_template: 글 속의 {이름} 을 빈칸으로 읽어 틀을 만든다
template = PromptTemplate.from_template("{place}에서 {days}일 동안 할 일을 세 가지 추천해 줘.")
print(template.input_variables)
# format: 빈칸에 값을 넣은 글자열을 돌려준다
print(template.format(place="강릉", days=2))
# invoke: 사전을 받아 프롬프트 값을 돌려준다. to_string() 으로 글을 꺼내 본다
value = template.invoke({"place": "경주", "days": 3})
print(type(value).__name__, "|", value.to_string())
# partial: days 만 먼저 채워 둔 새 틀. 이제 place 만 넣으면 된다
two_days = template.partial(days=2)
print(two_days.input_variables, two_days.format(place="여수"))['days', 'place']
강릉에서 2일 동안 할 일을 세 가지 추천해 줘.
StringPromptValue | 경주에서 3일 동안 할 일을 세 가지 추천해 줘.
['place'] 여수에서 2일 동안 할 일을 세 가지 추천해 줘.input_variables 는 글에 나온 차례가 아니라 이름의 차례(days, place)로 늘어놓습니다. partial 로 days 를 채운 틀은 빈칸이 place 하나만 남았습니다.
코드가 의미하는 것
이 코드는 입금표 양식 한 장을 인쇄해 두고 빈칸을 여러 방식으로 채워 보는 일을 흉내 낸 것입니다. 틀을 만들고, 빈칸 이름을 보고, 두 가지 방법으로 채우고, 한 칸만 먼저 채운 새 양식을 만듭니다.
| 코드 | 하는 일 | 챗봇에서 뜻하는 것 |
|---|---|---|
PromptTemplate.from_template("{place}에서 …") | 글 속의 중괄호를 빈칸으로 읽어 틀을 만든다 | 손님마다 프롬프트를 새로 쓰지 않고, 틀 하나를 두고 값만 바꿉니다 |
template.input_variables | 빈칸 이름 목록을 보여 준다 | 이 틀을 채우려면 무엇을 넣어야 하는지 알려 주는 목록입니다 |
template.format(place=…, days=…) | 채운 보통 글자열을 돌려준다 | 사람이 눈으로 확인할 때 쓰기 좋습니다 |
template.invoke({...}) | 사전을 받아 프롬프트 값을 돌려준다 | 체인 안에서 쓰이는 채우기입니다. 그래서 체인에 넣는 입력도 사전입니다 |
template.partial(days=2) | 일부만 채운 새 틀을 만든다 | 늘 같은 값은 미리 채워 둡니다. 실습 1에서 FAQ 를 이렇게 넣습니다 |
결과에서 읽을 것은 세 가지입니다.
- 빈칸 이름은 이름 차례로 찍힙니다. 첫 줄
['days', 'place']는 글에 나온 차례가 아닙니다. - format 과 invoke 는 꼴이 다릅니다. 둘째 줄은 글자열이고, 셋째 줄은
StringPromptValue입니다. 글은 같아도 체인에 넘기는 것은 프롬프트 값입니다. - partial 뒤에는 빈칸이 줄어듭니다. 넷째 줄의
['place']처럼 남은 빈칸만 채우면 됩니다.
주의할 점
- 빈칸을 빠뜨리고
invoke({"place": "경주"})처럼 부르면KeyError가 나고, 오류 글에 "missing variables {'days'}"(빠진 변수)가 적힙니다. 오류 글을 읽으면 어느 빈칸이 비었는지 바로 압니다. - 프롬프트에 중괄호를 글자 그대로 쓰고 싶으면
{{와}}처럼 두 번 겹쳐 적습니다. 한 번만 쓰면 빈칸으로 읽힙니다. JSON 예시를 프롬프트에 넣을 때 자주 걸립니다.