회사 IT 헬프데스크에 이런 메시지가 들어왔다고 해 보겠습니다. 티켓은 헬프데스크에 들어온 요청 한 건을 기록해 두는 접수 번호표입니다. 같은 LLM을 쓰는 프로그램 세 개가 이 요청을 받으면 어떻게 될까요? 차이는 모델의 똑똑함이 아니라 다음에 할 일을 누가 정하느냐에서 생깁니다.
스크롤을 내리면 세 칸이 한 단계씩 진행됩니다
옆으로 밀면 세 칸을 차례로 볼 수 있습니다 →
LLM이 답을 글로 씁니다. 그다음은 사람이 합니다.
개발자가 미리 정해 둔 순서대로 갑니다.
LLM이 결과를 보고 다음 할 일을 고릅니다.
설명을 위해 만든 예시 시나리오입니다. 실제 모델이 늘 이렇게 판단하지는 않습니다. 상황을 바꿔 보세요. 챗봇과 워크플로는 같은 길로 가고 Agent만 길을 바꿉니다.
세 프로그램 모두 안에 같은 LLM이 들어 있습니다. LLM(Large Language Model)은 엄청나게 많은 글을 읽고 배운 프로그램입니다. 받은 글 뒤에 올 말을 이어 씁니다. 휴대폰 키보드가 다음 단어를 추천하는 것과 비슷한데, 한 단어 대신 긴 답을 통째로 씁니다.
LLM은 글을 토큰 단위로 읽고 씁니다. 토큰은 단어보다 조금 작은 글 조각입니다. 모델에게 주는 글은 프롬프트라고 부릅니다. 아래에서 티켓을 열어 달라고 보내 보세요.
LLM이 "티켓을 열었습니다"라고 써도 티켓은 생기지 않습니다. 실제로 일어난 일은 글자가 나온 것뿐입니다. LLM은 회사 시스템에 직접 접속하지 못합니다. 접속했다는 문장을 쓸 수 있을 뿐입니다.
그래서 세 프로그램의 차이는 LLM 바깥에 있습니다. LLM이 쓴 글을 받아서 무엇을 하느냐가 프로그램마다 다릅니다.
돌아오는 글은 토큰 단위로 하나씩 이어 붙습니다. 토큰을 나누는 방식은 설명을 위해 단순하게 그렸습니다.
프로그램이 LLM을 쓸 때는 API로 부릅니다. API는 다른 프로그램에 일을 시키는 정해진 주문 양식입니다. 식당 주문서처럼 칸이 정해져 있어서 그 칸을 채워 보내면 답이 돌아옵니다.
요청에는 대화가 메시지 목록으로 들어갑니다. 메시지마다 누가 한 말인지 적는 role이 붙습니다. system은 개발자가 모델에게 주는 지시, user는 사용자의 말, assistant는 모델이 앞서 한 말입니다.
base_url 어느 주소로 보낼지비우면 OpenAImodel 어떤 모델에게 시킬지예: gpt-5.5messages 대화 목록
system 너는 IT 헬프데스크 상담원이다.
user 와이파이가 안 잡혀요. 티켓 열어 주세요.choices[0].message 모델이 쓴 답
assistant 와이파이를 껐다 켜 보세요. 그래도 안 되면 티켓을 열어 드릴게요.usage 쓴 토큰 수요금과 속도가 이 숫자로 정해집니다번호는 아래 코드의 번호 표지와 같습니다. 답 문장은 설명을 위한 예시입니다.
Python으로 쓰면 아래와 같습니다. OpenAI가 만든 openai 패키지(3.29)를 씁니다. 이런 패키지를 SDK라고 하는데, 주문서 양식을 코드로 채우기 쉽게 만들어 둔 도구 모음입니다.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LLM_API_KEY"],
# 1 요청을 보낼 주소
base_url=os.getenv("LLM_BASE_URL"),
)
response = client.chat.completions.create(
# 2 어떤 모델에게 시킬지
model=os.getenv("LLM_MODEL", "gpt-5.5"),
# 3 대화 목록
messages=[
{"role": "system", "content": "너는 IT 헬프데스크 상담원이다."},
{"role": "user", "content": "와이파이가 안 잡혀요. 티켓 열어 주세요."},
],
)
# 4 모델이 쓴 답
print(response.choices[0].message.content)
# 5 이번 호출에 쓴 토큰 수
print(response.usage.total_tokens)출력은 예시 답입니다. 같은 코드라도 모델과 때에 따라 답 문장과 토큰 수는 달라집니다.
choices가 목록인 이유는 한 번에 답을 여러 개 받을 수도 있어서입니다. 보통은 첫 번째 하나만 씁니다. Python 목록은 0번부터 세서 [0]이 첫 번째입니다.temperature는 답을 얼마나 들쭉날쭉하게 뽑을지 정하는 옵션입니다. 그런데 GPT-5 계열처럼 답하기 전에 먼저 생각하는 추론 모델은 이 옵션을 받지 않는다는 보고가 많습니다. 넣으면 오류가 납니다. 모델을 바꿔 가며 쓸 코드라 기본 예제에서는 뺐습니다.
있습니다. OpenAI는 새 프로젝트에 Responses API를 권하면서도 위에서 쓴 Chat Completions도 계속 지원합니다. 이 글은 메시지 목록을 그대로 주고받는 Chat Completions로 설명합니다. 다른 회사 모델과 로컬 모델 서버도 대부분 이 양식을 받기 때문입니다.
API를 한 번 부르고 나면 모델 쪽에는 아무것도 남지 않습니다. 이 API는 요청 하나를 받아 답 하나를 돌려주면 끝나는 방식이라, 앞 요청의 내용을 다음 요청에 이어 주지 않습니다. 이렇게 앞선 일을 기억하지 않는 성질을 stateless라고 합니다. state는 프로그램이 들고 있는 현재 상황, 곧 상태입니다.
그래서 대화를 이어 가려면 지금까지의 대화 전체를 매번 다시 보내야 합니다. 단톡방에 늦게 들어온 친구에게 앞의 대화를 캡처해 보내 주는 것과 비슷합니다. 다만 친구와 달리 모델은 매번 처음 보는 사람이라서 캡처를 빼먹으면 정말로 아무것도 모릅니다.
묶음 하나가 API 호출 한 번이며 위쪽 칸들은 실어 보낸 메시지, 굵은 테두리 칸은 이번에 새로 더한 메시지입니다. 맨 아래 "답" 말풍선은 돌아온 답이라 보낸 메시지 수에 세지 않습니다. 막대는 보낸 토큰 수이며 숫자는 설명을 위한 예시 값입니다. 스크롤을 내리면 호출이 하나씩 늘어납니다.
대화 전체를 보내면 묶음이 매번 길어지고 토큰도 늘어납니다. 대화가 길어질수록 요금이 오르고 답도 느려집니다. 모델이 한 번에 받을 수 있는 양에도 한도가 있는데, 이 한도를 context window라고 합니다. 한도를 넘으면 요청이 거절되므로, 오래된 대화를 지우거나 줄여서 보내야 합니다.
이번 질문만 보내면 모델은 앞에서 와이파이 이야기를 했다는 것도, 재부팅을 해 봤다는 것도 모릅니다. 대화를 기억하는 쪽은 모델이 아니라 메시지 목록을 들고 있는 내 코드입니다. 코드로는 목록에 한 줄씩 더해 가며 보내면 됩니다.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LLM_API_KEY"],
base_url=os.getenv("LLM_BASE_URL"),
)
MODEL = os.getenv("LLM_MODEL", "gpt-5.5")
SYSTEM = "너는 IT 헬프데스크 상담원이다."
# 1 대화 기록, 곧 프로그램의 상태
messages = [{"role": "system", "content": SYSTEM}]
while True:
question = input("나: ")
if question in ("", "끝"):
break
# 2 사용자의 말을 목록에 더한다
messages.append({"role": "user", "content": question})
# 3 목록 전체를 보낸다
response = client.chat.completions.create(model=MODEL, messages=messages)
answer = response.choices[0].message.content
# 4 모델의 답도 목록에 더한다
messages.append({"role": "assistant", "content": answer})
print("상담원:", answer)
print(f"[보낸 메시지 {len(messages) - 1}개]")출력은 예시 대화입니다. 두 번째 질문을 보낼 때 실린 메시지가 4개로 늘어난 것만 눈여겨보면 됩니다.
두 번 물어보면 첫 호출에는 메시지 2개가, 두 번째 호출에는 4개가 실려 갑니다. 여기까지가 가장 단순한 챗봇입니다. 히어로의 첫 번째 칸과 같습니다. 모델은 답을 글로 쓰고 그 글로 무엇을 할지는 사람이 정합니다.
챗봇에서 다음 단계를 정하는 쪽은 사람입니다. 그 결정을 코드나 모델에게 넘기면 워크플로와 Agent가 됩니다. 라면 끓이기로 비교하면 아래 그림과 같습니다.
색은 히어로와 같은 뜻입니다. 파랑은 사람, 초록은 미리 짠 코드, 주황은 모델이 다음 단계를 정한다는 표시입니다.
Anthropic은 2024년 12월 글 「Building effective agents」에서 둘을 이렇게 나눴습니다.
워크플로는 LLM과 도구가 개발자가 미리 짜 둔 코드 순서대로 움직이는 시스템입니다. Agent는 일을 어떻게 진행할지, 어떤 도구를 쓸지를 LLM이 스스로 정하는 시스템입니다.Anthropic, Building effective agents (2024-12-19)를 풀어 옮김
여기서 도구(tool)는 모델이 쓸 수 있게 열어 준 함수, 곧 이름을 붙여 둔 코드 묶음입니다. 티켓 조회, 티켓 생성 같은 것들입니다. 워크플로도 LLM과 도구를 쓰지만 어떤 순서로 쓸지는 개발자가 코드에 이미 적어 두었습니다. 히어로의 워크플로가 같은 티켓을 또 만든 것도 그 순서에 "먼저 찾아보기"가 없어서입니다.
요리사 비유가 맞지 않는 부분도 있습니다. Agent는 개발자가 열어 준 도구만 쓸 수 있습니다. 또 틀린 판단을 알아채지 못한 채 그대로 밀고 나갈 수도 있습니다.
Agent를 이루는 부품은 네 가지입니다. 판단하는 모델, 개발자가 주는 지시(system 메시지), 모델이 부를 수 있는 도구, 지금까지 일어난 일을 담은 상태(메시지 목록)입니다. 이 넷을 엮는 것이 루프입니다. 같은 순서를 되풀이하는 반복문을 말합니다.
개발자 Simon Willison은 2025년 9월 이것을 한 문장으로 정리했습니다. "LLM Agent는 목표를 이루려고 루프 안에서 도구를 실행한다." 아래로 스크롤하면 히어로의 Agent가 한 일이 한 단계씩 진행됩니다.
왼쪽 아래 칸을 눈여겨보세요. 모델이 쓰는 것은 "티켓을 조회해 달라"는 요청서까지입니다. 이 요청서를 읽고 진짜 티켓 시스템에 접속하는 건 내 코드입니다. 이렇게 모델이 도구 사용을 요청하게 하는 기능을 Tool Calling이라고 합니다.
모델이 어떤 도구를 쓸 수 있는지 아는 것도 코드 덕분입니다. 코드가 바퀴마다 도구 이름과 설명을 적은 목록을 메시지와 함께 보내면 모델은 그 목록 안에서 고릅니다.
루프는 모델이 요청서 대신 사람에게 줄 답을 쓰면 끝납니다. 끝나지 않을 때를 대비해 최대 반복 횟수도 정해 둡니다. 다음 글에서는 Tool Calling 요청서가 실제로 어떻게 생겼고 코드가 그걸 어떻게 실행하는지 알아보겠습니다.
OpenAI의 「A practical guide to building agents」(2025)는 Agent를 "사용자를 대신해 일을 스스로 해내는 시스템"이라고 정의합니다. 구성요소로는 모델·도구·지시 세 가지를 꼽습니다. 상태는 따로 꼽지 않았지만 루프가 끝나는 조건(도구 호출, 정해진 출력, 오류, 최대 반복 횟수)을 같이 설명합니다. LangChain은 "LLM이 프로그램의 흐름을 정하는 시스템"이라고 정의합니다. 표현은 달라도 다음 할 일을 모델이 고른다는 점은 같습니다.
Agent는 상황에 맞춰 길을 바꾸는 대신 대가를 치릅니다. 루프를 한 바퀴 돌 때마다 모델을 다시 부르니 시간과 토큰이 더 듭니다. 모델이 길을 고르니 같은 요청에도 매번 다르게 움직일 수 있습니다. 앞에서 한 잘못된 판단이 뒤로 이어지기도 합니다.
그래서 Anthropic도 같은 글에서 가장 단순한 방법부터 찾으라고 권합니다. 답만 쓰면 되는 일이면 챗봇으로, 순서가 늘 같은 일이면 워크플로로 충분합니다. 상황마다 다음 할 일이 달라지고 그 판단을 모델에게 맡길 만할 때 Agent를 씁니다.
이 시리즈에서는 히어로의 헬프데스크 Agent를 조금씩 실제로 만듭니다. 오늘 만든 것은 그림에서 주황으로 칠한 부분, 모델을 부르고 대화 기록을 들고 있는 데까지입니다.
주황 칸은 이 글에서 만든 부분, 점선 칸은 앞으로 만들 부분입니다. 도구와 데이터는 모두 연습용으로 새로 만듭니다.
정리하면 챗봇, 워크플로, Agent는 같은 LLM을 씁니다. 다른 점은 LLM이 쓴 글을 받은 뒤 다음 단계를 누가 정하느냐입니다. 사람이 정하면 챗봇, 미리 짠 코드가 정하면 워크플로, 모델이 결과를 보고 정하면 Agent입니다.