byteforce

CPN 한국어 자습서 · Claude Certified Developer — Foundations Prep

2-2 · 프롬프팅 기법

시스템 프롬프트, XML, 퓨샷, 출력 제약

System prompts, XML, few-shot, and output constraints

대화 창에서 한 번 잘 됐던 프롬프트도, 프로덕션(실서비스 환경)에서 시험해 보지 않은 입력을 만나면 그대로 깨지는 경우가 많습니다. 이럴 때는 설명을 길게 덧붙이기보다, 프롬프트에 빠져 있는 구조 요소 하나를 찾아 더하는 쪽이 효과적입니다. 이 레슨에서는 실패한 출력을 읽는 방법부터 시작해, 실패를 고치는 네 가지 기법을 차례로 살펴봅니다.

이 장에서 배우는 것What you'll learn

약 29분
1

실패한 출력을 보고 빠진 구조 요소를 찾아내는 법

2

네 가지 기법 — 시스템 프롬프트 · XML 태그 · 퓨샷 예시 · 출력 제약

3

분류 프롬프트를 고치기 전과 후로 비교하는 예제

4

프롬프트 대신 API가 출력 형태를 보장하는 구조화된 출력

5

제약 디코딩을 쓸 때 생기는 비용 네 가지

6

길어지기만 하는 프롬프트 — 일찍 알아차릴수록 좋은 실패 패턴

먼저 짚고 갈 용어
시스템 프롬프트 (system prompt)
대화 전체에 적용되는 규칙을 적어 두는 자리. 역할·범위·출력 형식을 여기에 정해 두면 매 응답에 똑같이 적용된다.
XML 태그
<example>처럼 여닫는 태그로 지시문과 데이터·예시의 경계를 표시하는 방법.
퓨샷 예시 (few-shot examples)
올바른 입력–출력 쌍을 직접 보여 주는 기법. 패턴을 말로 설명하는 대신 그대로 제시한다.
출력 제약 (output constraint)
응답의 형식·필드 이름·어디서 멈출지를 명시하는 규칙. 내용이 아니라 형태를 정한다.
구조화된 출력 (structured outputs)
JSON 스키마를 API에 건네, 생성 단계에서부터 스키마에 맞는 출력만 나오게 하는 Claude API 기능.
파서 (parser)
모델의 응답을 받아 해석하는 다음 단계 코드. 기대한 형태와 다른 응답이 오면 여기서 깨진다.

출력의 형태를 잡는 네 가지 기법

Four techniques for a reliable output shape

첫 응답이 기대와 다를 때, 많은 경우 프롬프트에 설명을 더 추가하는 방식으로 해결하려 합니다. 하지만 이 방식으로는 문제가 잘 해결되지 않고, 원인을 찾기도 어려워집니다. 문장을 다듬는 것은 말하는 방식을 바꿀 뿐, 프롬프트에 빠져 있는 구조 요소를 채워 주지는 못하기 때문입니다.

예를 들어 Claude가 지시문과 입력 데이터를 구분하지 못하고 섞어 읽는 상황이라면, 문장을 아무리 또렷하게 다듬어도 결과가 달라지지 않습니다. 출력 형식이 계속 어긋나는 경우도 마찬가지입니다. "형식을 지켜 주세요"라는 한 줄을 덧붙이는 것만으로는 개선되기 어렵습니다.

이럴 때 효과적인 개선은, 프롬프트가 어떻게 실패하고 있는지 확인하는 것부터 시작합니다. 실패 유형이 보이면 네 기법 중 무엇이 빠졌는지 알 수 있고, 거기에 맞는 기법 하나만 더하면 됩니다.

FAILURE → MISSING PIECE — 실패 유형별 진단표

