byteforce

CPN 한국어 자습서 · 러닝패스 2 / 4 — Building with the Claude API

API로 Claude에 접근하기

시스템 프롬프트

System prompts

Claude의 말투와 역할을 한 곳에서 정하는 방법입니다. 같은 질문이라도 system prompt 하나로 답이 어떻게 달라지는지 보고, 그걸 재사용 가능한 chat 함수에 깔끔하게 끼워 넣습니다.

전체 내레이션영상 나레이션 한국어 번역 (전체)

Stephen Grider · Anthropic 기술 스태프

이번 영상에서는 Claude가 생성하는 응답의 말투와 스타일을 어떻게 바꾸는지 살펴봅니다. 왜 이게 중요한지 감을 잡기 위해, 수학 튜터 챗봇을 만든다고 상상해 보겠습니다. 사용자는 이 챗봇으로 수학 문제 푸는 걸 도와달라고 합니다. 예를 들어 "5x + 2 = 3을 풀어줘"처럼요.

이 튜터에게 시키고 싶은 것과, 절대 시키고 싶지 않은 것이 있습니다. 시키고 싶은 것: 처음에는 힌트만 한두 개 줍니다. 학생이 어떻게 접근할지 살짝 알려주는 정도요. 그래도 이해하지 못하면, 그제야 단계별로 풀이를 안내합니다. 비슷한 문제의 풀이를 보여줘서 영감을 줄 수도 있습니다. 반대로, 절대 시키고 싶지 않은 것: 곧바로 완성된 정답을 내놓는 것, 그리고 "그냥 계산기 쓰세요"라고 말하는 것입니다.

이걸 해결하려고 system prompting이라는 기법을 씁니다. 시스템 프롬프트는 Claude가 응답할 스타일과 말투를 맞춤 설정하는 데 쓰입니다. 평범한 문자열로 정의해 create 함수 호출에 넘기면 됩니다. 시스템 프롬프트의 첫 줄에서는 보통 Claude에게 역할을 부여합니다. 예컨대 "너는 인내심 있는 수학 튜터다"라고 직접 말해 주는 거죠. 그러면 Claude는 실제 수학 튜터처럼 답하게 됩니다 — 인내심 있게, 설명을 많이 곁들이되, 학생의 질문에 곧바로 답하기보다 풀이로 안내하면서요.

실제로 보기 위해 노트북으로 돌아갑니다. 새 노트북을 만들고, 클라이언트 생성과 헬퍼 함수 세 개만 그대로 가져왔습니다. 새 노트북을 만들 필요는 없습니다 — 정리하려고 그렇게 한 것입니다. 다음 셀에서 아주 단순한 수학 문제를 물어보고, 시스템 프롬프트 없이 어떻게 답하는지 봅니다. 메시지 목록을 만들고, 사용자 메시지로 "5x + 3 = 2를 x에 대해 풀어줘"를 추가한 뒤, chat으로 답을 받아 출력합니다.

실행하면 정확한 단계별 풀이가 그대로 나옵니다. 학생에게 유용하긴 하지만, 우리가 원하는 건 이게 아닙니다. 학생이 스스로 생각하게 만들고, 스스로 답에 도달하게 하고 싶습니다. 작은 단계만 주면서 올바른 방향으로 안내하는 거죠. 그래서 시스템 프롬프트로 Claude의 응답 방식을 바꿉니다.

chat 함수 안에 system 변수를 만들고, 여러 줄 문자열을 할당합니다. 미리 적어 둔 시스템 프롬프트를 넣습니다 — 너는 인내심 있는 수학 튜터이고, 학생의 질문에 곧바로 답하지 말고, 풀이로 안내하라고요. 이걸 create 함수에 system 키워드 인자로 넘기고 셀을 다시 실행합니다.

훨씬 나은 답이 나옵니다. 풀이를 그대로 알려주는 대신, Claude는 학생에게 단계별로 풀어 보라고 유도합니다. 먼저 x를 한쪽으로 모으는 걸 제안하고, 그걸 어떻게 할지 학생에게 물어봅니다. 훨씬 상호작용적인 경험이고, 직접 답을 주는 것보다 학습에 도움이 됩니다. 시스템 프롬프트가 주어진 입력에 Claude가 어떻게 답할지 방향을 잡는 강력한 도구라는 게 분명합니다.

