Skip to content

필사 모드: Prompt Engineering 2025 실전 가이드: GPT-4o와 Claude 3.5를 최대한 활용하는 법

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

프롬프트 엔지니어링이 여전히 중요한 이유

"LLM이 너무 똑똑해져서 프롬프트 엔지니어링은 곧 필요 없어지지 않을까요?"

이 질문을 자주 받습니다. 답은 "아니오"입니다. 이유가 있습니다.

모델 능력 × 프롬프트 품질 = 출력 품질

최고의 모델에 나쁜 프롬프트를 주면 나쁜 결과가 나옵니다. 좋은 프롬프트는 같은 모델에서 20-40% 이상의 성능 차이를 만들어낼 수 있습니다. GPT-4o가 GPT-3.5보다 능력이 좋지만, GPT-3.5에 최적화된 프롬프트를 넣은 결과가 GPT-4o에 허접한 프롬프트를 넣은 결과보다 나을 수 있습니다.

2025년에는 새로운 모델이 계속 나오고 있지만, 좋은 프롬프트 작성 능력은 모델과 무관하게 이식 가능한 기술입니다.

기법 1: System Prompt 설계 원칙

System Prompt는 LLM의 "설정값"입니다. 대부분의 개발자들은 이걸 소홀히 합니다.

나쁜 시스템 프롬프트 (대부분의 사람들이 이렇게 시작)

bad_prompt = "You are a helpful assistant."

문제: 너무 모호해서 모델이 무엇을 해야 하는지 모름

COSTAR 프레임워크를 적용한 좋은 시스템 프롬프트

good_prompt = """

Role (역할)

당신은 TechCorp의 시니어 한국어 고객 서비스 전문가입니다.

Context (맥락)

TechCorp는 HR 관리를 위한 B2B SaaS 소프트웨어를 판매합니다.

고객은 주로 100-5,000명 규모 회사의 HR 매니저입니다.

Objective (목표)

고객의 문제를 해결하고 제품 관련 질문에 답합니다.

결제 관련 문제는 finance@techcorp.com으로 에스컬레이션하세요.

Style (스타일)

- 격식 있지만 따뜻한 한국어 (존댓말 사용)

- 답변은 200자 이내로 유지

- 항상 후속 질문으로 마무리

Tone (톤)

전문적, 공감적, 해결 지향적

Audience (대상)

한국의 HR 매니저, 보통 비기술적 사용자

Response Format (형식)

1. 문제 인지 ("네, [문제]에 대해 도움드리겠습니다")

2. 해결책 제시 (필요시 단계별로)

3. 마무리: "이 외에 도움이 필요한 부분이 있으신가요?"

"""

COSTAR 프레임워크: **C**ontext, **O**bjective, **S**tyle, **T**one, **A**udience, **R**esponse Format. 이 여섯 가지를 명시하면 예측 가능한 일관된 결과를 얻을 수 있습니다.

기법 2: Few-Shot Prompting

"말로 설명하는 것보다 예시를 보여주는 게 빠르다"는 원칙입니다.

Zero-shot (예시 없음)

zero_shot = """

리뷰 감정을 분류하세요: '배송이 너무 느려요'

"""

결과: 분류는 하지만 일관성이 없을 수 있음

Few-shot (예시 포함)

few_shot = """

리뷰 감정을 긍정적 / 부정적 / 중립으로 분류하세요.

리뷰: "정말 좋은 제품이에요!" → 긍정적

리뷰: "배송이 조금 늦었지만 제품은 만족해요" → 중립

리뷰: "완전 불량품이에요 환불하고 싶어요" → 부정적

리뷰: "가격 대비 그냥 그래요" → 중립

리뷰: "배송이 너무 느려요" → """

결과: 부정적 (훨씬 일관성 높음)

Few-shot의 핵심:

- **예시의 질**: 전형적인 케이스를 포함할 것

- **예시의 다양성**: 엣지 케이스도 보여줄 것

- **예시의 수**: 보통 3-8개가 최적 (더 많다고 꼭 좋지 않음)

- **예시의 순서**: 가장 최근 예시가 답에 가장 큰 영향 (Recency Bias)

기법 3: Chain-of-Thought (CoT) 프롬프팅

LLM에게 "생각하는 과정"을 보여달라고 하면 복잡한 추론 능력이 극적으로 향상됩니다.

CoT 없음 (나쁜 결과)

no_cot = """

기차가 2시간 동안 120km를 이동하고, 이후 3시간 동안 180km를 이동했습니다.

평균 속도는 얼마인가요?

"""

LLM이 (120/2 + 180/3) / 2 = 60으로 잘못 계산할 수 있음 (틀린 방법)