관찰된 증상빠져 있는 것그 기법이 해결하는 이유
결과가 엉뚱한 형태로 돌아온다. 라벨을 기대했는데 문장이 오고, JSON을 기대했는데 일반 텍스트가 온다.출력 제약. 응답의 형식·필드 이름·어디서 멈출지를 프롬프트에서 아직 정해 주지 않은 상태다.출력 제약은 내용과 별개로 응답의 형태를 정한다. 이것이 없으면 Claude는 그럴듯한 텍스트를 내놓는데, 다음 단계 파서가 받을 수 있는 형태가 아니다.
내용이 어긋난다. 다루는 범위가 조금씩 넓어지고, 어조가 바뀌고, 물어본 것보다 넓은 질문에 답한다. 대화가 길어질수록 심해진다.시스템 프롬프트, 또는 지금보다 더 구체적인 시스템 프롬프트. 규칙이 너무 느슨하게 적혀 있어서 대화가 이어지는 동안 유지되지 못했다.시스템 프롬프트에 적은 규칙은 사용자가 무엇을 입력하든 모든 응답에 적용된다. 여기가 구체적이지 않으면 역할·범위·형식을 대화 내내 지켜 줄 장치가 없다.
과제는 맞는데 구조를 멋대로 만든다. 할 일은 제대로 이해했는데, 요청한 적 없는 형태로 출력한다.퓨샷 예시. 말로 된 설명만으로는 Claude가 정확한 구조를 짐작하지 못한다.퓨샷 예시는 패턴을 말로 설명하는 대신 그대로 보여 준다. 올바른 입력–출력 쌍을 하나 주면, 글로 아무리 설명해도 잘 전달되지 않던 정확한 형태가 한 번에 전달된다.
시험해 본 입력에서는 멀쩡한데 조금 다른 입력이 들어오면 깨진다. 예외 사례, 특이한 필드, 예상 못 한 입력.그런 입력을 다루는 제약. 프롬프트가 정상 케이스만 다루고 있고, 파서를 깨뜨리는 사례에 대한 규칙이 없다.좁은 범위의 입력으로만 시험했을 때 자주 나타나는 상황이다. 그런 변형 입력을 제약에 직접 적어 넣거나 예시로 추가하면, 시험 때는 드러나지 않았던 허점이 메워진다.
쉽게 말하면

문장만 고치는 것은 같은 부탁을 말투만 바꿔서 다시 하는 것과 비슷합니다. 기법을 더하는 것은 빠진 부품을 끼우는 것에 가깝고요. 그래서 어느 부품이 빠졌는지 확인하는 일부터 시작합니다.

작업 예제 — 분류 프롬프트 전과 후

Worked example: a classification prompt before and after

순서는 단순합니다. 어떤 실패인지 먼저 확인하고, 거기에 맞는 기법 하나를 더하고, 다시 실행합니다. 그래도 실패하면 다시 진단합니다. 수정할수록 프롬프트가 길어지는데 결과가 크게 달라지지 않는다면, 새로운 조건을 추가하기 전에 현재 문제가 어디에서 발생하는지 먼저 살펴볼 필요가 있습니다.

진단표의 첫 행이 실제로 어떻게 나타나는지 예제로 확인해 보겠습니다. 내용은 맞는데, 다음 단계 코드가 받을 수 없는 형태로 출력이 나오는 경우입니다. 한 개발자가 지원 티켓을 billing·technical·escalation 세 가지로 분류하려고 합니다. 첫 프롬프트는 출력에 아무 제약이 없는, 지시문 한 줄짜리입니다.

제약 없는 첫 프롬프트 — 출력 형태가 실행마다 달라진다

prompt · v0
System: "You are a support classifier. Classify the ticket."

User:   <ticket>I was charged twice for the same month.</ticket>

이렇게 실행하면 Claude는 어떤 때는 Billing, 어떤 때는 billing, 가끔은 "This looks like a billing issue." 같은 문장 전체를 돌려줍니다. 다음 단계 라우터분류 라벨을 받아 티켓을 맞는 처리 흐름으로 보내는 다음 단계 코드.는 정해진 라벨 중 하나가 온다고 가정하고 있어서, 이렇게 매번 다른 출력이 오면 에러가 납니다.