넘어가기 전에 chat 함수를 조금 리팩터링하겠습니다. 시스템 프롬프트를 함수 안에 하드코딩하는 대신, chat을 호출할 때마다 넘길 수 있게 하고 싶습니다. 그래서 이 문자열을 잘라 아래 셀로 옮기고, 시스템 프롬프트를 인자로 넘기는 형태로 바꿉니다. 이렇게 하면 하드코딩 없이 다양한 문제에 두루 쓸 수 있는 재사용 가능한 chat 함수가 됩니다. 이제 이 system 인자를 받아 create 함수에 넘겨야 합니다.

그런데 이게 생각보다 일이 좀 더 듭니다. 먼저 chat 함수에 system 키워드 인자를 추가하고 기본값을 None으로 둡니다. 이 상태로 실행하면 잘 동작합니다. 하지만 아래에서 시스템 프롬프트를 아예 넘기지 않기로 하고 지운 뒤 실행하면 오류가 납니다. system 값으로 None은 넘길 수 없기 때문입니다. 그래서 create 함수에 넘길 파라미터를 좀 더 동적으로 조립해야 합니다. system이 None이면 이 파라미터를 아예 포함하지 않으려는 거죠.

작은 리팩터링으로 해결합니다. 위쪽에 파라미터 딕셔너리를 만들고 model, max_tokens, messages를 딕셔너리 형태로 옮깁니다. 그다음 시스템 프롬프트가 넘어왔는지 확인해서, 넘어왔다면 params 딕셔너리에 system 키로 추가합니다. create 호출은 별표 두 개를 붙여 params를 펼쳐 넘기도록 바꿉니다. 이제 셀을 다시 실행하면, system 인자 없이 chat을 호출해도 문제없고, 시스템 프롬프트를 넘겨도 잘 동작합니다. chat 함수가 시스템 프롬프트를 지원하게 됐습니다.

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

약 6분
1

system prompt은 Claude의 말투·역할을 정한다 (출력 스타일 커스터마이즈)

2

평범한 문자열로 정의해 create()system 인자로 넘긴다

3

첫 줄에 역할을 부여하면(예: patient math tutor) 그 역할처럼 답한다

4

같은 질문도 system 유무에 따라 답이 크게 달라진다

5

system=None은 API에 못 넘긴다 — params조건부로 조립한다

왜 system prompt인가

Why system prompts

수학 튜터 챗봇을 만든다고 해봅니다. 학생이 How do I solve 5x+3=2 for x?라고 물을 때, Claude가 진짜 튜터처럼 굴길 바랍니다 — 정답만 뱉지 않고요.

좋은 수학 튜터라면 이렇게 답하길 원합니다.

반대로, 이렇게는 원하지 않습니다.

같은 모델, 같은 질문입니다. 달라지는 건 역할 지시 하나뿐입니다. 아래에서 직접 켜고 꺼 보세요.

시스템 프롬프트 · 같은 질문, 달라지는 답
system = "..." · 역할 부여
You are a patient math tutor.
Do not directly answer a student's questions.
Guide them to a solution step by step.
YouHow do I solve 5x+3=2 for x?
Claude그냥 풀어 줌

system prompt이란

What it is

system prompt은 Claude가 어떻게 답할지를 정하는 지시입니다. 사용자 질문(messages)과는 별도로, create()system이라는 키워드 인자로 넘깁니다. 보통 첫 줄에 역할을 부여하는 것으로 시작합니다 — 그 역할을 맡은 사람이라면 그렇게 답했을 방식으로 Claude가 응답합니다.

# 시스템 프롬프트 정의 → create()에 전달
노트북 · create()에 system 넘기기
system_prompt = """
You are a patient math tutor.
Do not directly answer a student's questions.
Guide them to a solution step by step.
"""

client.messages.create(
    model=model,
    messages=messages,
    max_tokens=1000,
    system=system_prompt,
)
model 변수

model은 앞 레슨에서 정해 둔 모델 문자열입니다(이 자습서는 현행 claude-sonnet-4-6). system은 모델 선택과 무관하게, 같은 모델의 응답 방식만 바꿉니다.

핵심은 셋입니다.

용어
system prompt (시스템 프롬프트)
Claude에게 역할·말투·태도를 지시하는 문자열입니다. 사용자 메시지와 별도로 create()에 전달합니다.
role (역할)
시스템 프롬프트 첫 줄에서 흔히 부여하는 정체성입니다. You are a patient math tutor.처럼 적습니다.
system (system 인자)
create()의 키워드 인자입니다. 여기에 시스템 프롬프트 문자열을 넘깁니다. messages 안의 메시지가 아닙니다.

재사용 가능한 chat 함수로

Making it reusable

system prompt을 chat 함수 안에 하드코딩하면, 그 말투로만 쓸 수 있습니다. 대신 인자로 받게 바꾸면, 같은 함수 하나로 수학 튜터도, 다른 역할도 자유롭게 쓸 수 있습니다. 그래서 system=None 기본값을 가진 인자를 추가합니다.

