루프 없이는 agent도 없다
Claude Code의 모든 비밀은 한 줄로 요약됩니다: while stop_reason == "tool_use"
agent는 실제로 무슨 일을 하는가?
터미널에서 Claude Code를 실행해 "모든 TODO 주석을 목록으로 정리해줘"라고 하면, 스스로 grep을 실행하고, 몇 개 파일을 cat으로 읽고, 마지막에 Markdown을 출력하는 과정을 볼 수 있습니다. 모델 자체는 코드를 실행하지 않습니다—실행을 요청할 뿐입니다. 이 모델이 "손발이 있는 것처럼" 보이게 만드는 건 바깥을 감싸는 30줄 남짓의 접착 코드입니다.
이번 강의는 그 접착 코드를 뜯어봅니다. 이것을 agent loop(에이전트 루프)라고 하며, 구조는 다음과 같습니다:
while response.stop_reason == "tool_use": response = LLM(messages, tools) # 1. ask the model execute_tools(response.tool_calls) # 2. run what it asked for messages.append(tool_results) # 3. feed results back
이게 전부입니다. production Claude Code는 이 위에 권한, hook, 서브 agent, worktree 격리, 메모리 압축을 쌓아올렸지만—핵심은 이 네 줄입니다.
핵심 직관: 모델은 매번 응답할 때 "도구를 호출하고 싶다"거나 "할 말을 다 했다" 중 하나입니다. 전자 상태에 있는 한 루프는 계속되고, 후자가 되면 루프에서 빠져나옵니다. 판단 기준은 응답의 stop_reason 필드입니다.
messages[]가 한 단계씩 쌓이는 것 보기
아래 루프가 시뮬레이션하는 작업은 "현재 디렉토리에 어떤 파일이 있나요? 그리고 package.json을 읽어주세요"입니다. Step을 클릭하면 루프 내 동작이 하나씩 진행됩니다. 왼쪽은 사람이 보는 대화 말풍선, 오른쪽은 실제로 모델에게 전달되는 messages[] 배열—어떻게 늘어나는지 주목하세요.
tool 결과는 반드시 메시지 히스토리에 다시 넣어야 한다
초보자가 가장 자주 실수하는 부분은 "도구 실행"을 부수 효과로 취급하는 것입니다—실행하면 끝이라고 생각하는 것이죠. 하지만 모델은 다음 추론에서 messages[]만 볼 수 있고, 거기 없는 건 일어난 적 없는 것과 같습니다. append를 한 번 빠뜨리면 루프 전체가 망가집니다.
실수의 형태는 두 가지입니다:
- append를 잊는 경우: 모델은 다음 차례에 결과를 볼 수 없어, 같은 도구를 다시 요청합니다—그래서 무한 루프가 됩니다.
- append는 했지만
tool_use_id를 누락한 경우: Anthropic API가 바로tool_result must have tool_use_id에러를 반환하고, API 호출 단계에서 루프가 중단됩니다.
같은 작업: "프로젝트에 Python 파일이 몇 개 있나요". 두 버전을 나란히 실행해 결과의 차이를 확인하세요.
stop_reason 읽기
모델은 매번 응답할 때 stop_reason을 함께 반환합니다. 루프를 계속할지 말지는 이 필드를 보면 됩니다:
tool_use— 모델이 도구를 호출하려 합니다, 루프 계속.end_turn— 모델이 할 말을 다 했습니다, 루프 종료.max_tokens— 토큰 한도에 달해 잘렸습니다, 루프 종료 (일반적으로 예외로 처리하고 사용자에게 출력이 불완전함을 알립니다).stop_sequence— 커스텀 정지 문자열을 만났습니다, 루프 종료.
"tool_use가 아니면 계속"으로 잘못 작성하거나, max_tokens를 정상적인 end로 무시하는 것은 흔한 버그입니다.
직접 실행해보기
로컬에 shareAI-lab/learn-claude-code를 클론하세요:
git clone https://github.com/shareAI-lab/learn-claude-code cd learn-claude-code pip install -r requirements.txt cp .env.example .env # fill in ANTHROPIC_API_KEY python agents/s01_agent_loop.py
그런 다음 실제 작업을 시켜보세요: 이 저장소에 .py 파일이 몇 개 있나요. ls -R을 한 번 보내고, 출력을 보고, find . -name "*.py" | wc -l을 보내고, 답을 알려주는 과정을 볼 수 있습니다. 전체 흐름이 바로 이 루프가 돌아가는 것입니다.