3장. 프롬프트 설계
따라하기 — 프롬프트 다섯 가지 비교
출력 안내: 실행 코드는 답변·도구 결과·청크 본문을 글자 수로 자르지 않고 출력합니다. 아래의 기존 실행 예시는 일부 축약된 기록이며, 실제 실행 화면에서 전체 내용을 확인하세요.
목표
같은 모델에게 프롬프트만 바꿔 가며 다섯 번 비교합니다. 시스템 프롬프트가 있을 때와 없을 때, temperature가 낮을 때와 높을 때, 예시를 줬을 때, 과정을 적게 했을 때 답이 어떻게 달라지는지 눈으로 확인합니다.
0. 실습 준비
VS Code에서 haru-market 폴더를 열고 터미널에서 환경을 켭니다.
$ conda activate myenv
이번 장은 새로 설치할 것이 없습니다.
1. 파일 만들기
haru-market 폴더 맨 위에 새 파일을 만듭니다.
lesson03_prompt.py
아래 코드 전체를 복사해 붙여 넣고 저장합니다.
# -*- coding: utf-8 -*-
"""[3장] 브랜드 응대 프롬프트 설계 — 하루마켓 상담원처럼 답하게 만들기
문제 상황: 답은 하는데 말투가 하루마켓이 아니고, 실행할 때마다 결과가 다르다.
해결 도구: system instruction · temperature · few-shot · Chain-of-Thought
실행: python lesson03_prompt.py
"""
from google.genai import types
from config import MODEL, get_client, print_usage
client = get_client()
# ── 하루마켓 상담 시스템 프롬프트 (이 과정 내내 재사용) ────────────────
# 프롬프트도 코드다. 버전을 남기고(HARU_SYSTEM_V1) 바꿀 때마다 비교한다.
HARU_SYSTEM_V1 = """당신은 온라인 쇼핑몰 '하루마켓'의 고객지원 상담원 '하루'입니다.
[응대 원칙]
1. 존댓말을 쓰고, 문장은 짧고 명확하게 씁니다.
2. 고객의 불편에 먼저 공감을 표한 뒤 해결책을 안내합니다.
3. 모르는 것은 아는 척하지 않고 "확인 후 안내드리겠습니다"라고 합니다.
4. 확인되지 않은 환불 금액·배송 일정을 단정해서 말하지 않습니다.
[금지 표현]
- "그건 저희 책임이 아닙니다" 등 책임 회피성 표현
- "무조건", "100% 보장" 등 과장 표현
- 반말, 이모티콘 남용
[형식]
- 3~5문장 이내로 답합니다.
- 다음 행동(버튼/메뉴 위치 등)을 마지막 문장에서 안내합니다."""
question = "배송이 왜 이렇게 늦어요? 화나네요."
# ── 1. 시스템 프롬프트 유무 비교 ──────────────────────────────────────
print("■ [비교 1] 시스템 프롬프트 없음")
r = client.models.generate_content(model=MODEL, contents=question)
print(r.text, "\n")
print("■ [비교 2] 하루마켓 시스템 프롬프트 적용")
r = client.models.generate_content(
model=MODEL,
contents=question,
config=types.GenerateContentConfig(
system_instruction=HARU_SYSTEM_V1,
temperature=1.0, # Gemini 3 계열 권장 기본값
max_output_tokens=2048,
),
)
print(r.text, "\n")
print_usage(r)
# ── 2. temperature 실험 — 같은 질문을 3번씩 ───────────────────────────
print("\n■ [비교 3] temperature 실험 (같은 질문 3회 반복)")
temp_questions = {
"답이 정해진 질문": "다음 문의의 유형을 한 단어로만 답해: '반품하면 배송비 누가 내요?'",
"자유롭게 쓰는 질문": "하루마켓의 장점을 한 문장으로 소개해줘.",
}
for label, temp_q in temp_questions.items():
print(f"\n [{label}] {temp_q}")
for temp in (0.0, 2.0):
print(f" temperature={temp}")
for i in range(3):
r = client.models.generate_content(
model=MODEL,
contents=temp_q,
config=types.GenerateContentConfig(temperature=temp),
)
print(f" {i+1}회: {r.text.strip()}")
# → Gemini 3 계열은 temperature 기본값(1.0)을 권장한다.
# 실험 결과는 직접 비교하며, 온도만으로 출력 형식을 보장하지 않는다.
# 답이 정해진 질문은 원래 흔들리지 않고, 자유롭게 쓰는 질문은 온도를 내려도 문장이 달라진다.
# '항상 같은 형식'이 필요하면 온도가 아니라 형식을 고정한다 (4장 구조화 출력).
# ── 3. few-shot — 답변 '형식'을 예시로 가르치기 ───────────────────────
print("\n■ [비교 4] few-shot 으로 답변 형식 고정")
few_shot = """다음 예시와 같은 형식으로 답하세요.
문의: 반품 신청은 어디서 하나요?
답변: [공감] 반품 절차가 궁금하셨군요. [안내] 마이페이지 > 주문내역에서 \
'반품신청' 버튼으로 접수하실 수 있어요. [다음행동] 접수 후 1~3영업일 내 \
택배기사가 방문합니다.
문의: 포인트는 언제 적립되나요?
답변: [공감] 포인트 적립 시점이 궁금하셨군요. [안내] 구매확정 다음 날 자동 \
적립됩니다. [다음행동] 마이페이지 > 하루포인트에서 내역을 확인해 주세요.
문의: 배송지를 잘못 입력했어요. 바꿀 수 있나요?
답변:"""
r = client.models.generate_content(
model=MODEL,
contents=few_shot,
config=types.GenerateContentConfig(system_instruction=HARU_SYSTEM_V1, temperature=1.0),
)
print(r.text)
# ── 4. Chain-of-Thought — 단계적 추론 유도 ────────────────────────────
print("\n■ [비교 5] CoT: 환불 금액 계산")
cot_q = """고객이 35,000원짜리 상품(무료배송으로 받음)을
단순 변심으로 '전체' 반품합니다.
정책:
- 단순 변심 반품 배송비는 편도 3,000원이며 고객이 부담한다 (환불액에서 차감).
- 부분 반품으로 잔여 주문금액이 3만원 미만이 되면 최초 배송비 3,000원을 추가 차감한다.
- 전체 반품은 잔여 주문이 없으므로 이 추가 차감을 적용하지 않는다.
반품 후 실 환불액을 단계적으로 계산한 뒤, 마지막 줄에 '실 환불액: OOO원'으로 답하세요."""
r = client.models.generate_content(
model=MODEL, contents=cot_q,
config=types.GenerateContentConfig(temperature=1.0),
)
print(r.text)
print("""
──────────────────────────────────────────────────────────
오늘의 산출물: HARU_SYSTEM_V1 (하루마켓 상담 시스템 프롬프트)
남은 문제: 답변이 '자유 문장'이라 코드가 다루기 어렵다 → 4장 구조화 출력
──────────────────────────────────────────────────────────""")
2. 코드에서 볼 곳
파일은 다섯 번의 비교로 이루어져 있습니다.
| 비교 | 무엇을 바꾸나 | 볼 것 |
|---|---|---|
| 1 | 시스템 프롬프트 없음 | 2장과 같은 맨 호출 |
| 2 | HARU_SYSTEM_V1 적용 |
소속·길이·마지막 문장 |
| 3 | 같은 질문을 temperature 0.0과 2.0으로 3번씩 | 온도를 바꾸면 답이 달라지는가 |
| 4 | few-shot 예시 두 개 | [공감] [안내] [다음행동] 형식 |
| 5 | 정책을 다 적어 준 단계적 추론 | 계산 과정과 마지막 줄 |
비교 4의 예시 문장 끝에 있는 \는 긴 줄을 나눠 적은 표시입니다. 따옴표 세 개로 감싼 문자열 안에서 줄 끝의 \는 "줄바꿈 없이 다음 줄로 이어진다"는 뜻이어서, 모델에게는 답변 하나가 한 줄로 전달됩니다.
비교 2에서 호출이 어떻게 달라졌는지만 다시 봅니다. config=가 추가된 것이 전부입니다.
r = client.models.generate_content(
model=MODEL,
contents=question,
config=types.GenerateContentConfig(
system_instruction=HARU_SYSTEM_V1,
temperature=1.0, # Gemini 3 계열 권장 기본값
max_output_tokens=2048,
),
)
3. 실행하기
실행하기 전에 짐작해 보세요.
- 비교 2의 답은 비교 1과 어디가 달라질까요?
HARU_SYSTEM_V1의[형식]을 다시 읽어 보세요. - 비교 5에서, 환불액은 얼마가 나올까요? 정책 세 줄을 읽고 직접 계산해 보세요.
$ python lesson03_prompt.py
LLM을 스무 번 가까이 호출하므로 30초~1분 걸립니다.
실행 결과 (문장은 실행할 때마다 달라집니다)
■ [비교 1] 시스템 프롬프트 없음
고객님, 배송 지연으로 인해 큰 불편과 불쾌감을 드려 정말 죄송합니다. 기다리시는 동안 얼마나 답답하셨을지 충분히 공감합니다.
현재 고객님의 주문 건이 어떤 상황인지 정확하게 확인해 드리겠습니다.
아래 정보를 알려주시면 바로 확인 후 빠르게 조치해 드리겠습니다.
* **주문번호**
* **성함**
* **연락처**
빠르게 확인하여 정확한 배송 상황과 해결 방안을 안내해 드리겠습니다. 다시 한번 진심으로 사과드립니다.
■ [비교 2] 하루마켓 시스템 프롬프트 적용
배송 지연으로 많이 화나셨겠습니다. 마음이 상하셨을 텐데 진심으로 사과드립니다.
현재 고객님의 주문 건이 어떤 단계에 있는지 정확한 확인이 필요합니다.
자세한 배송 조회는 '마이페이지 > 주문배송조회' 메뉴에서 확인하실 수 있습니다.
[토큰] 입력 218 + 출력 73 = 총 291
■ [비교 3] temperature 실험 (같은 질문 3회 반복)
(중략)
■ [비교 4] few-shot 으로 답변 형식 고정
[공감] 배송지 변경 건으로 걱정이 많으셨겠습니다. [안내] 상품이 '상품 준비 중' 단계일 때만 직접 변경하실 수 있습니다. [다음행동] 마이페이지 > 주문내역에서 배송지 변경 가능 여부를 확인해 주세요.
■ [비교 5] CoT: 환불 금액 계산
1단계: 상품 구매 금액 확인
- 고객이 결제한 금액: 35,000원 (무료배송 혜택 적용)
2단계: 반품 배송비(고객 부담) 차감
- 단순 변심에 따른 편도 반품 배송비: 3,000원 차감
- 35,000원 - 3,000원 = 32,000원
3단계: 추가 배송비 차감 여부 확인
- 전체 반품이므로 잔여 주문금액이 0원이 되어, '잔여 주문금액이 3만원 미만일 때의 최초 배송비 3,000원 추가 차감'은 적용되지 않습니다.
실 환불액: 32,000원
(이하 생략)
4. 무엇을 관찰했나
비교 1과 2 — 시스템 프롬프트가 바꾼 것
비교 1도 공손하고 사과도 합니다. 얼핏 보면 큰 차이가 없습니다. 달라진 곳은 세 군데입니다.
- 마지막 문장. 비교 2는
'마이페이지 > 주문배송조회' 메뉴를 알려 주며 끝납니다. 프롬프트의[형식]에 "다음 행동을 마지막 문장에서 안내합니다"라고 적었기 때문입니다. - 길이. 비교 2는 네 문장입니다. "3~5문장 이내"를 지켰습니다.
- 굵은 글씨(
**)와 목록 기호가 사라졌습니다. 채팅 화면에 그대로 내보낼 수 있는 모양이 되었습니다.
프롬프트가 바꾼 것은 말하는 방식입니다. 주문이 실제로 어느 단계에 있는지 조회하는 일은 5장부터 도구를 붙여서 합니다.
Gemini 3 계열은 temperature 기본값을 사용한다
Gemini 3 계열에서는 temperature 기본값(1.0)을 사용합니다. 온도만으로 결과의 일관성을 보장할 수 없으므로 출력 형식과 규칙을 명시하고 실제 응답을 확인합니다.
비교 4 — 예시 두 개로 형식이 맞춰진다
[공감] [안내] [다음행동] 세 꼬리표가 예시와 똑같이 붙었습니다. 형식을 길게 설명하지 않았는데도 그렇습니다. 형식은 예시가 정했고, 안내에 들어갈 하루마켓의 실제 규정은 12장부터 정책 문서에서 가져옵니다.
비교 5 — 조건을 다 적어 주니 단계마다 근거가 보인다
답은 32,000원입니다. 과정을 정책 세 줄과 맞춰 봅니다.
| 풀이의 단계 | 근거가 된 정책 줄 |
|---|---|
| 2단계: 반품 배송비 3,000원 차감 → 32,000원 | 첫째 줄 (편도 3,000원, 고객 부담) |
| 3단계: 추가 차감 없음 | 둘째·셋째 줄 (전체 반품에는 적용하지 않는다) |
풀이의 모든 단계가 우리가 적어 준 정책의 어느 줄에서 왔는지 짚을 수 있습니다. 조건을 빠짐없이 적어 주었기 때문입니다. 그리고 마지막 줄이 실 환불액: 32,000원으로 정해진 자리에 정해진 형식으로 왔으므로, 코드는 마지막 줄만 꺼내 쓰면 됩니다.
단계적 추론은 적어 준 조건 위에서 정확해집니다. 그래서 규정은 정책 문서에서 가져와 넣고(12장부터), 실제 금액 계산은 코드에 맡깁니다.
5. 지금 폴더의 모습
haru-market/
├── config.py
├── lesson02_first_call.py
└── lesson03_prompt.py ← 이번 장
핵심 정리
- 시스템 프롬프트는 마지막 문장, 길이, 표기 방식처럼 확인할 수 있는 부분을 바꿉니다.
- Gemini 3 계열에서는 temperature 기본값(1.0)을 사용합니다. 온도만으로 결과의 일관성을 보장할 수 없으므로 출력 형식과 규칙을 명시하고 실제 응답을 확인합니다.
- few-shot은 예시 두 개로 답의 형식을 맞춰 줍니다.
- 정책을 빠짐없이 적은 단계적 추론은 풀이의 단계마다 근거가 되는 조건을 짚을 수 있습니다.
- 이번 장의 산출물은
HARU_SYSTEM_V1입니다.