LLM에서 에이전트로

평범한 한 번 호출과 는 무엇이 다른가. 답은 단순하다 — 루프가 있다. 그리고 그 루프 안에서 모델은 로 세상과 닿는다.


단발 호출의 그림

은 본질적으로 텍스트를 받아 텍스트를 뱉는 함수다. 한 번 부르면 끝이다.

prompt → model → text 이게 전부다. 에 들어간 토큰을 보고 다음 토큰을 예측한다. 가 바뀌지 않으면 다음 호출도 거의 같은 답을 낸다. 외부 상태를 모르고, 결과를 자기 행동으로 이어 붙이지 못한다.

단발 호출은 질문 한 번에 답 한 번. 그 이상도 이하도 아니다.


가장 짧은 LLM 호출

먼저 베이스라인부터 본다. Anthropic 로 한 줄 받고 끝내는 호출이다. 는 한 사용자 메시지가 전부다.

# Verified against: https://platform.claude.com/docs/en/api/messages
# Verified at: 2026-06-02
from anthropic import Anthropic

client = Anthropic()

reply = client.messages.create(
  model="claude-sonnet-4-6",
  max_tokens=256,
  messages=[
      {"role": "user", "content": "서울 오늘 기온은?"}
  ],
)

print(reply.content[0].text)

가 아니라 단순 응답이다. 모델은 “실시간 정보가 없다”고 답할 것이다.


한 단계 진화: 도구를 손에 쥐어주기

외부 함수를 노출하는 기능이다. 모델은 함수를 직접 실행하지 못한다. 대신 “이 함수를 이런 인자로 부르고 싶다”는 의도를 JSON으로 뱉는다. 실행은 우리 코드가 한다.

이게 가능해진 순간 LLM은 챗봇에서 한 발 빠져나온다. 날씨 API, DB, 검색 — 무엇이든 도구 한 줄이면 모델의 입력 영역이 넓어진다. 한 동작의 첫 신호다. 로 가는 1단계라 봐도 좋다.


도구를 선언한 호출

도구를 한 번 선언하고 모델이 그걸 부르도록 유도해 본다. 는 같지만 이번에는 이 등장한다. 응답 형식이 평범한 텍스트가 아닌 구조화된 출력이라는 점에 주목한다. 한 출력이다.

# Verified against: https://platform.claude.com/docs/en/docs/build-with-claude/tool-use
# Verified at: 2026-06-02
from anthropic import Anthropic

client = Anthropic()

tools = [{
  "name": "get_weather",
  "description": "도시 이름을 받아 현재 기온을 반환",
  "input_schema": {
      "type": "object",
      "properties": {
          "city": {"type": "string"},
      },
      "required": ["city"],
  },
}]

reply = client.messages.create(
  model="claude-sonnet-4-6",
  max_tokens=512,
  tools=tools,
  messages=[{"role": "user", "content": "서울 오늘 기온은?"}],
)

print(reply.stop_reason)  # "tool_use"
print(reply.content)      # [{"type": "tool_use", "name": "get_weather", ...}]

stop_reasontool_use로 떨어지면, 그건 모델이 우리에게 도와달라는 신호다.


그래도 아직 에이전트는 아니다

을 한 번 부르고 끝나면 그건 그저 “도구가 붙은 ”이다. 모델이 결과를 받고 그걸로 다음 행동을 결정하지 못하면 이 없다.

진짜 차이는 결과를 다시 컨텍스트에 넣고, 모델을 한 번 더 부르고, 또 도구를 부를지 끝낼지 자기가 정하는 데서 나온다. 이 반복 구조가 다. 진짜 의 출발선이다.


ReAct 패턴

은 Yao 외 연구진이 2022년에 정리한 단순한 아이디어다. 한 사이클 안에서 모델이 두 가지를 번갈아 한다.

도구를 부르면 결과가 돌아온다. 그 결과를 Observation으로 받아 적고, 다시 Reason으로 들어간다. 사이클이 도는 동안 는 점점 두꺼워지고, 모델은 매 사이클마다 남은 일이 뭔지 자기에게 묻는다. 결과적으로 가 완성된다.

