일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 개인회고
- 알고리즘스터디
- 백준
- mrc
- 정렬
- 그래프이론
- 그리디
- 부스트캠프_AITech_3기
- dp
- 백트랙킹
- python3
- Level2
- 부스트캠프_AITech3기
- ODQA
- 구현
- U_stage
- Level1
- 글또
- 파이썬 3
- 기술면접
- 단계별문제풀이
- 알고리즘_스터디
- 프로그래머스
- dfs
- 최단경로
- 다시보기
- Level2_PStage
- 이진탐색
- 이코테
- 주간회고
- Today
- Total
국문과 유목민
[Langchain]Langchain의 프롬프트를 파일로 관리해보자! [YAML, JSON] 본문
LangChain이나 LangGraph는 LLM 서비스 구축을 쉽게 도와주는 프레임워크이다보니 현업에서도 많이 사용하게 됩니다. 특히, 프롬프트만을 조정함으로써 성능이 좋은 LLM 모델들을 우리 도메인에 피팅시켜 사용할 수 있는 것은 큰 매력 포인트인 것 같다. 그러나 프롬프트를 만드는 작업은 계속해서 텍스트를 수정하고, 교체하며 테스트해야하는 과정이 필수적이다보니, 형상관리가 필요하다고 생각만 했었습니다. 그러던 중 파일로도 프롬프트를 관리할 수 있다는 것을 알게 됐는데, 튜토리얼에도 파일을 로드하는 방법만 나와있을 뿐 어떤 변수가 포함되어야 하는 지와 같은 정보가 없어서 이를 한 번 정리해보면 도움이 될 수 있을 것 같아 글을 작성하게 되었습니다.
랭체인 프롬프트 템플릿
LangChain 프롬프트에 대해서 알고 계신 분들은 다음 장으로 넘어가셔도 됩니다.
프롬프트 템플릿은 사용자 입력 및 매개 변수를 언어 모델에 대한 지침으로 변환하는 데 도움을 줍니다. 이는 모델의 응답을 유도하는 데 사용할 수 있으며, 컨텍스트를 이해하며 일관되고 관련성 있는 출력을 생성하는 데 도움을 줍니다. 프롬프트 템플릿은 딕셔너리를 입력으로 사용하며, 각 키는 프롬프트 템플릿의 변수를 나타냅니다. 프롬프트 템플릿은 PromptValue를 출력합니다. 프롬프트 템플릿은 크게 StringPromptTemplates
과 ChatPropmtTemplates
로 나뉘며, 자세한 사용법은 다음과 같습니다.
StringPromptTemplates
하나의 문자열의 형식을 지정하는 데 사용되는 템플릿으로, 간단한 입력에 사용됩니다. PromptTemplate을 생성하고 사용하는 일반적인 방법의 예는 다음과 같습니다.
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template("해당 {topic}에 대해 농담을 해봐")
prompt_template.invoke({"topic": "cats"})
ChatPromptTemplates
목록 형식의 메시지를 지정하는 데 사용되는 템플릿으로, 템플릿들을 목록으로 구성합니다. ChatPromptTemplate을 생성하고 사용하는 일반적인 방법의 예는 다음과 같습니다.
- 해당 예시에서는 system과 user의 프롬프트 템플릿이 설정되어 있고, 호출될 경우 아래 두 개의 메시지를 생성합니다.
- SystemMessage는 변수가 없는 포맷이라 그대로 반환되며, 두 번째 HumanMessages는 사용자가 전달하는
topic
변수에 의해 형식이 지정됩니다.
from langchain_core.prompts import ChatPromptTemplate
prompt_template = ChatPromptTemplate([
("system", "너는 내가 한 질문에 대해 최대한 열심히 답변을 해야해"),
("user", "해당 {topic}에 대해 농담을 해봐")
])
prompt_template.invoke({"topic": "cats"})
MessagePlaceholder
추가적으로 MessagesPlaceholder가 있습니다. 해당 프롬프트 템플릿은 특정 위치에 string이 아닌, 프롬프트 메시지 목록을 추가할 수 있게 해줍니다. 해당 포맷은 다음과 같은 상황에서 사용됩니다.
- 사용자로부터 어떤 질문(HumanMessage)을 받게될 지 정해지지 않은 경우, 모든 케이스를 예측해 중복되는 Systemmessage를 가지는 ChatPromptTemplate을 만드는 것은 비효율적일 수 있습니다. MessagesPlaceholder를 활용하면 사용자로부터 다양한 질문을 받을 수 있습니다.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage
prompt_template = ChatPromptTemplate([
("system", "너는 다양한 도움을 주는 도구야"),
MessagesPlaceholder("msgs") # "msgs"
])
human_message = HumanMessage(content="해당 {topic}에 대해 농담을 해봐")
prompt_template.invoke({
"msgs": [human_message.format(topic="cats")]
})
# `placetholder`로 위와 같이 클래스를 명시적으로 사용하지 않고 동일한 작업 수행 가능
prompt_template = ChatPromptTemplate([
("system", "너는 다양한 도움을 주는 도구야"),
("placeholder", "{msgs}")
])
# 위와 동일 #
PromptTemplate을 파일로 관리하기
이제 본격적으로 위와 같은 PromptTemplate을 파일로 관리하는 방법을 알아보고자 합니다. LangChain의 공식 Docs를 보면, load_prompt
라는 기능이 있다는 것을 알 수 있습니다. 하지만, 이를 사용하기 위해서는 해당 파일들을 어떻게 구성해야 하는 지에 대해서는 가이드가 나와있지 않아 Source코드를 살펴보며, prompt 파일을 만드는 방법과 이를 사용하는 방법에 대해서 알아보겠습니다.
Langchain에서 지원하는 프롬프트 포맷
해당 포스팅은 'langchain-core==0.3.15'를 기준으로 작성이 되어 있으며, 해당 기능의 경우 발전 중인 것으로 보이기에 참고 부탁드립니다.
LangChain의 loading mdoule을 확인해보면, YAML과 JSON 두 가지 포맷을 지원하는 것을 확인할 수 있습니다. 해당 포스팅에서는 두 가지 방식을 모두 다뤄보고자 합니다.그 전에 앞서 YAML과 JSON은 무엇인지에 대해서 간단히 살펴보겠습니다.
- JSON(JavaScript Object Notation): 키-값 쌍과 배열을 기반으로 한 간단하고 기계 친화적인 데이터 표현 방식.
{
"name": "John Doe",
"age": 30,
"skills": ["Python", "JavaScript", "SQL"]
}
- YAML(YAML Ain’t Markup Language): 들여쓰기를 사용하여 계층 구조를 표현하며, 사람이 읽고 쓰기 쉬운 형식.
name: John Doe
age: 30
skills:
- Python
- JavaScript
- SQL
프롬프트를 파일로 관리함에 있어서 YAML파일이 조금 더 효율적이라고 생각합니다.
- 주석을 지원
- 여러 단락으로 이루어진 프롬프트 관리 용이(JSON의 경우 '\n'을 통해 문단을 나눠줘야 함)
YAML파일로 프롬프트 관리하기
우선, YAML 파일로 Prompt를 관리하는 방법을 알아보도록 하겠습니다. (기본 template에 포함되는 변수들은 동일하기에, 새로 등장하는 변수들에 대해서만 추가 설명을 하겠습니다) 로드할 수 있는 프롬프트 유형은 ["prompt", "chat", "few_shot"]으로 각 type 정의에 필요한 변수들과 사용 방법에 대해서 작성해보겠습니다.
PromptTemplate: 'prompt' type
가장 기본적인 propmt type으로, String형태의 prompt를 생성합니다.
# type_prompt_sample.yml
_type: prompt
template: |
{date} 주어지는 주제 [{topic}]에 대해 농담을 작성해주는 서비스야
input_variables: ["topic"]
partial_variables: {"date": "2025-01-01"}
output_parser: default
template_format: f-string
name: Joke Maker
description: Create jokes about the given topic
_type
: 프롬프트 파일의 경우 기본적으로 type을 명시해줘야 합니다._type
으로 설정 가능한 값은 다음과 같으며, 입력하지 않을 시 기본값(default)는prompt
입니다.- ["prompt", "chat", "few_shot"]
template
: 실제 프롬프트로 작성할 텍스트 ( '|'를 입력 후 밑에 텍스트를 그대로 작성하면 됩니다)template_path
: , `template` 대신 사용할 수 있는 변수입니다. template에 해당되는 txt 파일이 있다면 경로를 지정해줍니다.template
이나template_path
는 둘 중 하나만 사용 가능합니다. (중복될 경우 오류가 발생합니다)- ex.
template_path: example_template.txt
input_varialbles
: template 상에서 '{ }'' 로 정의된 변수를 자동으로 할당합니다.- 그러나 코드 가독성 및 유지보수를 위해 입력하는 것을 권장합니다.
partial_variables
:- 사용자에게 변수를 입력받지는 않지만, 동적으로 변수를 변경해서 넣어줘야 하는 경우(날짜, 시간 등)에 사용됩니다.
partial_variables
와input_variables
간에 중복이 없어야 합니다. (중복될 경우 오류가 발생합니다)partial_variables
의 경우 prompt로드 후 동적으로 변경하는 것을 추천합니다.(prompt.partial(key=date))
아래 변수들은 현재 버전에서는 하나의 경우만 지원하며, 굳이 변수를 정의하지 않아도 default값으로 사용할 수 있습니다.
outpur_parser
: StrOutputParser 지원 (default만 지원)template_format
: f-string 사용
아래 변수들은 Template 생성 시 직접적으로 필요하지는 않지만, 템플릿을 관리하기 위해 추가가 가능한 변수입니다.
name
: 프롬프트의 이름description
: 프롬프트에 대한 설명
from langchain_core.prompts import load_prompt
prompt = load_prompt("prompt/prompt_sample.yaml", encoding="utf-8")
# partial 변수 주는 법
from datetime import datetime
prompt = prompt.partia1l(date=datetime.today().strftime('%Y-%m-%d'))
prompt.invoke("cats")
# StringPromptValue(text='2025-01-19 주어지는 주제 [cats]에 대해 농담을 작성해주는 서비스야')
PromptTemplate: 'chat' type
Chat type의 경우 목록 형식(System, Human)의 메시지를 지정하는 데 사용되는 템플릿으로, 템플릿들을 목록으로 구성합니다.
- 다만, load_prompt 시 chat type의 경우 HumanTemplate 하나의 string만을 input으로 받게됩니다 (위 ChatPromptTemplate과는 일부 다름)
# type_chat_sample.yml
_type: chat
messages:
- prompt:
template: 주어지는 주제 [{topic}]에 대해 농담을 작성해주는 서비스야
input_variables: "topic"
prompt = load_prompt("prompt/chat_prompt_sample.yaml", encoding="utf-8")
prompt
# ChatPromptTemplate(input_variables=['topic'], input_types={}, partial_variables={},
messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['topic'], input_types={}, partial_variables={},
template='주어지는 주제 [{topic}]에 대해 농담을 작성해주는 서비스야'), additional_kwargs={})])
위와 같이 chat type의 경우 HumanTemplate에 대해서만 설정이 가능하기 때문에, 다음과 같은 파일을 통해 config 파일을 로드해 위에서 정의한 ChatPromptTemplate처럼 사용하는 방법도 있습니다.
# type_multi_chat_sample.yml
_type: chat
messages:
- prompt:
template:
- ("system", "너는 내가 한 질문에 대해 최대한 열심히 답변을 해야해")
- ("user", "해당 {topic}에 대해 농담을 해봐")
input_variables: "topic"
# load yaml file prompt_sample.yaml
from langchain_core.prompts import ChatPromptTemplate
import yaml
with open("prompt/chat_prompt_sample.yaml", "r", encoding="utf-8") as f:
chat_prompt_dict = yaml.safe_load(f)
messages = chat_prompt_dict['messages']
template = messages[0]["prompt"].pop("template") if messages else None
ChatPromptTemplate(template)
PromptTemplate: 'few_shot' type
few_shot type의 경우 few-shot prompting을 쉽게 구현하게 도와주는 것으로, 기본 prompt 형태에 앞에 prefix가 붙고, example들이 중간에 작성되고, 뒤에 suffix가 붙는 구조로 되어있습니다. 여기서 suffix가 HumanTemplate의 역할을 한다고 할 수 있습니다.
# type_fewshot_sample.yml
_type: few_shot
prefix: "질문에 대한 답을 도와주는 서비스"
suffix: "Question:\n{question}\nAnswer:"
example_prompt:
_type: prompt
template: Question:\n{question}\nAnswer:\n{answer}
partial_variables:
date: "2022-01-01"
input_variables: ["question", "answer"]
examples:
- question: "스티브 잡스와 아인슈타인 중 누가 더 오래 살았나요?"
answer: |
이 질문에 추가 질문이 필요한가요
추가 질문: 스티브 잡스는 몇 살에 사망했나요?
중간 답변: 스티브 잡스는 56세에 사망했습니다.
추가 질문: 아인슈타인은 몇 살에 사망했나요?
중간 답변: 아인슈타인은 76세에 사망했습니다.
최종 답변은: 아인슈타인
input_variables: ["question"]
prefix
: 시스템 프롬프트 처럼 컨텍스트 제공합니다.suffix
: 입력 질문과 답변 생성의 구조를 지정합니다.input_variables
: 사용자 입력 데이터를 템플릿에 연결.examples
와example_prompt
: Few-shot 학습 예제 제공합니다.
prompt = load_prompt("prompt/chat_prompt_sample.yaml", encoding="utf-8")
topic = "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"
final_prompt = prompt.format(question=topic)
PromptTemplate을 JSON 으로 관리하기
Json으로 관리하는 방법은 실제 config를 활용하는 방법과 크게 다르지 않습니다. 따라서 실제 프롬프트 생성 시 사용되는 변수는 동일하기에, JSON 포맷의 경우 프롬프트 파일 예시만 보여드리겠습니다.
PromptTemplate: 'prompt' type
{
"_type": "prompt",
"template": "{date} 주어지는 주제 [{topic}]에 대해 농담을 작성해주는 서비스야",
"input_variables": ["topic"],
"partial_variables": {
"date": "2022-01-01"
},
"output_parser": {
"_type": "default"
},
"template_format": "f-string",
"name": "Joke Maker",
"description": "Create jokes about the given topic"
}
PromptTemplate: 'chat' type
{
"_type": "chat",
"messages": [
{
"prompt": {
"template": "{topic}에 대해 농담을 해봐"
}
}
],
"input_variables": "topic"
}
PromptTemplate: 'fewshot' type
{
"_type": "few_shot",
"suffix": "Question:\n{question}\nAnswer:",
"prefix": "질문에 대한 답을 도와주는 서비스",
"examples": [
{
"question": "스티브 잡스와 아인슈타인 중 누가 더 오래 살았나요?",
"answer": "이 질문에 추가 질문이 필요한가요\n추가 질문: 스티브 잡스는 몇 살에 사망했나요?\n중간 답변: 스티브 잡스는 56세에 사망했습니다.\n추가 질문: 아인슈타인은 몇 살에 사망했나요?\n중간 답변: 아인슈타인은 76세에 사망했습니다.\n최종 답변은: 아인슈타인"
}
],
"example_prompt": {
"_type": "prompt",
"template": "Question:\n{question}\nAnswer:\n{answer}",
"partial_variables": {
"date": "2022-01-01"
},
"input_variables": ["question", "answer"]
},
"input_variables": ["question"]
}
'기술 견문록' 카테고리의 다른 글
[RBP] EC2, VM 대신 16만원으로 개인 서버 구축하기 (0) | 2025.02.16 |
---|