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

3장. 프롬프트 설계

실습문제와 해답

코드를 조금 고쳐 보며 3장 내용을 손에 익힙니다. 먼저 스스로 해 본 뒤 해답을 펼쳐 보세요. 해답은 새 파일로 만들어 실행합니다.


문제 1 — 두 문장으로만 답하고, 범위 밖 질문은 거절하기

새 파일 ex03_short_system.py를 만들고, 다음 조건의 시스템 프롬프트를 직접 써서 두 질문에 답하게 하세요.

  • 하루마켓 상담원 '하루'이다.
  • 공감 한 문장 + 안내 한 문장, 모두 두 문장으로만 답한다.
  • 쇼핑과 관련 없는 질문에는 "하루마켓 쇼핑 관련 문의만 도와드릴 수 있어요." 라고만 답한다.

질문은 이 두 개입니다.

  1. "배송이 왜 이렇게 늦어요? 화나네요."
  2. "요즘 비트코인 사도 될까요?" (표준 질문 Q11)
해답 보기
from google.genai import types

from config import MODEL, get_client, print_usage

client = get_client()

HARU_SYSTEM_SHORT = """당신은 온라인 쇼핑몰 '하루마켓'의 고객지원 상담원 '하루'입니다.
존댓말로, 공감 한 문장과 안내 한 문장, 모두 두 문장으로만 답합니다.
쇼핑과 관련 없는 질문에는 "하루마켓 쇼핑 관련 문의만 도와드릴 수 있어요."라고만 답합니다."""

for q in ["배송이 왜 이렇게 늦어요? 화나네요.", "요즘 비트코인 사도 될까요?"]:
    r = client.models.generate_content(
        model=MODEL,
        contents=q,
        config=types.GenerateContentConfig(system_instruction=HARU_SYSTEM_SHORT),
    )
    print(f"\n고객: {q}\n하루: {r.text.strip()}")
    print_usage(r)
$ python ex03_short_system.py

실행 결과 (문장은 실행마다 다릅니다)

고객: 배송이 왜 이렇게 늦어요? 화나네요.
하루: 배송 지연으로 많이 속상하셨겠습니다.
현재 고객님의 주문 건 배송 상황을 빠르게 확인해 드리겠습니다.
  [토큰] 입력 89 + 출력 28 = 총 117

고객: 요즘 비트코인 사도 될까요?
하루: 하루마켓 쇼핑 관련 문의만 도와드릴 수 있어요.
  [토큰] 입력 87 + 출력 13 = 총 100

해설 — 두 가지를 봅니다.

첫째, 출력 토큰이 28과 13입니다. 2장의 첫 호출은 407토큰이었습니다. 길이를 숫자로 정해 준 것만으로 비용이 열 배 넘게 줄었습니다.

둘째, 범위 밖 질문을 거절했습니다. 거절 문구를 따옴표 안에 그대로 적어 주었더니 그 문장을 그대로 썼습니다. 회사 상담 창구가 투자 의견을 말하면 그것은 회사의 발언이 됩니다. 답하지 않는 것이 정답입니다.


문제 2 — 금지 표현을 실제로 지키는지 확인하기

lesson03_prompt.py의 HARU_SYSTEM_V1에는 "'무조건', '100% 보장' 등 과장 표현"을 금지하는 규칙이 있습니다. 고객이 "내일까지 무조건 도착한다고 약속해 주세요." 라고 요구할 때 이 규칙이 지켜지는지 확인하는 파일 ex03_forbidden.py를 만드세요.

  • 힌트: from lesson03_prompt import HARU_SYSTEM_V1로 프롬프트를 가져오면 lesson03_prompt.py 전체가 실행되어 시간이 걸립니다. 이 문제에서는 프롬프트 문자열을 복사해 와서 씁니다.
  • 답에 "무조건", "100%", "보장"이 들어 있는지 코드로 검사해 출력합니다.
해답 보기
from google.genai import types

from config import MODEL, get_client

client = get_client()

HARU_SYSTEM_V1 = """당신은 온라인 쇼핑몰 '하루마켓'의 고객지원 상담원 '하루'입니다.

[응대 원칙]
1. 존댓말을 쓰고, 문장은 짧고 명확하게 씁니다.
2. 고객의 불편에 먼저 공감을 표한 뒤 해결책을 안내합니다.
3. 모르는 것은 아는 척하지 않고 "확인 후 안내드리겠습니다"라고 합니다.
4. 확인되지 않은 환불 금액·배송 일정을 단정해서 말하지 않습니다.

[금지 표현]
- "그건 저희 책임이 아닙니다" 등 책임 회피성 표현
- "무조건", "100% 보장" 등 과장 표현
- 반말, 이모티콘 남용

[형식]
- 3~5문장 이내로 답합니다.
- 다음 행동(버튼/메뉴 위치 등)을 마지막 문장에서 안내합니다."""

question = "내일까지 무조건 도착한다고 약속해 주세요."
r = client.models.generate_content(
    model=MODEL,
    contents=question,
    config=types.GenerateContentConfig(system_instruction=HARU_SYSTEM_V1),
)
answer = r.text.strip()
print(f"고객: {question}\n하루: {answer}\n")

for word in ["무조건", "100%", "보장"]:
    print(f"  금지 표현 '{word}': {'들어 있음' if word in answer else '없음'}")
$ python ex03_forbidden.py

실행 결과 (문장은 실행마다 다릅니다)

고객: 내일까지 무조건 도착한다고 약속해 주세요.
하루: 배송 일정으로 걱정이 많으셨겠습니다.

빠른 수령을 원하시는 마음은 충분히 이해합니다.

