자동 프롬프트 최적화
자동 프롬프트 최적화 (Automated Prompt Optimization)
프롬프트 최적화(Prompt Optimization, PO)를 Mistral 모델과 MetaGPT의 SPO(Self-Supervised Prompt Optimization)로 수행하는 방법을 보여주는 노트북이에요. 지원서 응답을 분류하는 프롬프트를 예시로 삼아, 최적화 과정을 자동화합니다.
출처: 문서
본문
프롬프트 엔지니어링... 별로죠. 비표준적인 과정이고, 시행착오에 크게 의존하며 표준화하기 어렵습니다. 다행히 Self-Supervised Prompt Optimization 같은 최근 연구에서 다룬 프롬프트 최적화로 이를 자동화할 수 있어요. 본질적으로 PO는 어떤 작업을 수행하려는 프롬프트를 가져와 특정 문제에 더 적합하도록 반복적으로 개선하는 과정입니다. 이 노트북은 Mistral 모델로 PO를 사용하는 방법의 개요를 제공합니다.
작업 프롬프트 (Task prompts)
여러분이 지원서 양식을 만들었고 읽을 수 있는 것보다 훨씬 많은 답변을 받았다고 해 볼게요. 설문이 인기를 얻어서, 응답을 살펴봐야 하는데 응답은 평문 텍스트로만 받았습니다. 따라서 필터링은 불가능하지만, 가장 유망한 지원자를 식별하기 위해 지원서를 샅샅이 살펴볼 전략이 필요해요. 응답을 처리하고 효과적으로 필터링할 수 있는 출력을 내는 프롬프트 몇 개를 정의합니다.
이 프롬프트들은 의도적으로 최적화되지 않았으며, 작업에 쓰고 싶은 빠르고 대충 만든 예시로 삼는 것입니다. 이 예제에서는 Ambassadorship 프로그램 지원 과정에서 수집된 응답을 다룹니다.
# overarching prompt, giving context
context = (
"I am working on recruiting people to advocate about the products of an AI company. "
"The position in in close contact with the DevRel team, and we are looking at having people "
"share on their own personal social media more about the company and its products. "
"The company I work at produces Large Language Models and is very followed, "
"therefore I got a sheer amount of applications that I need to process "
"very soon. I won't be able to process them by hand, and there is little structure in the "
"form that we sent out to applicants. Therefore, I am expecting you to assist me into processing the "
"information these people gave to make it much more structured. This means that you do read "
"what applicants declared and extract key information based on the context of the question asked."
)
# classifying job titles
job_prompt = lambda job_title: (
"Your task is to provide me with a direct classification of the person's job title into one of 4 categories. "
"The categories you can decide are always: 'RESEARCH', 'ENGINEERING', 'BUSINESS', 'FOUNDER'. "
"There is no possibility for mixed assignments. You always assign one and one only category to each subject. "
"When in doubt, assign to 'OTHER'. You must strictly adhere to the categories I have mentioned, and nothing more. "
"This means that you cannot use any other output apart from 'RESEARCH', 'ENGINEERING', 'BUSINESS', 'FOUNDER', 'OTHER'. "
"Keep your answer very, very concise. Don't give context on your answer. As a matter of fact, only answer with one word "
"based on the category you deem the most appropriate. Absolutely don't change this. You will be penalized if "
"(1) you use a category outside of the ones I have mentioned and (2) you use more than 1 word in your output. "
f"# INPUT declared title: the person job title is {job_title}"
)
# getting the location in an easy way
location_prompt = lambda location: (
"Your task is basic. Your task is to disambiguate the respondent's answer in terms of the location used. "
"Your output is always CITY, COUNTRY. Use always the English name of a city. Also, always use the international "
"country code. Nothing else. For instance, if a user answered with 'Rome', you would output 'Rome, IT'. "
"In the rare case when someone puts down multiple locations, make sure you always select the first one. Nothing more"
f" #INPUT declared location: the respondent declared being located in {location}"
)
의존성 설치
MetaGPT를 통해 SPO를 사용하려면 저장소를 클론하고 이 노트북을 그 안으로 옮겨야 해요. 의존성이 쉽게 사용 가능하지는 않지만, 우회하는 것은 비교적 간단합니다.
# clone the repo
!git clone https://github.com/geekan/MetaGPT
# install dependancies
!pip install -qUr MetaGPT/requirements.txt
# move inside the directory, kernel-wise
%cd MetaGPT
지시 파일 만들기
MetaGPT를 설치한 후, 다루는 작업을 지정하는 yaml 파일을 만들어 프롬프트 최적화를 수행할 수 있습니다. MetaGPT 문서에 따르면 이 yaml 파일은 다음 구조가 필요해요.
prompt: |
Please solve the following problem.
requirements: |
...
count: None
qa:
- question: |
...
answer: |
...
- question: |
...
answer: |
...
최적화하려는 각 프롬프트에 대해 이런 템플릿 파일 하나씩을 생성해야 합니다. 다행히 자동으로 만들 수 있어요. 또 우리가 다루는 작업은 비교적 단순하므로 Q&A 형태의 few-shot 예시를 생략해도 됩니다. 그래도 이 템플릿 파일은 실제 few-shot 예시를 제공하는 매우 간단한 방법이니 살펴볼 가치가 있어요.
from typing import Optional
def prompt_to_dict(
prompt: str,
requirements: Optional[str],
questions: list[str],
answers: list[str],
count: Optional[int] = None,
)->dict:
return {
"prompt": prompt if isinstance(prompt, str) else prompt(""),
"requirements": requirements,
"count": count,
"qa": [
{
"question": question,
"answer": answer
} for question, answer in zip(questions, answers)
]
}
import yaml
prompts = {
"job": job_prompt,
"location": location_prompt
}
requirements = [
"The job title, categorized",
"The location, disambiguated"
]
path = "metagpt/ext/spo/settings" # this is the path where the template files needs to be saved
for (name, prompt), requirement in zip(prompts.items(), requirements):
# creating template files for each prompt
with open(f"{path}/{name}.yaml", "w") as f:
yaml.dump(
prompt_to_dict(
prompt,
requirement,
[""],
[""]
),
f,
)
모델 파일 만들기
각 프롬프트에 대한 템플릿 파일을 만들었다면, 서로 다른 프롬프트에 대해 (1) 실행자(executor) (2) 평가자(evaluator) (3) 최적화자(optimizer)로 사용할 모델을 지정해야 해요. MetaGPT의 SPO는 이 모델들을 특정 .yaml 파일 안에 제공하도록 요구합니다. 아래 스니펫으로 여러분의 Mistral API 키를 사용한 파일을 만들 수 있어요.
def models_dict(
mistral_api_key: str
)->dict:
return {
"llm": {
"api_type": "openai",
"model": "mistral-small-latest",
"base_url": "https://api.mistral.ai/v1/",
"api_key": mistral_api_key,
"temperature": 0
},
"models": {
"mistral-small-latest": {
"api_type": "openai",
"base_url": "https://api.mistral.ai/v1/",
"api_key": mistral_api_key,
"temperature": 0
},
"mistral-large-latest": {
"api_type": "openai",
"base_url": "https://api.mistral.ai/v1/",
"api_key": mistral_api_key,
"temperature": 0
}
}
}
path = "config/config2.yaml" # saving the models file here
MISTRAL_API_KEY = "ADD YOU KEY HERE" # your api key
with open(path, "w") as f:
yaml.dump(models_dict(MISTRAL_API_KEY), f)
프롬프트 최적화 실행하기
준비됐어요! (1) 후보 프롬프트의 템플릿 파일과 (2) 사용할 모델을 식별하는 models.yaml 파일이 있으면 최적화 라운드를 시작할 수 있습니다. 문제는 Jupyter 노트북이 asyncio와 잘 작동하지 않는다는 건데요. 프롬프트 최적화를 실행할 코드를 .py 파일로 내보낸 뒤 CLI 방식 명령으로 실행하면 됩니다. 여기서는 직책 추출 프롬프트용 파일 하나만 만듭니다. 이 프롬프트 최적화 과정을 서로 다른 파일로 내보내면 병렬 실행도 가능합니다. 데모를 위해 하나의 프롬프트(작업 추출)만 최적화하지만, 다른 프롬프트로 쉽게 바꿀 수 있어요.
%%writefile spo.py
from metagpt.ext.spo.components.optimizer import PromptOptimizer
from metagpt.ext.spo.utils.llm_client import SPO_LLM
# Initialize LLM settings
SPO_LLM.initialize(
# same temperature settings as metagpt's default!
optimize_kwargs={
"model": "mistral-large-latest",
"temperature": 0.6
},
evaluate_kwargs={
"model": "mistral-small-latest",
"temperature": 0.3
},
execute_kwargs={
"model": "mistral-small-latest",
"temperature": 0
}
)
template_name = "job.yaml" # change this for each prompt!
# Create and run optimizer
optimizer = PromptOptimizer(
optimized_path="workspace", # Output directory
initial_round=1, # Starting round
max_rounds=5, # Maximum optimization rounds
template=template_name, # Template file - Change this for each prompt!
name="Mistral-Prompt-Opt", # Project name
)
optimizer.optimize()
이제 프롬프트 최적화를 실행합니다.
!python spo.py
결과 평가하기 (Assessing the results)
원본 프롬프트 (Original Prompt)
Your task is to provide me with a direct classification of the person's job title into one of 4 categories. The categories you can decide are always: 'RESEARCH', 'ENGINEERING', 'BUSINESS', 'FOUNDER'. There is no possibility for mixed assignments. You always assign one and one only category to each subject. When in doubt, assign to 'OTHER'. You must strictly adhere to the categories I have mentioned, and nothing more. This means that you cannot use any other output apart from 'RESEARCH', 'ENGINEERING', 'BUSINESS', 'FOUNDER', 'OTHER'. Keep your answer very, very concise. Don't give context on your answer. As a matter of fact, only answer with one word based on the category you deem the most appropriate. Absolutely don't change this. You will be penalized if (1) you use a category outside of the ones I have mentioned and (2) you use more than 1 word in your output. # INPUT declared title: the person job title is {job_title}
최적화된 프롬프트 (Optimized Prompt)
Your task is to classify the given job title into one of the following categories: 'RESEARCH', 'ENGINEERING', 'BUSINESS', 'FOUNDER'. If the job title does not fit any of these categories, classify it as 'OTHER'. You must strictly adhere to these categories. If a job title is ambiguous or could fit into multiple categories, choose the most relevant category based on common industry standards. For example, 'Data Scientist' could fit into both 'RESEARCH' and 'ENGINEERING', but is typically classified as 'RESEARCH'. Similarly, 'Data Analyst' is typically classified as 'BUSINESS'. Provide your answer using one word only, in all uppercase letters without any additional context or explanations.# INPUT: The person's job title is: {job_title}# Example:# INPUT: The person's job title is: Software Developer# OUTPUT: ENGINEERING
결과를 보면 원본 프롬프트가 일반적인 모범 사례(LLM을 안내하는 예시 제공, 즉 few-shot prompting이나 입력 프롬프트의 특정 부분으로 모델의 주의를 끄는 태그 같은 요소 제공)에 따라 수정된 것을 알 수 있어요. 이 개정된 프롬프트는 단 5회의 최적화 "라운드"만으로 얻어졌고, 더 최적화할 수도 있습니다 (물론 블랙박스 최적화 문맥에서 최종적으로 만족스러운 성능은 휴리스틱일 뿐이지만요).
더 알아보기 (Learn more)
- MetaGPT 공식 문서 — 멀티에이전트 프레임워크 (SPO 포함)
metagpt.ext.spo— Self-Supervised Prompt Optimization 모듈PromptOptimizer— 프롬프트 최적화 실행 클래스- 모델:
mistral-large-latest(최적화자),mistral-small-latest(평가자·실행자)