진단표에 맞춰 보면 첫 행에 해당합니다. 출력이 파서가 받을 수 없는 형태로 오고 있으니, 빠진 것은 출력 제약입니다. 이 제약을 더하면서 기법 두 개를 같이 넣으면 좋습니다. 라벨 목록을 고정하고 형식을 알려 줄 때는, 글로 길게 설명하기보다 퓨샷 예시와 XML 태그로 직접 보여 주는 쪽이 효과적이기 때문입니다.

출력 제약 + XML 태그 + 퓨샷 예시를 적용한 프롬프트

prompt · constrained
System: "You are a support classifier. Classify each ticket into exactly one of:
BILLING, TECHNICAL, ESCALATION. Return only the label. No other text."

<example_input>My account shows two charges for April.</example_input>
<ideal_output>BILLING</ideal_output>

<example_input>The API keeps returning a 429 error.</example_input>
<ideal_output>TECHNICAL</ideal_output>

User:   <ticket>I was charged twice for the same month.</ticket>

여기서 세 기법이 각자 다른 일을 맡습니다.

이 셋이 합쳐지면 프로그램에서 그대로 라우팅할 수 있을 만큼 일관된 결과가 나옵니다. 그렇다면 기법을 언제 다 쌓고, 언제 덜어내면 좋을까요?

STACK · SIMPLIFY · DIAGNOSE — 세 가지 판단 기준

네 기법을 쌓는다출력 형식이 명확하게 정의된 과제에는 네 기법을 함께 씁니다. 형식이 잘 정해져 있고, 예외 사례도 예시로 처리할 수 있는 과제가 여기에 해당합니다.
프롬프트를 덜어낸다기법 하나로 끝나는 단순한 과제도 많습니다. "이 문단을 요약해 줘" 정도의 프롬프트라면 퓨샷 예시와 출력 스키마까지 더하지 않아도 충분합니다.
더하기 전에 진단한다다섯 번을 다시 썼는데도 출력이 계속 틀리면, 텍스트를 더 넣기 전에 실패 유형부터 진단해 봅니다. 길이만 늘고 결과가 그대로일 때 특히 필요한 단계입니다.

다시 쓰기 전에, 진단부터

The iteration loop: Diagnosing before re-prompting

첫 응답이 기대와 다르면 설명부터 더하게 되는 경우가 많다는 이야기를 앞에서 했습니다. 개선이 잘 되는 순서는 조금 다릅니다. 먼저 실패 유형을 확인하고, 그 결과를 근거로 다시 씁니다. 실패 유형이 확인되면 어떤 기법이 빠졌는지도 함께 보입니다.

결국 손볼 부분은 표현이 아니라 구조일 때가 많습니다. 어느 기법이 빠졌는지 찾은 다음, 그 하나만 더하면 됩니다.

핵심

시스템 프롬프트는 대화 전체에 적용되는 규칙 모음입니다. 한 번 적어 두면 세션 내내 유지되는 자리라고 생각하면 됩니다. Claude의 역할, 출력 형식, 대화가 어떻게 흘러가도 바뀌면 안 되는 규칙을 여기에 적어 두면 됩니다.

출력 제어를 프롬프트에서 API로

Moving output control into the API with structured outputs

지금까지 본 기법은 전부 프롬프트에 지시를 적어 두고, Claude가 그 지시를 따라 주기를 기대하는 방식입니다. 대체로 잘 동작하지만, 프롬프트는 어디까지나 요청입니다. 모델이 필요 없는 문장을 하나 덧붙이거나, 필드 이름을 틀리거나, 파서를 깨뜨리는 잘못된 JSON을 돌려줄 가능성은 여전히 남아 있습니다.

Claude API에는 이 가능성 자체를 없애는 별도 기능이 있습니다. 구조화된 출력(structured outputs)입니다. 원하는 형태를 말로 요청하는 대신 JSON 스키마를 API에 건네면, 모델은 생성 단계에서부터 그 스키마에 맞는 출력만 내도록 제한됩니다. 이 동작 방식이 제약 디코딩(constrained decoding)입니다. Claude가 토큰을 하나씩 생성할 때 API가 스키마에 맞는 토큰만 허용하기 때문에, 스키마를 어기는 응답은 애초에 만들어질 수가 없습니다.

쉽게 말하면

