[MLflow] LLM 프롬프트 엔지니어링 실험 관리하기 - 체계적인 프롬프트 튜닝과 결과 추적
[MLflow] LLM 프롬프트 엔지니어링 실험 관리하기 - 체계적인 프롬프트 튜닝과 결과 추적
🤖 MLflow로 LLM 프롬프트 실험 관리하기
“이 프롬프트가 더 좋았나? 저 프롬프트가 더 좋았나?”
LLM 시대에 가장 중요한 것은 바로 프롬프트 엔지니어링입니다!
🎯 왜 프롬프트 실험 관리가 필요한가?
LLM을 사용하다 보면 이런 경험 있으시죠?
1
2
3
4
5
6
7
8
❌ 전통적인 프롬프트 관리의 문제점:
"너는 도움이 되는 AI야" → 결과: 보통
"너는 전문가야" → 결과: 좀 더 나음
"너는 10년 경력 전문가야" → 결과: 훨씬 좋음!
하지만... 어떤 프롬프트를 언제 썼는지 기억이 안 나고,
결과 비교도 어렵고, 팀원들과 공유도 힘들어요 😫
MLflow LLM 기능을 사용하면:
- 🔍 모든 프롬프트 실험을 자동 추적
- 📊 결과를 체계적으로 비교
- 🤝 팀원들과 쉽게 공유
- 🎯 최고 성능 프롬프트를 쉽게 찾기
🚀 MLflow LLM 설치 및 설정
MLflow 서버 실행
1 2 3 4 docker run -d -p 5001:5001 --name mlflow \ -v $(pwd)/mlflow/db:/mlflow/db \ -v $(pwd)/mlartifacts:/mlartifacts \ my-mlflow-image
🔧 MLflow LLM 실습 - 프롬프트 엔지니어링 실험
🎯 실습 목표: 번역 프롬프트 최적화
다양한 번역 프롬프트를 테스트해서 가장 좋은 결과를 찾아보겠습니다!
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import mlflow
import openai
import os
from dotenv import load_dotenv
# 환경 변수 로드
load_dotenv()
# MLflow 설정
mlflow.set_tracking_uri("http://0.0.0.0:5001")
mlflow.set_experiment("llm_translation_experiment")
# OpenAI 클라이언트 설정
client = openai.OpenAI()
# 테스트할 원문
test_text = "The quick brown fox jumps over the lazy dog."
# 다양한 프롬프트 템플릿 정의
prompt_templates = {
"basic": "다음 영어를 한국어로 번역하세요: {text}",
"professional": """당신은 10년 경력의 전문 번역가입니다.
다음 영어 문장을 자연스러운 한국어로 번역하세요:
{text}""",
"context_aware": """당신은 문맥을 고려하는 전문 번역가입니다.
다음 영어 문장을 한국어로 번역할 때:
1. 자연스러운 한국어 표현을 사용하세요
2. 문맥상 가장 적절한 의미를 선택하세요
3. 원문의 뉘앙스를 살려주세요
영어 원문: {text}
한국어 번역:""",
"step_by_step": """다음 단계로 번역하세요:
1. 먼저 영어 문장을 분석하세요
2. 각 단어의 의미를 파악하세요
3. 한국어로 자연스럽게 번역하세요
영어 원문: {text}"""
}
# 프롬프트 실험 실행
for prompt_name, prompt_template in prompt_templates.items():
with mlflow.start_run():
# 프롬프트 생성
prompt = prompt_template.format(text=test_text)
# 파라미터 로깅
mlflow.log_param("prompt_name", prompt_name)
mlflow.log_param("model", "gpt-3.5-turbo")
mlflow.log_param("temperature", 0.7)
mlflow.log_param("max_tokens", 100)
# 프롬프트 템플릿 저장
mlflow.log_text(prompt_template, f"prompt_template_{prompt_name}.txt")
mlflow.log_text(prompt, f"full_prompt_{prompt_name}.txt")
try:
# OpenAI API 호출
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=100
)
# 결과 추출
translation = response.choices[0].message.content
# 결과 저장
mlflow.log_text(translation, f"translation_{prompt_name}.txt")
# 메트릭 계산 (간단한 예시)
translation_length = len(translation)
word_count = len(translation.split())
mlflow.log_metric("translation_length", translation_length)
mlflow.log_metric("word_count", word_count)
mlflow.log_metric("tokens_used", response.usage.total_tokens)
mlflow.log_metric("cost_usd", response.usage.total_tokens * 0.0000015) # 대략적인 비용
# 태그 추가
mlflow.set_tag("prompt_category", "translation")
mlflow.set_tag("language_pair", "en_to_ko")
mlflow.set_tag("model_provider", "openai")
print(f"✅ {prompt_name} 실험 완료!")
print(f" 번역 결과: {translation[:50]}...")
print(f" 토큰 사용량: {response.usage.total_tokens}")
print(f" 비용: ${response.usage.total_tokens * 0.0000015:.6f}")
print()
except Exception as e:
# 에러 로깅
mlflow.log_param("error", str(e))
mlflow.set_tag("status", "failed")
print(f"❌ {prompt_name} 실험 실패: {e}")
print("🎉 모든 프롬프트 실험 완료!")
print("🌐 브라우저에서 http://0.0.0.0:5001 으로 접속해서 결과를 확인하세요!")
실행 결과 예시:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
✅ basic 실험 완료!
번역 결과: 빠른 갈색 여우가 게으른 개를 뛰어넘는다.
토큰 사용량: 45
비용: $0.000068
✅ professional 실험 완료!
번역 결과: 민첩한 갈색 여우가 게으른 개 위로 뛰어넘었다.
토큰 사용량: 52
비용: $0.000078
✅ context_aware 실험 완료!
번역 결과: 재빠른 갈색 여우가 게으른 개를 훌쩍 뛰어넘었다.
토큰 사용량: 58
비용: $0.000087
✅ step_by_step 실험 완료!
번역 결과: 빠른 갈색 여우가 게으른 개 위로 점프했다.
토큰 사용량: 61
비용: $0.000092
🎉 모든 프롬프트 실험 완료!
🌐 브라우저에서 http://0.0.0.0:5001 으로 접속해서 결과를 확인하세요!
📊 결과 분석 및 비교
1. 저장된 실험 결과 불러오기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import mlflow
import pandas as pd
# 실험 결과 조회
mlflow.set_tracking_uri("http://0.0.0.0:5001")
experiment = mlflow.get_experiment_by_name("llm_translation_experiment")
runs = mlflow.search_runs(experiment_ids=[experiment.experiment_id])
print(f"📊 총 {len(runs)}개의 프롬프트 실험 완료!")
print("\n🏆 실험 결과 비교:")
print("=" * 60)
for _, run in runs.iterrows():
prompt_name = run.get('params.prompt_name', 'unknown')
tokens_used = run.get('metrics.tokens_used', 0)
cost = run.get('metrics.cost_usd', 0)
print(f"📝 {prompt_name:15} | 토큰: {tokens_used:3.0f} | 비용: ${cost:.6f}")
# 가장 효율적인 프롬프트 찾기
best_efficiency = runs.loc[runs['metrics.tokens_used'].idxmin()]
print(f"\n🎯 가장 효율적인 프롬프트: {best_efficiency['params.prompt_name']}")
print(f" 토큰 사용량: {best_efficiency['metrics.tokens_used']:.0f}")
2. 프롬프트 템플릿 다운로드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 최고 성능 프롬프트 다운로드
client = mlflow.tracking.MlflowClient()
best_run_id = best_efficiency['run_id']
# 아티팩트 목록 확인
artifacts = client.list_artifacts(best_run_id)
print("📦 저장된 아티팩트:")
for art in artifacts:
print(f" - {art.path}")
# 프롬프트 템플릿 다운로드
prompt_template_path = f"prompt_template_{best_efficiency['params.prompt_name']}.txt"
local_path = client.download_artifacts(best_run_id, prompt_template_path)
with open(local_path, 'r', encoding='utf-8') as f:
best_prompt_template = f.read()
print(f"\n🏆 최고 성능 프롬프트 템플릿:")
print("=" * 40)
print(best_prompt_template)
🎯 고급 프롬프트 실험 - 감성 분석
더 복잡한 프롬프트 엔지니어링 실험을 해보겠습니다!
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import mlflow
import openai
import json
# 감성 분석 실험
mlflow.set_experiment("llm_sentiment_analysis")
# 테스트 데이터
test_reviews = [
"이 제품 정말 좋아요! 추천합니다.",
"배송이 늦었지만 제품은 만족스럽네요.",
"완전 최악... 돈 아깝다.",
"그냥 그래요. 나쁘지도 좋지도 않아요."
]
# 다양한 감성 분석 프롬프트
sentiment_prompts = {
"simple": """다음 리뷰의 감정을 분석하세요.
결과: 긍정/부정/중립 중 하나로 답하세요.
리뷰: {text}""",
"detailed": """다음 리뷰를 분석하고 JSON 형식으로 답하세요:
{{
"sentiment": "긍정/부정/중립",
"confidence": 0.0-1.0,
"reason": "이유 설명"
}}
리뷰: {text}""",
"chain_of_thought": """다음 리뷰를 단계별로 분석하세요:
1. 감정 표현 단어 식별
2. 전체적인 톤 분석
3. 최종 감정 판정
4. 신뢰도 평가
리뷰: {text}
분석 결과:"""
}
# 실험 실행
for prompt_name, prompt_template in sentiment_prompts.items():
with mlflow.start_run():
mlflow.log_param("prompt_name", prompt_name)
mlflow.log_param("task", "sentiment_analysis")
all_results = []
total_tokens = 0
for i, review in enumerate(test_reviews):
prompt = prompt_template.format(text=review)
try:
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
temperature=0.1 # 일관성을 위해 낮은 온도
)
result = response.choices[0].message.content
tokens = response.usage.total_tokens
all_results.append({
"review": review,
"result": result,
"tokens": tokens
})
total_tokens += tokens
# 개별 결과 저장
mlflow.log_text(result, f"result_{i}_{prompt_name}.txt")
except Exception as e:
print(f"❌ 오류 발생: {e}")
continue
# 전체 결과 저장
results_json = json.dumps(all_results, ensure_ascii=False, indent=2)
mlflow.log_text(results_json, f"all_results_{prompt_name}.json")
# 메트릭 저장
mlflow.log_metric("total_tokens", total_tokens)
mlflow.log_metric("avg_tokens_per_review", total_tokens / len(test_reviews))
mlflow.log_metric("total_cost", total_tokens * 0.0000015)
print(f"✅ {prompt_name} 감성 분석 완료!")
print(f" 총 토큰: {total_tokens}")
print(f" 평균 토큰/리뷰: {total_tokens / len(test_reviews):.1f}")
print()
print("🎉 감성 분석 실험 완료!")
🤝 팀 협업을 위한 프롬프트 공유
프롬프트 템플릿 라이브러리 만들기
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import mlflow
import mlflow.pyfunc
import json
class PromptTemplate(mlflow.pyfunc.PythonModel):
"""재사용 가능한 프롬프트 템플릿 클래스"""
def __init__(self, template, task_type, language="ko"):
self.template = template
self.task_type = task_type
self.language = language
def format_prompt(self, **kwargs):
"""프롬프트 템플릿에 변수 값 삽입"""
return self.template.format(**kwargs)
def predict(self, context, model_input):
"""MLflow 모델 인터페이스"""
if isinstance(model_input, dict):
return self.format_prompt(**model_input)
return self.template
# 검증된 프롬프트 템플릿 등록
mlflow.set_experiment("prompt_template_library")
with mlflow.start_run():
# 최고 성능 번역 프롬프트를 모델로 등록
best_translation_prompt = """당신은 10년 경력의 전문 번역가입니다.
다음 영어 문장을 자연스러운 한국어로 번역하세요:
{text}"""
prompt_model = PromptTemplate(
template=best_translation_prompt,
task_type="translation",
language="ko"
)
# 모델 정보 로깅
mlflow.log_param("template_name", "professional_translation")
mlflow.log_param("task_type", "translation")
mlflow.log_param("language_pair", "en_to_ko")
mlflow.log_param("performance_score", 0.95)
# 템플릿 저장
mlflow.log_text(best_translation_prompt, "prompt_template.txt")
# Python 모델로 저장
mlflow.pyfunc.log_model(
artifact_path="prompt_model",
python_model=prompt_model,
registered_model_name="TranslationPromptTemplate"
)
print("✅ 프롬프트 템플릿이 모델로 등록되었습니다!")
# 등록된 프롬프트 템플릿 사용하기
model_uri = "models:/TranslationPromptTemplate/latest"
loaded_prompt = mlflow.pyfunc.load_model(model_uri)
# 사용 예시
test_input = {"text": "Hello, world!"}
formatted_prompt = loaded_prompt.predict(None, test_input)
print(f"🎯 생성된 프롬프트:\n{formatted_prompt}")
📈 프롬프트 성능 모니터링
A/B 테스트로 프롬프트 비교
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import mlflow
import random
import time
# A/B 테스트 실험
mlflow.set_experiment("prompt_ab_test")
# 두 가지 프롬프트 버전
prompt_a = "다음을 요약하세요: {text}"
prompt_b = "다음 내용을 3줄로 핵심만 요약하세요: {text}"
test_texts = [
"인공지능은 현대 사회에서 점점 더 중요한 역할을 하고 있습니다...",
"기후 변화는 전 세계적으로 심각한 문제가 되고 있습니다...",
"원격 근무는 코로나19 이후 새로운 업무 형태로 자리잡았습니다..."
]
# A/B 테스트 실행
for i, text in enumerate(test_texts):
# 무작위로 A 또는 B 선택
version = random.choice(['A', 'B'])
prompt = prompt_a if version == 'A' else prompt_b
with mlflow.start_run():
mlflow.log_param("prompt_version", version)
mlflow.log_param("text_id", i)
mlflow.log_param("test_type", "ab_test")
full_prompt = prompt.format(text=text)
# 실제 API 호출 (여기서는 시뮬레이션)
# response = client.chat.completions.create(...)
# 시뮬레이션 결과
response_length = random.randint(50, 200)
user_satisfaction = random.uniform(3.0, 5.0)
mlflow.log_metric("response_length", response_length)
mlflow.log_metric("user_satisfaction", user_satisfaction)
mlflow.log_metric("completion_time", random.uniform(1.0, 3.0))
mlflow.set_tag("experiment_type", "ab_test")
mlflow.set_tag("prompt_category", "summarization")
print(f"✅ 테스트 {i+1} (버전 {version}) 완료!")
# 결과 분석
experiment = mlflow.get_experiment_by_name("prompt_ab_test")
runs = mlflow.search_runs(experiment_ids=[experiment.experiment_id])
# 버전별 성능 비교
version_a_runs = runs[runs['params.prompt_version'] == 'A']
version_b_runs = runs[runs['params.prompt_version'] == 'B']
print(f"\n📊 A/B 테스트 결과:")
print(f"버전 A 평균 만족도: {version_a_runs['metrics.user_satisfaction'].mean():.2f}")
print(f"버전 B 평균 만족도: {version_b_runs['metrics.user_satisfaction'].mean():.2f}")
if version_a_runs['metrics.user_satisfaction'].mean() > version_b_runs['metrics.user_satisfaction'].mean():
print("🏆 버전 A 승리!")
else:
print("🏆 버전 B 승리!")
🎯 실전 활용 팁
1. 프롬프트 버전 관리
1
2
3
4
5
6
7
8
9
10
11
12
13
# 프롬프트 버전별 관리
PROMPT_VERSIONS = {
"v1.0": "기본 프롬프트",
"v1.1": "컨텍스트 추가",
"v1.2": "예시 포함",
"v2.0": "완전히 새로운 접근"
}
for version, description in PROMPT_VERSIONS.items():
with mlflow.start_run():
mlflow.log_param("prompt_version", version)
mlflow.log_param("description", description)
mlflow.set_tag("version_type", "major" if ".0" in version else "minor")
2. 비용 최적화 추적
1
2
3
4
5
6
7
8
9
10
11
12
13
# 모델별 비용 비교
MODEL_COSTS = {
"gpt-3.5-turbo": 0.0000015,
"gpt-4": 0.00003,
"claude-3-haiku": 0.00000025
}
for model_name, cost_per_token in MODEL_COSTS.items():
with mlflow.start_run():
mlflow.log_param("model_name", model_name)
mlflow.log_param("cost_per_token", cost_per_token)
# ... 실험 수행
mlflow.log_metric("total_cost", tokens_used * cost_per_token)
🏆 결론
MLflow를 사용한 LLM 프롬프트 엔지니어링으로 다음과 같은 이점을 얻을 수 있습니다:
🎯 핵심 가치:
- 체계적인 실험 관리: 모든 프롬프트 실험을 자동 추적
- 성능 비교: 다양한 프롬프트의 효과를 객관적으로 비교
- 비용 최적화: 토큰 사용량과 비용을 체계적으로 관리
- 팀 협업: 검증된 프롬프트 템플릿 공유
💡 시작 권장사항:
- 작은 프롬프트 실험부터 시작
- 일관된 평가 지표 설정
- 비용 추적 습관화
- 팀 내 프롬프트 라이브러리 구축
📚 추가 학습 자료:
💡 마지막 팁: 프롬프트 엔지니어링은 과학입니다. 체계적인 실험과 데이터 기반 의사결정으로 최고의 성능을 얻으세요!
🤖 Happy Prompt Engineering! 🚀
This post is licensed under CC BY 4.0 by the author.