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_reason이 tool_use로 떨어지면, 그건 모델이 우리에게 도와달라는 신호다.
그래도 아직 에이전트는 아니다
을 한 번 부르고 끝나면 그건 그저 “도구가 붙은 ”이다. 모델이 결과를 받고 그걸로 다음 행동을 결정하지 못하면 이 없다.
진짜 차이는 결과를 다시 컨텍스트에 넣고, 모델을 한 번 더 부르고, 또 도구를 부를지 끝낼지 자기가 정하는 데서 나온다. 이 반복 구조가 다. 진짜 의 출발선이다.
ReAct 패턴
은 Yao 외 연구진이 2022년에 정리한 단순한 아이디어다. 한 사이클 안에서 모델이 두 가지를 번갈아 한다.
- Reason: 다음에 뭘 할지 자기 생각을 쓴다 ().
- Act: 도구를 부르거나, 끝내겠다고 선언한다.
도구를 부르면 결과가 돌아온다. 그 결과를 Observation으로 받아 적고, 다시 Reason으로 들어간다. 사이클이 도는 동안 는 점점 두꺼워지고, 모델은 매 사이클마다 남은 일이 뭔지 자기에게 묻는다. 결과적으로 가 완성된다.
ReAct 루프를 코드로
루프 자체는 30줄짜리다. 핵심은 while과 stop 조건 두 줄이다. 이 코드에서 는 과 을 번갈아 가며 답에 수렴한다.
# 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("서울 오늘 기온?"))한 사이클 들여다보기
위 코드 안에서 일어나는 일을 한 사이클만 풀어 본다. 가 어떻게 도는지 그림이 잡힐 거다.
- Reason — 모델이 “도시 이름이 있으니
get_weather부르자”고 결정한다. 이게 단계다. - Act —
tool_use블록을 뱉는다. 우리 코드가run_tool로 실제 함수를 실행한다. - Observe — 실행 결과(“서울 기온 24도”)를
tool_result메시지로 다시 컨텍스트에 넣는다. 단계다. - Reason 다시 — 모델이 결과를 보고 “이 정도면 답 가능”이라 판단하고 자연어 응답을 만든다.
stop_reason이end_turn이 된다.
”이건 에이전트인가?” 판별 기준
한 시스템을 보고 인가 아닌가가 헷갈리면 세 줄로 체크한다.
- 루프가 있는가 — 을 1회 호출로 끝내지 않고, 결과를 다시 입력으로 넣는가.
- 도구가 있는가 — 외부 함수·API·검색을 로 부를 길이 열려 있는가.
- 종료 조건을 자기가 정하는가 — “이제 됐다”를 사람이 아닌 모델이 판단하는가. 의 핵심이다.
세 개 다 ✅면 한 진짜 에이전트다. 두 개면 에이전틱한 워크플로. 하나뿐이면 그냥 챗봇에 도구가 붙은 것뿐이다.
단발 vs 에이전트, 입출력 한 줄 비교
| 축 | 단발 | |
|---|---|---|
| 입력 | 하나 | + 사용자 + 누적 |
| 호출 횟수 | 1회 | N회 (N은 동적) |
| 외부 상태 | 모름 | 로 닿음 |
| 종료 결정 | 사람·앱 | 모델 자신 |
핵심은 호출 횟수가 동적이라는 점이다. 사용자 질문 한 줄에 모델이 도구를 1번 부를지 7번 부를지는 런타임이 정한다.
에이전트가 비용이 비싼 이유
장점만 있는 건 아니다. 가 돈다는 건 을 여러 번 부른다는 뜻이다. 는 사이클마다 두꺼워지고, 토큰이 누적되면 지연도 비용도 함께 오른다.
가 많을수록 모델이 고를 거리도 많아진다. 옳은 도구를 항상 고른다는 보장도 없다. 한 번 잘못 부르면 그 잘못된 이 다음 추론을 오염시킨다. 이 두 가지 — 비용 누적과 오류 누적 — 이 다음 장들에서 계속 따라붙는다.
다음 장으로
와 가 있다는 건 알았다. 그렇다면 루프 안에서 가 어떤 모듈로 쪼개지는지가 다음 질문이다.
다음 장에서는 에이전트의 내부 구성 — , , , 인터페이스 — 를 해부한다.