03. 가장 단순한 에이전트: 메모리도 도구도 없이

🎯 이 장의 목표
  • LLM API를 직접 호출해 가장 작은 "에이전트"를 만든다
  • 시스템 프롬프트가 에이전트의 성격과 역할을 어떻게 정하는지 이해한다
  • 토큰과 토큰화(tokenization)의 기본 개념을 잡는다
  • "왜 이 단순한 버전은 대화를 기억하지 못하는가"를 직접 확인한다

비유로 시작하기: 기억상실증 상담원

전화 상담원이 한 명 있는데, 통화가 끝날 때마다 방금 무슨 얘기를 했는지 완전히 잊어버린다고 합시다. 매 통화가 서로 무관한 첫 대화입니다. 이번 장에서 만드는 에이전트가 바로 이렇습니다. 메모리가 없고 도구도 없는, 가장 순수한 형태입니다.

왜 이렇게 단순한 것부터 시작할까요? 복잡한 프레임워크를 배우기 전에, "LLM 호출 한 번이 어떻게 작동하는지"를 손으로 만져봐야 나중에 추상화된 도구들의 동작이 눈에 들어오기 때문입니다.

핵심 구조: 시스템 프롬프트 + 사용자 입력

LLM API 호출은 보통 메시지 목록을 보냅니다. 가장 중요한 두 가지 역할(role)은 다음과 같습니다. system은 에이전트의 정체성과 규칙을 정하는 지시문이고, user는 사용자가 던지는 실제 질문입니다.

PYTHON
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI()  # OPENAI_API_KEY 환경변수를 자동 사용

def run_agent(user_input: str) -> str:
    response = client.chat.completions.create(
        model="gpt-4o-mini",  # 빠르고 저렴 — 학습용으로 적합
        messages=[
            {"role": "system", "content": "너는 간결하고 정확한 기술 비서다."},
            {"role": "user", "content": user_input},
        ],
    )
    return response.choices[0].message.content

print(run_agent("파이썬 리스트와 튜플의 차이를 한 문장으로 설명해줘."))

이게 전부입니다. system으로 성격을 정하고, user 입력을 받아 응답을 돌려줍니다.

📌 핵심: 시스템 프롬프트는 에이전트의 "직무기술서"입니다. 같은 모델이라도 시스템 프롬프트를 바꾸면 완전히 다른 에이전트가 됩니다. "너는 시니컬한 비평가다" vs "너는 친절한 초등 교사다"는 출력 톤을 근본적으로 바꿉니다.

동작 흐름

sequenceDiagram
    participant U as 사용자
    participant A as 에이전트 함수
    participant L as LLM API

    U->>A: user_input
    A->>L: system + user 메시지 전송
    L-->>A: 응답 텍스트
    A-->>U: 최종 답변
    Note over A,L: 호출이 끝나면 아무것도 남지 않는다<br/>(메모리 없음)

모델 바꿔보기

model 인자만 바꾸면 다른 모델로 실험할 수 있습니다. 모델마다 속도·비용·품질이 다릅니다.

모델 (예시)특징적합한 용도
gpt-4o-mini빠르고 저렴학습, 분류, 단순 작업
gpt-4o균형일반적인 추론 작업
최신 추론 모델고품질·고비용복잡한 다단계 추론

⚠️ 흔한 실수: 모델명을 추측해서 쓰는 것. 모델명과 가격은 자주 바뀝니다. 실제 사용 가능한 모델명은 항상 OpenAI 공식 모델 문서에서 확인하세요. 존재하지 않는 모델명을 넣으면 에러가 납니다.

💡 실습 아이디어(강의의 Practice Opportunity 대응): 시스템 프롬프트를 "너는 위트 있는 트윗 작성가다. 280자 이내로 답한다"로 바꾸고, 주제를 입력하면 트윗을 생성하는 에이전트로 변형해 보세요. 코드는 그대로, 시스템 프롬프트만 바꾸면 됩니다.

토큰과 토큰화 이해하기

LLM은 글자를 그대로 읽지 않고 토큰(token) 단위로 처리합니다. 토큰은 대략 단어 조각입니다. 영어는 보통 1토큰 ≈ 4글자, 한국어는 글자당 토큰 수가 더 많은 편입니다.

토큰이 중요한 이유는 두 가지입니다. 첫째, 비용이 토큰 수에 비례합니다(입력 토큰 + 출력 토큰). 둘째, 모델마다 컨텍스트 한도(한 번에 다룰 수 있는 최대 토큰)가 있습니다. 긴 대화나 큰 문서를 넣으면 한도를 넘겨 잘리거나 에러가 납니다.

PYTHON
# 토큰 사용량 확인 (응답에 포함됨)
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "안녕"}],
)
print(response.usage)
# prompt_tokens, completion_tokens, total_tokens 확인 가능

📌 핵심: 토큰은 곧 돈이자 한계입니다. 9부에서 비용·토큰 관리를 깊게 다루지만, 지금부터 usage를 들여다보는 습관을 들이면 좋습니다.

메모리 없음을 직접 확인하기

이 에이전트가 정말 기억을 못하는지 실험해 봅시다.

PYTHON
print(run_agent("내 이름은 민수야."))
print(run_agent("내 이름이 뭐라고 했지?"))
# 두 번째 호출은 첫 호출을 전혀 모른다 → "알 수 없다"는 식의 답

두 번째 호출은 첫 번째 호출의 내용을 전혀 모릅니다. 각 호출이 독립적이기 때문입니다. 함수 안에서 매번 새 메시지 목록을 만들고, 호출이 끝나면 그 목록은 사라집니다. 이 한계가 바로 다음 장에서 메모리를 도입하는 이유입니다.

이 장에서 배운 것

  • 가장 단순한 에이전트는 system + user 메시지를 LLM에 보내고 응답을 받는 함수다.
  • 시스템 프롬프트가 에이전트의 정체성과 규칙을 정한다. 코드를 안 바꿔도 프롬프트만으로 다른 에이전트를 만들 수 있다.
  • LLM은 토큰 단위로 처리하며, 토큰 수가 비용과 컨텍스트 한도를 좌우한다.
  • 이 단순 버전은 호출 간에 아무것도 기억하지 못한다 — 메모리가 없기 때문이다.

✍️ 확인 문제

  1. 코드 한 줄도 바꾸지 않고 에이전트를 "공손한 고객 응대원"에서 "냉정한 코드 리뷰어"로 바꾸려면 무엇을 수정해야 하는가?
  2. response.usage에서 확인할 수 있는 세 가지 값은 무엇이며, 각각 왜 중요한가?
  3. 이번 장의 에이전트에게 이름을 알려준 뒤 다음 호출에서 이름을 물으면 답하지 못하는 근본 원인은 무엇인가?
이전 장: 02. 개발 환경 준비
다음 장: 04. 메모리 — 기억상실증 에이전트에게 기억을 달아줍니다.