다만, 배송은 택배사의 사정에 따라 변동될 수 있어 정확한 도착일을 확답드리기 어렵습니다.

마이페이지의 '주문배송 조회'에서 현재 배송 단계를 확인하실 수 있습니다.

  금지 표현 '무조건': 없음
  금지 표현 '100%': 없음
  금지 표현 '보장': 없음

해설 — 고객이 요구해도 규칙이 먼저였습니다. 시스템 프롬프트가 사용자 메시지보다 위에서 작동한다는 것을 확인한 것입니다.

더 중요한 것은 검사를 코드로 했다는 점입니다. 답을 눈으로 읽고 "괜찮네"라고 하는 대신, 지켜야 할 규칙을 코드로 확인했습니다. 프롬프트를 고칠 때마다 이런 검사를 돌리면 "고쳤더니 다른 데가 망가졌다"를 바로 알 수 있습니다. 규칙을 확인할 수 있게 써야 하는 이유가 이것입니다.


문제 3 — 조건을 다 적어 부분 반품 환불액 계산하기

lesson03_prompt.py의 비교 5는 전체 반품이었습니다. 이번에는 부분 반품을 계산하는 파일 ex03_cot_partial.py를 만드세요.

  • 상황: 고객이 20,000원짜리 텀블러와 15,000원짜리 머그컵을 한 번에 주문했습니다(합계 35,000원, 무료배송). 이 중 15,000원짜리 머그컵만 단순 변심으로 반품합니다.
  • 정책 세 줄은 lesson03_prompt.py의 cot_q와 같게 적습니다.
  • 답의 마지막 줄만 코드로 꺼내 따로 출력합니다.

실행하기 전에 직접 계산해 보세요. 실 환불액은 얼마일까요?

해답 보기
from google.genai import types

from config import MODEL, get_client

client = get_client()

question = """고객이 20,000원짜리 텀블러와 15,000원짜리 머그컵을 한 번에 주문했습니다
(합계 35,000원, 무료배송으로 받음). 이 중 15,000원짜리 머그컵만 단순 변심으로 반품합니다.
정책:
- 단순 변심 반품 배송비는 편도 3,000원이며 고객이 부담한다 (환불액에서 차감).
- 부분 반품으로 잔여 주문금액이 3만원 미만이 되면 최초 배송비 3,000원을 추가 차감한다.
- 전체 반품은 잔여 주문이 없으므로 이 추가 차감을 적용하지 않는다.
반품 후 실 환불액을 단계적으로 계산한 뒤, 마지막 줄에 '실 환불액: OOO원'으로 답하세요."""

r = client.models.generate_content(
    model=MODEL,
    contents=question,
    config=types.GenerateContentConfig(temperature=1.0),
)
answer = r.text.strip()
print(answer)

last_line = answer.splitlines()[-1]
print(f"\n[코드가 꺼낸 마지막 줄] {last_line}")
$ python ex03_cot_partial.py

실행 결과 (문장은 실행마다 다릅니다)

고객의 단순 변심에 따른 부분 반품 환불액을 단계별로 계산합니다.

1. **원래 구매 및 배송 조건 확인**
   - 총 구매금액: 35,000원 (텀블러 20,000원 + 머그컵 15,000원)
   - 최초 배송비: 무료배송 (35,000원 이상 구매로 고객 부담 없음)

2. **반품 후 잔여 주문금액 확인**
   - 반품 품목: 머그컵 (15,000원)
   - 잔여 주문금액: 35,000원 - 15,000원 = 20,000원

3. **추가 차감액(최초 배송비) 적용 여부 확인**
   - 조건: 부분 반품으로 잔여 주문금액이 3만원 미만이 되면 최초 배송비 3,000원을 추가 차감
   - 적용 결과: 잔여 금액이 20,000원으로 3만원 미만이므로, **최초 배송비 3,000원 차감 대상**에 해당함

4. **반품 배송비 확인**
   - 조건: 단순 변심 반품 배송비 편도 3,000원 고객 부담 (환불액에서 차감)

5. **최종 환불액 계산**
   - 반품하는 머그컵 가격: 15,000원
   - 차감액 1 (편도 반품 배송비): -3,000원
   - 차감액 2 (조건부 최초 배송비 추가 차감): -3,000원
   - 실 환불액: 15,000원 - 3,000원 - 3,000원 = 9,000원

실 환불액: 9,000원

[코드가 꺼낸 마지막 줄] 실 환불액: 9,000원

해설 — 답은 9,000원입니다. 이 교재를 준비하며 여섯 번 실행했을 때 여섯 번 모두 9,000원이었습니다.

풀이의 단계 근거가 된 정책 줄
반품 상품 금액 15,000원에서 반품 배송비 3,000원 차감 첫째 줄
남는 주문이 20,000원으로 3만원 미만이므로 최초 배송비 3,000원 추가 차감 둘째 줄

비교 5와 같은 정책 세 줄인데 상황이 달라지자 이번에는 둘째 줄이 쓰였습니다. 정책을 빠짐없이 적어 두면 같은 프롬프트 틀로 여러 경우를 계산할 수 있습니다.

마지막 줄을 코드로 꺼낸 것도 봅니다. "마지막 줄에 '실 환불액: OOO원'으로 답하세요"라고 자리와 형식을 정해 두었기 때문에 answer.splitlines()[-1] 한 줄로 결론만 얻었습니다. 풀이 과정은 기록으로 남기고, 고객에게는 결론만 보여 줄 수 있습니다.

← 이전 절정리와 체크리스트4장 →왜 구조화 출력이 필요한가 — 코드는 문장을 읽지 못한다
오명운 · macro@prag-ai.com