LLM 엔지니어링10장 · MCP 와 Prompt·Context Engineering8 / 22← 이전목차다음 →MetaContents

10장. MCP 와 Prompt·Context Engineering

퓨샷과 출력 꼴 고정

신입 사원에게 보고서 쓰는 법을 말로만 설명하는 것보다, 잘 쓴 보고서 두세 장을 보여 주는 편이 빠릅니다. 퓨샷은 모델에게 문제와 정답의 본보기를 보여 주는 것이고, 출력 꼴 고정은 "이 양식 칸에 맞춰 써 주세요"입니다.

제로샷과 퓨샷, 자유로운 글과 JSON 꼴을 견준 두 카드

왼쪽 카드는 예시를 메시지로 넣는 퓨샷입니다. user 에 문제, assistant 에 정답을 번갈아 넣으면 모델은 같은 꼴로 답합니다. 오른쪽 카드는 출력을 JSON 으로 고정하는 방법입니다. response_format={"type": "json_object"} 를 주면 모델이 JSON 만 쓰고, 우리 코드는 json.loads 로 읽습니다. 여기서 본보기 보고서가 퓨샷 예시, 양식 칸이 JSON 의 키입니다.

퓨샷을 넣는 두 방법

방법생김새좋은 점
글에 넣기시스템 지시에 "예: 문의 → 분류"를 줄줄이간단하다
메시지로 넣기user, assistant 메시지를 번갈아모델이 학습 때 본 대화 꼴과 같아 잘 따른다

출력 검증하기

JSON 으로 받았어도 키가 빠지거나 값이 목록 밖일 수 있습니다. 받은 뒤 코드로 확인합니다.

Python
import json

def check(text, labels=("배송", "교환반품", "결제", "회원", "기타")):
    """JSON 글을 읽어 '분류' 키가 목록 안의 값인지 본다. 틀리면 까닭을 돌려준다"""
    try:
        obj = json.loads(text)
    except json.JSONDecodeError:
        return None, "JSON 이 아닙니다"
    if obj.get("분류") not in labels:
        return None, f"분류 '{obj.get('분류')}' 는 목록에 없습니다"
    return obj, "통과"

for t in ['{"분류": "결제", "이유": "쿠폰 할인"}', '{"분류": "쿠폰"}', '분류: 결제']:
    print(check(t))

첫째는 통과, 둘째는 "분류 '쿠폰' 는 목록에 없습니다", 셋째는 "JSON 이 아닙니다"가 나옵니다. 통과하지 못하면 그 까닭을 붙여 다시 묻습니다("실패 복구 설계" 절).

코드에 나오는 함수

코드쉬운 뜻
json.loads(text)JSON 글을 파이썬 사전으로 읽는다. JSON 꼴이 아니면 오류를 낸다
try: … except json.JSONDecodeError:읽다가 JSON 해석 오류(decode error)가 나면 멈추지 말고 아래 줄로 가라는 뜻이다
labels=("배송", …)허용하는 분류 목록의 기본값이다. 괄호로 묶은 튜플이라 함수 안에서 바뀌지 않는다
obj.get("분류")"분류" 키의 값을 꺼낸다. 키가 없으면 오류 대신 None 을 준다
not in labels값이 허용 목록에 없으면 True 다
return None, "…"값 두 개(결과, 까닭)를 한꺼번에 돌려준다. 실패하면 결과 자리에 None 을 둔다

이 코드가 뜻하는 것

관공서 민원 창구로 읽어 봅니다. 창구 직원은 서류를 받으면 먼저 정해진 양식인지 보고, 다음에 칸의 값이 허용된 것인지 봅니다. 둘 중 하나라도 어긋나면 까닭을 적어 돌려줍니다. 여기서 정해진 양식이 JSON, 칸이 "분류" 키, 허용된 값이 labels, 까닭을 적은 반려 쪽지가 함수가 돌려주는 둘째 값입니다.

줄하는 일쉽게 말하면
obj = json.loads(text)글을 JSON 으로 읽는다양식에 맞는 서류인지 본다
except json.JSONDecodeError:읽지 못하면 "JSON 이 아닙니다"를 돌려준다"양식이 다릅니다" 반려
if obj.get("분류") not in labels:분류 값이 목록 밖이면 돌려보낸다"이 칸에 쓸 수 없는 값입니다" 반려
return obj, "통과"두 검사를 지나면 읽은 사전을 돌려준다접수 완료

결과의 둘째 경우 {"분류": "쿠폰"} 은 JSON 꼴은 맞지만 값이 목록 밖이라 걸렸습니다. "JSON 으로 받았다"와 "쓸 수 있는 답을 받았다"는 다른 일이라는 뜻입니다. 이 함수는 JSON 이 사전이라고 가정합니다. 모델이 [1, 2] 같은 목록 JSON 을 주면 obj.get 에서 오류가 나므로, 실제 서비스에서는 사전인지도 먼저 확인합니다.

작게 줄여서 눈으로 보기

json.dumps 의 ensure_ascii 옵션과, 모델이 자주 하는 실수인 작은따옴표 JSON 을 봅니다.

Python
import json

print(json.dumps({"분류": "결제"}))                       # 기본값: 한글을 \u 꼴로 바꾼다
print(json.dumps({"분류": "결제"}, ensure_ascii=False))   # 한글 그대로
for text in ['{"분류": "결제"}', "{'분류': '결제'}"]:
    try:
        print(json.loads(text))
    except json.JSONDecodeError as e:
        print("읽기 실패:", e.msg)
출력
{"분류": "결제"}
{"분류": "결제"}
{'분류': '결제'}
읽기 실패: Expecting property name enclosed in double quotes
결과읽는 법
{"분류": …}기본값에서는 한글이 글자 번호 꼴로 바뀐다. 뜻은 같지만 사람이 읽기 어렵다
{"분류": "결제"}ensure_ascii=False 를 주면 한글이 그대로 적힌다
{'분류': '결제'}큰따옴표 JSON 은 잘 읽혀 사전이 되었다
읽기 실패: Expecting property name enclosed in double quotes작은따옴표로 쓴 글은 JSON 이 아니다. "키 이름은 큰따옴표로 감싸야 한다"는 오류다

e.msg 는 오류 내용 가운데 사람이 읽는 설명 부분입니다. 파이썬 사전을 그대로 찍은 꼴(작은따옴표)은 JSON 처럼 보여도 json.loads 로 읽히지 않습니다. 모델의 답이 이렇게 오면 검증에서 걸러 다시 묻습니다.

주의할 점

  • 예시가 한쪽 분류에 몰리면 모델도 그 분류로 쏠립니다. 분류마다 고르게 넣습니다.
  • 예시가 많을수록 프롬프트가 길어집니다. 예시 20개는 질문 하나보다 수십 배 깁니다. 컨텍스트 예산 안에서 정합니다.
← 이전 절10장 · 프롬프트 엔지니어링다음 절 →10장 · 컨텍스트 엔지니어링