프롬프트 지시는 부탁이고, 스키마 제약은 규칙입니다. 부탁은 어길 수 있지만, 규칙이 걸려 있으면 어긴 응답 자체가 아예 만들어지지 않습니다.

구조화된 출력은 실제 파이프라인에서 자주 만나는 두 가지 상황에 씁니다. 둘은 모델이 반환하는 내용의 서로 다른 부분을 제약하는데, 따로 써도 되고 한 요청에서 같이 써도 됩니다.

이 기능이 프로덕션 코드에서 중요한 이유는, 시험해 보지 않은 입력에서도 출력 형태를 믿을 수 있어야 하기 때문입니다. "JSON만 반환하라"는 프롬프트 지시는 시험해 본 사례에서는 잘 지켜지다가, 시험하지 않은 예외 사례에서 어긋나기 시작합니다. 앞의 분류 예제에서 본 것과 같은 실패입니다. 스키마 제약은 그렇게 어긋나지 않습니다. 모델이 지시를 기억해 주기를 기대하는 대신, API가 토큰 단위로 강제하기 때문입니다. 출력이 올바른지 나중에 검사하던 일이, API가 처음부터 잘못된 출력을 막아 주는 방식으로 바뀌는 겁니다.

다만 생성을 제약하는 데는 비용이 따릅니다. 프로덕션에서 이 방식을 고를 때는 무조건 켜 둘 것이 아니라, 아래 네 가지 비용을 따져 본 뒤에 결정하는 것이 좋습니다.

길어지기만 한 프롬프트

Watch Out: The prompt that grew longer instead of better

프로덕션에 낼 준비가 다 된 것처럼 보이는 프롬프트도, 눈에 잘 띄지 않는 방식으로 실패하고 있는 경우가 있습니다. 예외 사례에서 필드가 빠지거나 제약이 무시되는 식인데, 원인을 짚어 보면 제약을 충분히 정밀하게 적지 않았을 때가 대부분입니다.

앞의 예제에서 봤던 그 분류 과제입니다. 아래 표는 한 개발자가 제약 없는 지시문에서 시작해 프롬프트를 여섯 번 고쳐 쓴 기록입니다. 회차마다 글은 늘어나는데 출력은 계속 어긋납니다. 어떤 제약이 빠졌는지 확인하지 않은 채 설명만 계속 추가하면 이런 패턴이 나타나기 쉽습니다.

SIX PASSES — 여섯 번의 고쳐 쓰기 기록

회차무엇을 더했나출력이 어떻게 됐나
1"이 티켓을 billing, technical, escalation으로 분류하라."문장 전체가 돌아온다: "This appears to be a billing issue." 파서가 깨진다.
2"Be concise."와 "카테고리 이름만 쓸 것"을 추가.어떤 때는 Billing, 다른 때는 billing. 대소문자 불일치로 라우터가 깨진다.
3각 카테고리를 상세히 설명하는 세 문단을 추가.단순한 티켓은 통과. 모호한 티켓에서 billing/technical처럼 두 라벨을 돌려준다. 슬래시 문자 때문에 파서가 깨진다.
4"두 카테고리를 반환하지 말 것"과 "모호하면 가장 그럴듯한 하나를 고를 것"을 추가.티켓의 80%에서 동작. 두 카테고리에 다 걸칠 만한 티켓(예: "결제는 됐는데 기능도 멈췄어요")에서는 라벨 대신 설명 전체를 돌려준다.
5예외 사례에 관한 두 문단과 "정확하게"라는 당부를 추가.출력까지 장황해지기 시작한다 — 호출당 2,000자 이상. 모델이 응답의 길이와 문체를 입력에 맞추기 때문에, 프롬프트가 길고 초점이 흐리면 출력도 그만큼 길고 산만해진다. 출력이 길어져 지연은 크게 늘었는데 정확도 개선은 없다.
6지시문 전체를 JSON 스키마와, 정확한 입력–출력 쌍을 보여 주는 퓨샷 예시 2개로 교체.모든 티켓에서 {"category": "billing"} 형태를 돌려준다. 파서가 동작한다. 지연이 줄어든다. 모호한 티켓의 정확도도 4회차와 같은 수준이다.