CoT 적용 (올바른 결과)

with_cot = """

기차가 2시간 동안 120km를 이동하고, 이후 3시간 동안 180km를 이동했습니다.

평균 속도는 얼마인가요?

단계별로 생각해봅시다:

"""

LLM: "총 거리 = 120 + 180 = 300km

총 시간 = 2 + 3 = 5시간

평균 속도 = 300 / 5 = 60 km/h"

정확한 답!

Zero-shot CoT (가장 단순한 형태)

zero_shot_cot = """

문제: [복잡한 수학 문제]

풀이: 단계별로 생각해봅시다.

"""

CoT가 효과적인 이유: LLM은 자기 회귀적(autoregressive)으로 생성합니다. 중간 추론 단계를 토큰으로 생성하면, 이후 토큰들이 그 추론을 "참고"할 수 있어 최종 답의 정확도가 높아집니다.

**주의**: GPT-4o1, o3 같은 reasoning 모델들은 내부적으로 CoT를 이미 수행합니다. 이런 모델에는 "단계별로 생각해" 대신 문제를 명확하게 기술하는 데 집중하세요.

기법 4: Structured Output (구조화된 출력)

프로덕션 시스템에서 LLM 출력을 파싱해야 한다면, 반드시 구조화된 출력을 사용하세요.

from openai import OpenAI

from pydantic import BaseModel

client = OpenAI()

방법 1: JSON Mode (간단한 경우)

response = client.chat.completions.create(

model="gpt-4o",

response_format={"type": "json_object"},

messages=[{

"role": "user",

"content": """

다음 텍스트에서 제품 정보를 추출하고 JSON으로 반환하세요:

"아이폰 15 프로 맥스, 256GB, 스페이스 블랙, 정가 1,850,000원"

반환 형식: {"name": ..., "storage": ..., "color": ..., "price_krw": ...}

"""

}]

)

data = json.loads(response.choices[0].message.content)

방법 2: Structured Outputs (타입 안전, 더 안정적)

class ProductInfo(BaseModel):

name: str

storage: str

color: str

price_krw: int

response = client.beta.chat.completions.parse(

model="gpt-4o-2024-08-06",

messages=[{

"role": "user",

"content": "아이폰 15 프로 맥스, 256GB, 스페이스 블랙, 정가 1,850,000원"

}],

response_format=ProductInfo

)

product = response.choices[0].message.parsed

print(product.price_krw) # 1850000 (int 타입으로 보장)

OpenAI의 Structured Outputs는 JSON Schema를 100% 준수하는 출력을 보장합니다. 파싱 에러가 사라집니다.

기법 5: XML 태그로 구조화 (Claude 특효)

Claude는 XML 태그로 구조화된 프롬프트에 특히 잘 반응합니다. 이는 Anthropic이 Claude를 훈련할 때 XML 구조를 많이 사용했기 때문입니다.

Claude API 사용 예시

client = anthropic.Anthropic()

prompt = """

다음 문서를 요약하세요.

[긴 문서 내용 여기에]

- 최대 3개의 불릿 포인트

- 각 불릿: 1-2문장

- 실행 가능한 항목에 집중

- 전문 용어는 쉬운 말로 바꿀 것

다음 JSON 형식으로 반환하세요:

{

"summary_bullets": ["...", "...", "..."],

"key_action": "가장 중요한 실행 항목 1개"

}

"""

response = client.messages.create(

model="claude-3-5-sonnet-20241022",

max_tokens=1024,

messages=[{"role": "user", "content": prompt}]

)

XML 태그를 사용하면 LLM이 각 섹션의 역할을 명확히 이해하고, 지시사항을 더 정확하게 따릅니다.

기법 6: Role + Context + Constraint 패턴

가장 범용적이고 신뢰할 수 있는 프로덕션 프롬프트 패턴입니다.

def create_production_prompt(

role: str,

context: str,

task: str,

constraints: list[str],

output_format: str

) -> str:

constraints_text = "\n".join(f"- {c}" for c in constraints)

return f"""# Role

{role}

Context

{context}

Task

{task}

Constraints

{constraints_text}

Output Format

{output_format}"""

실제 사용 예시

prompt = create_production_prompt(

role="당신은 10년 경력의 한국어 카피라이터입니다. MZ 세대를 타겟으로 합니다.",

context="클라이언트: 스타트업 커피 브랜드 'BREW'. 신제품 콜드브루 출시.",

task="Instagram 캡션 3가지 버전을 작성하세요.",

constraints=[

"각 캡션은 50자 이내",

"이모지 1-2개 포함",

"가격이나 프로모션 언급 금지",

"해시태그 포함하지 말 것"

],

output_format="JSON 배열로 반환: ['캡션1', '캡션2', '캡션3']"

)

