실무 Multi-Agent 오케스트레이션 3장 · 프롬프트 설계 5 / 7 ← 이전목차다음 → TechLead Cro

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 입니다.
← 이전 절few-shot과 단계적 추론 — 예시로 가르치고, 과정을 적게 한다다음 절 →정리와 체크리스트
오명운 · macro@prag-ai.com