다이어그램 로딩…

ReAct 루프를 코드로

루프 자체는 30줄짜리다. 핵심은 whilestop 조건 두 줄이다. 이 코드에서 을 번갈아 가며 답에 수렴한다.

# Verified against: https://platform.claude.com/docs/en/docs/build-with-claude/tool-use
# Verified at: 2026-06-02
from anthropic import Anthropic

client = Anthropic()
TOOLS = [{
  "name": "get_weather",
  "description": "도시 기온을 반환",
  "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
}]

def run_tool(name, args):
  if name == "get_weather":
      return f"{args['city']} 기온 24도"
  return "unknown tool"

def agent(question: str) -> str:
  messages = [{"role": "user", "content": question}]
  while True:
      resp = client.messages.create(
          model="claude-sonnet-4-6", max_tokens=512, tools=TOOLS, messages=messages,
      )
      if resp.stop_reason == "end_turn":
          return resp.content[0].text
      messages.append({"role": "assistant", "content": resp.content})
      tool_use = next(b for b in resp.content if b.type == "tool_use")
      result = run_tool(tool_use.name, tool_use.input)
      messages.append({"role": "user", "content": [
          {"type": "tool_result", "tool_use_id": tool_use.id, "content": result}
      ]})

print(agent("서울 오늘 기온?"))

한 사이클 들여다보기

위 코드 안에서 일어나는 일을 한 사이클만 풀어 본다. 가 어떻게 도는지 그림이 잡힐 거다.

  1. Reason — 모델이 “도시 이름이 있으니 get_weather 부르자”고 결정한다. 이게 단계다.
  2. Acttool_use 블록을 뱉는다. 우리 코드가 run_tool로 실제 함수를 실행한다.
  3. Observe — 실행 결과(“서울 기온 24도”)를 tool_result 메시지로 다시 컨텍스트에 넣는다. 단계다.
  4. Reason 다시 — 모델이 결과를 보고 “이 정도면 답 가능”이라 판단하고 자연어 응답을 만든다. stop_reasonend_turn이 된다.

”이건 에이전트인가?” 판별 기준

한 시스템을 보고 인가 아닌가가 헷갈리면 세 줄로 체크한다.

  1. 루프가 있는가을 1회 호출로 끝내지 않고, 결과를 다시 입력으로 넣는가.
  2. 도구가 있는가 — 외부 함수·API·검색을 로 부를 길이 열려 있는가.
  3. 종료 조건을 자기가 정하는가 — “이제 됐다”를 사람이 아닌 모델이 판단하는가. 의 핵심이다.

세 개 다 ✅면 한 진짜 에이전트다. 두 개면 에이전틱한 워크플로. 하나뿐이면 그냥 챗봇에 도구가 붙은 것뿐이다.


단발 vs 에이전트, 입출력 한 줄 비교

단발
입력 하나 + 사용자 + 누적
호출 횟수1회N회 (N은 동적)
외부 상태모름로 닿음
종료 결정사람·앱모델 자신

핵심은 호출 횟수가 동적이라는 점이다. 사용자 질문 한 줄에 모델이 도구를 1번 부를지 7번 부를지는 런타임이 정한다.


에이전트가 비용이 비싼 이유

장점만 있는 건 아니다. 가 돈다는 건 여러 번 부른다는 뜻이다. 는 사이클마다 두꺼워지고, 토큰이 누적되면 지연도 비용도 함께 오른다.

가 많을수록 모델이 고를 거리도 많아진다. 옳은 도구를 항상 고른다는 보장도 없다. 한 번 잘못 부르면 그 잘못된 이 다음 추론을 오염시킨다. 이 두 가지 — 비용 누적오류 누적 — 이 다음 장들에서 계속 따라붙는다.


다음 장으로

가 있다는 건 알았다. 그렇다면 루프 안에서 가 어떤 모듈로 쪼개지는지가 다음 질문이다.

다음 장에서는 에이전트의 내부 구성 — , , , 인터페이스 — 를 해부한다.