Agent Loop — 에이전트의 실행 루프 해부
while 루프 하나가 에이전트의 전부다
에이전트 프레임워크가 수십 개 있지만, 전부 결국 같은 구조다.
기본 루프
messages = [system_prompt]
while True:
response = llm.call(messages, tools=available_tools)
if response.stop_reason == 'tool_use':
# 도구 호출이 있으면 실행하고 결과를 추가
for tool_call in response.tool_calls:
result = execute(tool_call)
messages.append(tool_result(result))
else:
# 도구 호출이 없으면 → 최종 응답
break
이게 전부다. LangChain이든 CrewAI든 AutoGen이든, 코어는 이 루프.
루프의 구성 요소
메시지 히스토리: 모든 대화와 도구 결과가 쌓이는 곳. 에이전트의 "기억"이다. 컨텍스트 윈도우 제한이 있어서, 히스토리가 길어지면 요약하거나 잘라야 한다.
도구 정의: LLM에게 "넌 이런 도구를 쓸 수 있어"라고 알려주는 스키마. 도구 이름, 설명, 파라미터를 JSON Schema로 정의. 도구 설명이 좋을수록 LLM이 적절한 도구를 고른다.
종료 조건: 루프가 언제 끝나는가. 보통 LLM이 도구를 안 부르고 텍스트만 반환하면 끝. 추가로 최대 반복 횟수, 타임아웃, 비용 한도 같은 안전장치가 필요하다.
실전 고려사항
에러 핸들링: 도구 실행이 실패하면? 에러 메시지를 LLM에 돌려주면 LLM이 다른 접근을 시도한다. 그냥 크래시시키면 안 된다.
컨텍스트 관리: 100번 도구를 호출하면 메시지 히스토리가 거대해진다. 토큰 한도를 넘기기 전에 오래된 메시지를 요약하거나 제거하는 전략이 필요.
병렬 도구 호출: Claude는 한 번의 응답에서 여러 도구를 동시에 호출할 수 있다. 독립적인 도구 호출을 병렬로 실행하면 속도가 올라간다.
관찰 가능성: 루프가 돌 때마다 뭘 하는지 로깅. 디버깅할 때 없으면 지옥이다.
동작 흐름
시스템 프롬프트 + 사용자 메시지로 초기 메시지 히스토리 구성
LLM 호출 → 응답에 tool_use가 있으면 도구 실행
도구 결과를 메시지 히스토리에 추가 → LLM 재호출
tool_use가 없으면 루프 종료 → 최종 응답 반환