프로덕션 프롬프트 엔지니어링: 체계적 설계와 버전 관리
프롬프트를 코드에 하드코딩하는 것은 환경변수를 소스에 직접 박는 것과 같습니다. 초반에는 편하지만, 프로덕션에서 반드시 문제가 됩니다.
고급 프롬프팅 기법
Chain-of-Thought (CoT)
"답만 내라"보다 "생각 과정을 보여라"가 복잡한 문제에서 훨씬 정확합니다.
# 기본 프롬프트 (정확도 낮음)
basic = "다음 계약서에서 위험 조항을 HIGH/MEDIUM/LOW로 분류하세요."
# CoT 프롬프트 (정확도 높음)
cot = """다음 계약서를 분석하세요.
분석 절차:
1. 계약서를 조항별로 구분하세요
2. 각 조항에서 잠재적 위험 요소를 나열하세요
3. 발생 가능성과 영향도를 평가하세요
4. 최종적으로 HIGH/MEDIUM/LOW로 분류하고 이유를 설명하세요
[계약서 내용]
{contract_text}
위 절차를 따라 단계별로 분석하세요:"""Few-shot 프롬프팅
few_shot = """고객 문의를 분류하세요.
예시:
입력: "결제가 안 돼요"
출력: {{"category": "billing", "priority": "high", "sentiment": "frustrated"}}
입력: "사용법이 궁금해요"
출력: {{"category": "inquiry", "priority": "low", "sentiment": "neutral"}}
이제 분류하세요:
입력: "{user_message}"
출력:"""Few-shot 예시 원칙: 엣지 케이스 포함, 3~5개 적당, 실제 실패 사례에서 추출.
XML 구조화 (Claude 특화)
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>"""프롬프트 버전 관리
prompts/
├── customer_support/
│ ├── classify_v1.txt # 구버전 보관
│ ├── classify_v2.txt # 현재 프로덕션
│ └── classify_v3_test.txt # 실험 중
└── manifest.yaml# manifest.yaml
prompts:
customer_support_classify:
production: v2
canary: v3_test
canary_percent: 5import 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 테스트
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}")프롬프트 인젝션 방어
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 서비스의 안정성을 크게 높입니다.
이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.