이 여섯 번의 과정에서 잘못된 지점은 두 곳이고, 성격이 서로 다릅니다. 4회차는 진단 실패입니다. 개발자가 문제를 잘못 짚어서 필요한 제약 대신 설명을 더했고, 출력은 계속 깨져 있었습니다. 5회차는 엔지니어링 실패입니다. 프롬프트가 장황해지면서 응답 속도까지 나빠졌습니다. 모델이 응답 길이를 입력에 맞추다 보니 호출당 2,000자를 넘겼는데, 정확도 이득은 없었습니다. 고치는 방법은 둘 다 같습니다. 출력 제약과 퓨샷 예시를 넣는 것입니다. 그래도 이 둘이 서로 다른 실패라는 점은 구분해 둘 필요가 있습니다. 첫 번째 문제를 해결한 뒤에도 두 번째 문제는 다시 생길 수 있기 때문입니다.

출력 제약과 퓨샷 예시를 적용해 고친 프롬프트

prompt · fixed
System: "You are a support classifier. Classify each ticket into exactly one of:
BILLING, TECHNICAL, ESCALATION. Return only the label. No other text."

<sample_input>My account shows two charges for April.</sample_input>
<ideal_output>BILLING</ideal_output>

<sample_input>The API keeps returning a 429 error.</sample_input>
<ideal_output>TECHNICAL</ideal_output>

User:   <ticket>I was charged twice for the same month.</ticket>
주의 · WATCH OUT
  • 여섯 번 모두 프롬프트를 길게 만들었을 뿐, 빠져 있던 출력 제약을 더한 회차는 한 번도 없었습니다.
  • 개발자는 매번 문제를 더 자세히 설명했지만, Claude에게 필요한 것은 billing 티켓에 대한 상세한 설명이 아니라 "허용되는 응답은 대문자 라벨 한 단어뿐"이라는 규칙이었습니다.
  • 고치는 데 필요한 것은 사실 두 줄이었습니다 — 정확한 형식을 명시하는 출력 제약과, 모호한 사례를 처리하는 퓨샷 예시.
  • 이 패턴은 일찍 알아차릴수록 좋습니다. 다시 쓰기를 세 번 연달아 실패했다면, 텍스트를 더하기 전에 어떤 기법이 빠졌는지부터 진단해 보세요.
기억할 점

스스로 점검 — 깨진 프롬프트 고치기

Checkpoint · Fix the broken prompt

원문의 점검 과제는 직접 써 보는 서술형입니다. 아래 프롬프트는 지원 티켓에서 category·urgency·한 문장짜리 summary 세 필드를 가진 JSON 객체를 뽑아내려는 것인데, 결함이 하나 있습니다. 무엇이 빠졌는지 먼저 진단해 보고, 고친 시스템 프롬프트를 직접 써 보세요.

BROKEN PROMPT — 결함이 하나 있는 추출 프롬프트

prompt · broken
System: "You are a support ticket processor. Extract the key information
from the ticket below."

User:   <ticket>My API key stopped working after I rotated it last night.
I have a production deployment that is failing. This needs to be fixed
immediately.</ticket>

이어서 객관식으로 점검합니다. 정답을 먼저 떠올려 본 뒤 골라 보세요. 맞히면 설명이 나옵니다.

Q1위 프롬프트의 결함은 무엇이었을까요?

Q2구조화된 출력을 켰는데도 스키마와 맞지 않는 응답이 돌아올 수 있는 경우는?

Q3다시 쓰기를 세 번 연달아 했는데도 출력이 계속 틀립니다. 다음에 할 일은 무엇일까요?

MEMBER SESSION REQUIRED · REGISTRATION IS FREE

여기부터는 등록한 분에게 열립니다.

전 코스는 계속 무료입니다. 등록하면 이 코스의 남은 38개 레슨을 끝까지 읽을 수 있습니다.

등록하고 이어서 읽기

이미 등록하셨다면 그때 쓰신 이메일을 넣어 주세요.