LLM 앱 개발 실전 #2 메시지와 파라미터 이해하기
1편에서 첫 호출을 해봤습니다. 그때는 messages에 사용자 메시지 하나만 넣고 넘어갔는데, 이번에는 그 messages의 구조와 호출에 함께 넘기는 주요 파라미터를 제대로 살펴봅니다. 이 점을 알아야 Claude에게 맥락과 지시를 정확히 전달할 수 있습니다.
대화는 메시지의 목록이다 #
messages는 이름 그대로 메시지의 목록입니다. 각 메시지는 누가 말했는지를 나타내는 role과 내용인 content로 이루어집니다. role에는 두 가지가 있습니다.
user— 사용자가 한 말assistant— Claude가 한 답변
1편에서는 user 하나만 넣었습니다. 그런데 대화를 이어가려면 이전에 주고받은 내용을 알려줘야 합니다. 여기서 LLM API의 중요한 성질이 하나 있습니다. API는 이전 대화를 기억하지 않습니다. 매 호출은 독립적이고, 서버는 지난번에 무슨 말을 했는지 저장해 두지 않습니다. 그래서 대화를 이어가려면 지금까지의 내용을 매번 통째로 다시 보내야 합니다.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{"role": "user", "content": "내 이름은 민수야."},
{"role": "assistant", "content": "안녕하세요 민수님, 반갑습니다."},
{"role": "user", "content": "내 이름이 뭐라고 했지?"},
],
)
for block in response.content:
if block.type == "text":
print(block.text)assistant 메시지를 직접 목록에 넣어 이전 답변을 다시 들려주는 모습에 주목하세요. Claude는 이 messages 전체를 읽고 답하므로, “민수"라는 이름을 정확히 기억해 답합니다. 만약 마지막 user 메시지만 보냈다면 Claude는 이름을 알 방법이 없습니다.
그래서 챗봇을 만들 때는 주고받은 메시지를 차곡차곡 목록에 쌓아 두고, 호출할 때마다 그 전체를 보냅니다. 대화가 길어지면 이 목록도 길어지고, 길어진 만큼 토큰 비용이 올라갑니다. 이걸 어떻게 관리하는지는 #9에서 다루겠습니다.
대화를 코드로 이어가기 #
위 예제는 messages를 손으로 적어 넣었습니다. 실제 챗봇에서는 대화가 오갈 때마다 이 목록에 메시지를 더해 갑니다. 패턴은 간단합니다. 사용자가 말하면 user 메시지를 목록에 더하고, 호출해서 받은 답변을 assistant 메시지로 다시 목록에 더합니다.
import anthropic
client = anthropic.Anthropic()
messages = []
def chat(user_input: str) -> str:
messages.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=messages,
)
answer = next(b.text for b in response.content if b.type == "text")
messages.append({"role": "assistant", "content": answer})
return answer
print(chat("내 이름은 민수야."))
print(chat("내 이름이 뭐라고 했지?")) # "민수" 를 기억해 답한다핵심은 받은 답변(assistant)을 반드시 다시 messages에 더해 두는 부분입니다. 이 한 줄을 빼먹으면 다음 호출 때 Claude는 자기가 방금 한 말을 모릅니다. 사용자 입력만 쌓이고 답변은 쌓이지 않아, 대화가 한쪽만 이어지는 셈이 됩니다.
system 프롬프트로 역할과 규칙 정하기 #
system은 messages와 별개의 파라미터로, Claude에게 역할과 규칙을 미리 일러두는 데 씁니다.
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="너는 초보자에게 친절한 파이썬 강사야. 답변에는 항상 짧은 예제 코드를 포함해.",
messages=[
{"role": "user", "content": "리스트를 정렬하는 법을 알려줘."}
],
)system에 적은 지침은 이후 모든 답변에 영향을 줍니다. user 메시지가 매번 바뀌어도 system의 지침은 그대로 유지됩니다. 위 예제라면 사용자가 무엇을 묻든 Claude는 친절한 강사의 말투로 예제 코드를 곁들여 답합니다.
system과 user의 역할을 구분해 두면 좋습니다.
system— 대화 내내 지속되는 역할, 말투, 출력 규칙user— 이번 turn의 구체적인 요청
“너는 파이썬 강사야” 같은 지침을 매 user 메시지마다 반복할 필요가 없다는 뜻입니다. 한 번 system에 적어 두면 됩니다.
max_tokens — 응답 길이의 상한 #
max_tokens는 응답이 최대 몇 토큰까지 생성될지 정하는 상한입니다. 1편에서 봤듯이 이 값을 넘으면 응답이 중간에 잘립니다.
토큰은 단어보다 작은 글자 조각이라고 생각하면 됩니다. 영어는 대략 한 단어가 1〜2토큰이고, 한국어는 글자당 토큰을 더 씁니다. 정확한 환산은 지금 신경 쓰지 않아도 되고, “길수록 토큰이 많다” 정도로 충분합니다.
너무 작게 잡으면 답이 잘립니다. 그렇다고 크게 잡아서 손해를 보지는 않습니다. 실제 생성된 만큼만 과금되고, max_tokens는 어디까지 허용할지의 상한일 뿐입니다. 그래서 보통 넉넉하게 1024나 그 이상을 줍니다.
응답이 잘렸는지는 response.stop_reason으로 확인합니다.
end_turn— Claude가 할 말을 마치고 자연스럽게 끝났습니다.max_tokens— 상한에 걸려 중간에 잘렸습니다. 값을 늘려야 합니다.
temperature — 답변의 무작위성 #
temperature는 답변이 얼마나 다양하게 나올지를 조절합니다. 0에 가까우면 매번 비슷하고 안정적인 답이 나오고, 1에 가까우면 더 다양하고 창의적인 답이 나옵니다. 값의 범위는 0~1입니다.
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
temperature=0.0,
messages=[
{"role": "user", "content": "버그를 고친 커밋 메시지를 한 줄로 써줘."}
],
)언제 어떤 값을 쓰는지는 작업 성격으로 정합니다.
- 낮게(0에 가깝게) — 사실 추출, 분류, 코드 생성처럼 일관성과 정확성이 중요할 때
- 높게(1에 가깝게) — 광고 카피, 브레인스토밍처럼 다양한 표현이 필요할 때
한 가지 오해를 짚어 두겠습니다. temperature=0이라고 해서 매번 글자까지 똑같은 답이 나오는 것은 아닙니다. “더 안정적"이라는 뜻이지 “완전히 똑같다"는 뜻은 아닙니다.
temperature는 Sonnet과 Haiku에서 쓸 수 있습니다. 가장 강력한 등급인 Opus 최신 모델(claude-opus-4-8 등)은 무작위성을 내부에서 알아서 조절하므로 이 파라미터를 받지 않고, 넣어서 호출하면 오류가 납니다. 이 시리즈의 기본 모델인 claude-sonnet-4-6에서는 정상 동작합니다.흔히 걸려 넘어지는 곳 #
메시지를 다루다 보면 자주 마주치는 문제들입니다.
- 첫 메시지는
user여야 한다 —messages의 첫 항목을assistant로 넣으면 오류가 납니다. 대화는 항상 사용자의 말로 시작합니다. - Claude가 방금 한 말을 기억하지 못한다 — 대개 이전 대화를 안 보낸 경우입니다. API는 상태를 저장하지 않으므로, 이어지는 대화에서는 직전까지의
messages를 모두 포함해야 합니다. system지침이 안 먹힌다 — 역할 지침을user메시지 안에 적지 않았는지 확인합니다. 지침은 별도의system파라미터로 넣어야 합니다.
마무리 #
이번 글에서는 messages의 구조와 호출에 함께 넘기는 핵심 파라미터를 정리했습니다.
- 대화는
role을 가진 메시지의 목록이고, API는 상태를 저장하지 않으므로 멀티턴에서는 히스토리를 매번 전부 보냅니다. system은 대화 내내 지속되는 역할과 규칙,user는 이번 요청입니다.max_tokens는 응답 길이의 상한이고,stop_reason으로 잘렸는지 확인합니다.temperature는 답변의 무작위성을 조절합니다. 0은 안정적, 높을수록 다양합니다.
다음 글인 “LLM 앱 개발 실전 #3 스트리밍으로 응답 실시간 출력"에서는 응답을 한 번에 받지 않고 생성되는 대로 화면에 흘려보내는 방법을 다루겠습니다. 긴 답변을 기다리는 체감 시간을 크게 줄여 주는 기법입니다.