프로덕션 프롬프트 엔지니어링: 체계적 설계와 버전 관리
프롬프트를 코드에 하드코딩하는 것은 환경변수를 소스에 직접 박는 것과 같습니다. 초반에는 편하지만, 프로덕션에서 반드시 문제가 됩니다.
고급 프롬프팅 기법
Chain-of-Thought (CoT)
"답만 내라"보다 "생각 과정을 보여라"가 복잡한 문제에서 훨씬 정확합니다.
Python
# 기본 프롬프트 (정확도 낮음)
basic = "다음 계약서에서 위험 조항을 HIGH/MEDIUM/LOW로 분류하세요."
# CoT 프롬프트 (정확도 높음)
cot = """다음 계약서를 분석하세요.
분석 절차:
1. 계약서를 조항별로 구분하세요
2. 각 조항에서 잠재적 위험 요소를 나열하세요
3. 발생 가능성과 영향도를 평가하세요
4. 최종적으로 HIGH/MEDIUM/LOW로 분류하고 이유를 설명하세요
[계약서 내용]
{contract_text}
위 절차를 따라 단계별로 분석하세요:"""Few-shot 프롬프팅
Python
few_shot = """고객 문의를 분류하세요.
예시:
입력: "결제가 안 돼요"
출력: {{"category": "billing", "priority": "high", "sentiment": "frustrated"}}
입력: "사용법이 궁금해요"
출력: {{"category": "inquiry", "priority": "low", "sentiment": "neutral"}}
이제 분류하세요:
입력: "{user_message}"
출력:"""Few-shot 예시 원칙: 엣지 케이스 포함, 3~5개 적당, 실제 실패 사례에서 추출.
XML 구조화 (Claude 특화)
Python
structured = """<system>
당신은 코드 리뷰 전문가입니다.
검토 기준: 보안 취약점, 성능 문제, 가독성
</system>
<context>
언어: Python, PEP 8 준수, type hints 필수
</context>
<code_to_review>
{code}
</code_to_review>
<output_format>
JSON으로만 반환:
{{"issues": [{{"type": "security|performance|readability", "line": 번호, "description": "설명", "suggestion": "수정안"}}],
"overall_score": 1-10}}
</output_format>"""프롬프트 버전 관리
CODE
prompts/
├── customer_support/
│ ├── classify_v1.txt # 구버전 보관
│ ├── classify_v2.txt # 현재 프로덕션
│ └── classify_v3_test.txt # 실험 중
└── manifest.yamlYAML
# manifest.yaml
prompts:
customer_support_classify:
production: v2
canary: v3_test
canary_percent: 5Python
import yaml, random
from pathlib import Path
class PromptRegistry:
def __init__(self, prompts_dir="prompts"):
self.base = Path(prompts_dir)
with open(self.base / "manifest.yaml") as f:
self.manifest = yaml.safe_load(f)
def get(self, name, use_canary=False):
config = self.manifest["prompts"][name]
if use_canary and "canary" in config:
version = (config["canary"]
if random.random() < config.get("canary_percent", 0) / 100
else config["production"])
else:
version = config["production"]
category = name.rsplit("_", 1)[0].replace("_", "/")
return (self.base / f"{category}_{version}.txt").read_text(encoding="utf-8")프롬프트 AB 테스트
Python
import hashlib
from dataclasses import dataclass
@dataclass
class PromptExperiment:
name: str
control_prompt: str
variant_prompt: str
traffic_split: float = 0.5
class PromptABTester:
def __init__(self, experiment):
self.exp = experiment
def get_prompt(self, user_id):
hash_val = int(hashlib.md5(f"{self.exp.name}:{user_id}".encode()).hexdigest(), 16)
bucket = (hash_val % 100) / 100
if bucket < self.exp.traffic_split:
return self.exp.control_prompt, "control"
return self.exp.variant_prompt, "variant"
def log_result(self, user_id, variant, metric):
print(f"exp={self.exp.name} user={user_id} variant={variant} metric={metric}")프롬프트 인젝션 방어
Python
def sanitize_user_input(user_input):
dangerous = [
"ignore previous instructions", "이전 지시를 무시",
"system prompt", "</system>", "<system>"
]
cleaned = user_input
for pattern in dangerous:
cleaned = cleaned.replace(pattern, "[FILTERED]")
return cleaned[:2000]프롬프트를 코드처럼 관리하면 변경 이력 추적, 롤백, A/B 테스트, 팀 협업이 가능해집니다. 이 실천법 하나가 LLM 서비스의 안정성을 크게 높입니다.
프롬프트를 바꾼 뒤 품질이 떨어질 때: 원인·재현·방지
버전 관리를 해도, 변경 후 특정 유형의 요청에서만 품질이 떨어지는 회귀는 배포 후에야 발견되는 경우가 많습니다.
원인
- 템플릿 변수 누락: 새 버전에서
{customer_tier}같은 변수를 추가했지만 호출 코드가 값을 넘기지 않아 빈 문자열이 들어감 - 모델 별칭 업데이트: 프롬프트는 그대로인데 공급사가 별칭이 가리키는 모델을 바꿈
- 지시 충돌: 새로 추가한 규칙이 기존 few-shot 예시와 모순됨
재현: 회귀 스위트 실행 대표 입력과 기대 조건을 파일로 두고, 두 버전을 같은 입력으로 비교합니다. 예를 들어 promptfoo를 쓰면 다음처럼 실행합니다.
YAML
# promptfooconfig.yaml
prompts: [file://prompts/support_v12.txt, file://prompts/support_v13.txt]
providers: [openai:gpt-4o-mini]
tests:
- vars: {question: "환불 기간이 지났는데 예외가 되나요?", customer_tier: "gold"}
assert:
- type: contains
value: "환불"
- type: not-contains
value: "{customer_tier}"Bash
npx promptfoo@latest eval && npx promptfoo@latest view해결
- 렌더링 단계에서 치환되지 않은
{...}패턴이 남아 있으면 예외를 던지도록 합니다. - 모델은 스냅샷 이름으로 고정하고, 프롬프트 버전과 모델 버전을 요청 로그에 함께 기록합니다.
재발 방지
- 회귀 스위트를 PR마다 실행하고, 운영에서 발견된 실패 사례를 테스트 케이스로 계속 추가합니다.
✦ ✦ ✦
출처·검증 안내
글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보
이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.
이 글이 도움이 되었나요?
댓글
첫 번째 댓글을 남겨보세요.