이 패턴의 장점:

- 코드로 프롬프트를 동적으로 생성 가능

- 각 구성요소를 독립적으로 수정 가능

- A/B 테스트 하기 쉬움

프롬프트 버전 관리: 프롬프트를 코드처럼 관리하라

프로덕션에서 프롬프트는 코드입니다. 버전 관리가 필요합니다.

나쁜 방법: 코드에 하드코딩

def summarize(text):

prompt = f"Summarize: {text}" # 버전 관리 불가

return call_llm(prompt)

좋은 방법: 프롬프트를 별도 파일/DB로 관리

prompts/summarize_v2.txt

"""

당신은 전문 비즈니스 분석가입니다.

다음 텍스트를 3줄 이내로 요약하세요.

핵심 수치와 의사결정 사항을 강조하세요.

텍스트: {text}

"""

class PromptManager:

def __init__(self):

self.prompts = {}

self.active_versions = {}

def register(self, name: str, version: str, template: str):

key = f"{name}_v{version}"

self.prompts[key] = template

def get(self, name: str, version: str = None) -> str:

version = version or self.active_versions.get(name, "1")

return self.prompts[f"{name}_v{version}"]

def set_active(self, name: str, version: str):

self.active_versions[name] = version

A/B 테스트: 50% 확률로 새 버전 사용

self.active_versions[name] = random.choice(["1", "2"])

pm = PromptManager()

pm.register("summarize", "1", "Summarize: {text}")

pm.register("summarize", "2", "당신은 전문 분석가... {text}")

pm.set_active("summarize", "2")

더 체계적인 관리를 원한다면 LangSmith나 PromptLayer 같은 도구를 고려하세요. 이들은 프롬프트 버전 관리, A/B 테스트, 성능 모니터링을 통합 제공합니다.

모델별 프롬프트 최적화 팁

같은 프롬프트가 모든 모델에서 동일하게 작동하지 않습니다:

GPT-4o:

- 명확한 지시사항 선호

- Markdown 형식을 잘 따름

- "당신은 [역할]입니다" 스타일에 잘 반응

Claude 3.5/3.7:

- XML 태그 구조에 특히 잘 반응

- 긴 시스템 프롬프트를 잘 소화

- "반드시 ~해야 합니다" 같은 강한 제약에 순응적

Gemini:

- 멀티모달 컨텍스트에 강함

- 검색 증강(grounding)을 내장 지원

오픈소스 (Llama, Mistral 등):

- 각 모델의 특정 채팅 템플릿을 반드시 사용

- 상업적 모델보다 지시사항 따르기가 약할 수 있음

- 더 명시적이고 구체적인 지시가 필요

프롬프트 디버깅 방법론

프롬프트가 기대한 대로 작동하지 않을 때:

1. 온도(Temperature)를 0으로 설정

→ 재현 가능한 결과 확보

2. 지시사항을 더 구체적으로

→ "좋게 써줘" → "전문적이고 직접적인 어조로, 능동태 사용, 50자 이내로 써줘"

3. 원하는 출력 예시 추가 (Few-shot)

→ 말로 설명하는 것보다 예시가 효과적

4. 제약사항 명시

→ "하지 말아야 할 것"을 명시하면 네거티브 케이스 감소

5. Chain-of-thought 추가

→ "단계별로 생각해봅시다" 한 마디가 복잡한 추론 정확도를 크게 높임

6. 프롬프트 길이 최적화

→ 길다고 꼭 좋은 게 아님. 필요 없는 내용 제거

결론: 프롬프트 엔지니어링은 기술이다

프롬프트 엔지니어링은 마법이 아닙니다. 테스트하고, 측정하고, 반복하는 공학적 기술입니다.

실전 조언:

1. **작게 시작해서 점진적으로 개선**: 기본 프롬프트부터 시작해 문제 발생 시 개선

2. **프롬프트를 코드로 관리**: 버전 관리, 테스트, 배포 파이프라인 구축

3. **정량적으로 평가**: "더 좋아진 것 같아"가 아니라 측정 가능한 지표로 판단

4. **모델 업데이트를 모니터링**: 새 모델 버전에서 기존 프롬프트가 다르게 작동할 수 있음

좋은 프롬프트는 좋은 코드처럼 시간이 지나도 유지보수 가능해야 합니다.

현재 단락 (1/203)

"LLM이 너무 똑똑해져서 프롬프트 엔지니어링은 곧 필요 없어지지 않을까요?"

작성 글자: 0원문 글자: 5,801작성 단락: 0/203