그런데 함정이 하나 있습니다. API는 system=None을 받지 않습니다. 그래서 "system이 있을 때만" system을 넘기도록, create()에 보낼 인자를 params 딕셔너리로 조건부 조립합니다.

Recap이전 헬퍼 다시 보기 — add_user_message · add_assistant_message

이 둘은 앞 레슨 멀티턴 대화에서 만든 그대로입니다. 바뀌는 건 chat뿐입니다.

헬퍼 둘 · 노트북 셀
def add_user_message(messages, text):
    user_message = {"role": "user", "content": text}
    messages.append(user_message)


def add_assistant_message(messages, text):
    assistant_message = {"role": "assistant", "content": text}
    messages.append(assistant_message)
# system을 인자로 받는 최종 chat
노트북 · 재사용 가능한 chat
def chat(messages, system=None):
    params = {
        "model": model,
        "max_tokens": 1000,
        "messages": messages,
    }

    if system:
        params["system"] = system

    message = client.messages.create(**params)
    return message.content[0].text

아래에서 두 가지 호출 방식을 눌러, params가 어떻게 달라지는지 직접 확인해 보세요.

params 조립기 · system이 있을 때만 넣기

chat()을 두 가지로 호출해 보세요. params 딕셔너리가 어떻게 달라지는지 보입니다.

함수 안의 분기

조립된 params

# chat() 안에서 조립 params = { "model": model, "max_tokens": 1000, "messages": messages, "system": system, }

client.messages.create( model=model, max_tokens=1000, messages=messages, system=None, )

✗ Error — system 인자에는 None을 넘길 수 없습니다

그래서 값이 없을 때는 키 자체를 빼야 합니다. if system:로 값이 있을 때만 params["system"]에 넣는 이유입니다.

if system

if system:None뿐 아니라 빈 문자열 ""도 걸러 냅니다. 빈 시스템 프롬프트는 어차피 넘길 이유가 없으니, 이 조건 하나로 두 경우가 모두 깔끔하게 처리됩니다.

직접 보기 — 없을 때 vs 있을 때

Seeing the difference

이제 chat이 system을 받으니, 같은 질문을 두 번 보내 비교합니다. 한 번은 system 없이, 한 번은 튜터 역할을 넣어서요.

# 1. system 없이
노트북 · system 없이
messages = []
add_user_message(messages, "How do I solve 5x+3=2 for x?")

# system 없이 호출
answer = chat(messages)
print(answer)
출력 — 그냥 풀어 버림
# Solving 5x + 3 = 2 for x

To solve, isolate x.
Step 1 — Subtract 3:  5x = 2 − 3 = −1
Step 2 — Divide by 5:  x = −1/5  (= −0.2)
# 2. 같은 질문 + 튜터 system
노트북 · system 추가
messages = []
add_user_message(messages, "How do I solve 5x+3=2 for x?")

system = """
You are a patient math tutor.
Do not directly answer a student's questions.
Guide them to a solution step by step.
"""

# 같은 질문 — system만 추가
answer = chat(messages, system=system)
print(answer)
출력 — 단계별로 유도
I'd be happy to guide you through this step
by step. Our goal is to isolate x on one side.

What do you think would be a good first step
to isolate x? What operation could we do to
both sides to start moving terms around?
차이

코드에서 바뀐 건 system 문자열 하나와, 그걸 넘기는 인자뿐입니다. 그런데 응답은 "정답 통보"에서 "스스로 생각하게 하는 안내"로 완전히 달라졌습니다. 이게 system prompt의 힘입니다.

자주 하는 실수

Common mistakes
주의
  • system은 messages 안에 넣는 게 아닙니다. user·assistant 메시지와 달리, system은 create()별도 인자입니다. messages{"role": "system", ...}을 넣는 건 이 API의 방식이 아닙니다.
  • system=None을 직접 넘기면 오류입니다. 값이 없을 땐 인자 자체를 생략해야 합니다. 그래서 params를 조건부로 조립합니다.
  • 함수 안에 system을 직접 넣지 마세요. 하드코딩하면 그 말투로만 쓸 수 있습니다. 인자로 받으면 한 함수로 여러 역할을 쓸 수 있습니다.

퀴즈

Quick check

Q1시스템 프롬프트는 어떻게 전달하나요?

Q2chat(messages)처럼 system 없이 호출하면? (chat은 system=None 기본값 + 조건부 params)

Q3params 딕셔너리를 조건부로 조립하는 이유는?