에이전틱 루프(agentic loop)는 모델이 컨텍스트를 수집하고, 행동하고, 결과를 검증하는 사이클을 스스로 반복하는 구조입니다. 이 수업에서는 루프의 4단계 수명주기와 종료 조건 설계, 그리고 하드코딩된 분기 대신 모델 주도 의사결정을 쓰는 이유를 다룹니다.
에이전틱 루프란 무엇인가
전통적인 LLM 애플리케이션은 한 번의 요청과 한 번의 응답으로 끝납니다. 반면 에이전트(agent)는 목표를 받은 뒤 어떤 도구를 언제 쓸지 모델이 스스로 결정하며, 도구 실행 결과를 다시 입력으로 받아 다음 행동을 정합니다. 이 반복 구조가 에이전틱 루프입니다.
Anthropic 공식 문서는 에이전트를 "루프 안에서 도구를 사용하는 모델(models using tools in a loop)"이라고 정의합니다. 핵심은 제어 흐름의 주체가 코드가 아니라 모델이라는 점입니다. 개발자는 루프의 뼈대와 도구, 종료 조건만 제공하고, 경로 선택은 모델에 맡깁니다.
가장 단순한 형태의 루프는 다음과 같습니다.
import anthropic
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "실패하는 테스트를 찾아 원인을 고쳐줘"}]
while True:
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
tools=tools, # 도구 스키마 목록
messages=messages,
)
# 모델이 도구 호출을 멈추면 루프 종료
if response.stop_reason != "tool_use":
break
# 도구를 실행하고 결과를 대화에 다시 넣는다
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": run_tools(response.content)})
루프를 도는 동안 대화 이력(messages)이 곧 에이전트의 작업 기억이 됩니다. 도구 결과가 누적되면서 모델은 이전 시도에서 배운 것을 다음 결정에 반영합니다. stop_reason 값이 tool_use인지 확인하는 조건문이 루프 전체를 지탱하는 관문이라는 점을 기억해 두십시오. 시험에서는 이 흐름의 각 단계를 순서대로 배열하거나, 어느 단계가 빠졌는지 찾는 문제가 자주 나옵니다.
루프의 4단계 수명주기
에이전틱 루프 한 바퀴는 보통 네 단계로 정리합니다.
- 컨텍스트 수집(gather context) — 모델이 판단에 필요한 정보를 확보합니다. 파일 읽기, 검색, 이전 도구 결과 검토가 여기에 속합니다.
- 행동(take action) — 도구 호출로 실제 변화를 만듭니다. 파일 편집, 명령 실행, API 호출 등입니다.
- 검증(verify work) — 행동의 결과를 확인합니다. 테스트 실행, 린트, 출력 비교가 대표적입니다.
- 반복(repeat) — 목표 달성 여부를 판단해 루프를 계속할지 멈출지 정합니다.
이 구분이 중요한 이유는 각 단계가 서로 다른 실패 모드를 갖기 때문입니다. 컨텍스트 수집이 부실하면 모델이 추측으로 행동하고, 검증이 없으면 잘못된 행동이 그대로 결과가 됩니다. Anthropic의 엔지니어링 블로그는 좋은 에이전트 하네스의 조건으로 "행동마다 피드백을 얻을 수 있는 환경"을 꼽습니다.
# 검증 단계를 도구로 명시적으로 제공하는 예
tools = [
{"name": "edit_file", "description": "파일의 일부를 수정합니다", ...},
{
"name": "run_tests",
"description": "pytest를 실행하고 실패 목록을 반환합니다. "
"edit_file 후에는 반드시 호출해 수정이 올바른지 확인하십시오.",
"input_schema": {"type": "object", "properties": {}},
},
]
도구 설명(description)에 "수정 후 반드시 테스트를 실행하라"처럼 검증 단계를 유도하는 문구를 넣으면 모델이 수명주기를 지키게 만들 수 있습니다. 검증 도구가 아예 없는 에이전트는 자신의 작업이 성공했는지 알 길이 없으므로, 설계 단계에서 검증 수단부터 확보하는 것이 원칙입니다.
모델 주도 의사결정 vs 하드코딩 분기
에이전트를 처음 설계할 때 가장 흔한 고민은 "이 판단을 코드로 짤 것인가, 모델에 맡길 것인가"입니다. Anthropic의 「Building effective agents」는 명확한 기준을 제시합니다. 경로가 미리 알려져 있고 단계가 고정적이면 워크플로(workflow)로 하드코딩하고, 경로를 예측할 수 없으면 에이전트에 맡기라는 것입니다.
하드코딩 분기의 장점은 예측 가능성과 낮은 비용입니다. 단점은 새로운 상황에 대응하지 못한다는 것입니다. 반대로 모델 주도 의사결정은 유연하지만 토큰 비용과 지연 시간이 늘고, 오류가 누적될 수 있습니다.
// 안티패턴: 모델이 결정해야 할 것을 코드가 결정
if (userQuery.includes("환불")) {
callRefundFlow(); // 키워드 매칭은 "환불 안 해줘서 좋아요" 같은
} else { // 문장을 오분류한다
callGeneralFlow();
}
// 개선: 분류 자체를 모델에 맡기고, 실행은 코드가 통제
const route = await classify(userQuery); // 모델이 의도 분류
switch (route) {
case "refund": return refundWorkflow(userQuery);
case "general": return generalWorkflow(userQuery);
}
실무에서 자주 쓰는 절충안은 라우팅은 모델, 각 경로 내부는 워크플로로 두는 구조입니다. 판단이 필요한 지점에만 모델을 쓰고, 결정된 뒤의 실행은 결정적(deterministic) 코드로 처리하면 비용과 유연성의 균형을 잡을 수 있습니다. 시험에서는 "항상 에이전트가 워크플로보다 낫다"는 선택지가 오답으로 자주 등장합니다. 단순하고 고정된 작업에 에이전트를 쓰는 것은 과잉 설계입니다.
종료 조건과 가드레일
루프는 반드시 멈출 수 있어야 합니다. 종료 조건 설계가 부실한 에이전트는 같은 실패를 무한히 반복하거나, 비용을 통제 불능으로 만들 수 있습니다. 실무에서 쓰는 대표적인 종료 조건은 다음과 같습니다.
- 자연 종료 — 모델이 더 이상 도구를 호출하지 않을 때(
stop_reason이tool_use가 아닐 때). 가장 바람직한 종료입니다. - 최대 반복 횟수(max iterations) — 루프 횟수 상한. 무한 루프를 막는 최후의 방어선입니다.
- 예산 상한 — 누적 토큰이나 비용이 한도를 넘으면 중단합니다.
- 명시적 완료 신호 —
task_complete같은 완료 보고 도구를 제공해 모델이 스스로 완료를 선언하게 합니다.
MAX_TURNS = 25
for turn in range(MAX_TURNS):
response = client.messages.create(...)
if response.stop_reason != "tool_use":
break
...
else:
# for-else: 상한에 도달했는데도 끝나지 않은 경우
raise AgentBudgetExceeded("에이전트가 25턴 안에 완료하지 못했습니다")
상한에 도달했을 때의 처리도 설계의 일부입니다. 조용히 중단하면 사용자는 절반만 끝난 결과를 완성본으로 오해할 수 있습니다. 중단 사유를 명시적으로 보고하고, 지금까지의 진행 상황을 요약해 넘기는 것이 올바른 패턴입니다.
가드레일은 종료 조건만이 아닙니다. 도구 권한 제한(읽기 전용 도구만 허용), 위험한 행동 전 사용자 확인, 실행 환경 샌드박스화도 루프를 안전하게 만드는 장치입니다. 시험 함정: "최대 반복 횟수만 있으면 충분하다"는 주장은 틀렸습니다. 횟수 상한은 폭주를 막을 뿐, 잘못된 방향으로 진행되는 것 자체는 검증 단계와 권한 제한이 막아야 합니다.
시험 함정
- 에이전트와 워크플로를 혼동하게 만드는 문제 — 경로가 고정되어 있으면 워크플로, 모델이 경로를 정하면 에이전트입니다.
- stop_reason 값 해석 — tool_use는 루프를 계속한다는 뜻이고, end_turn이 자연 종료입니다. 반대로 제시하는 선택지에 주의하십시오.
- 도구 결과를 assistant 메시지로 넣는 오답 — 도구 실행 결과는 user 역할의 tool_result 블록으로 추가해야 합니다.
- "에이전트는 항상 워크플로보다 우월하다"는 선택지 — 단순·고정 작업에는 워크플로가 더 저렴하고 예측 가능합니다.
- 검증 단계를 생략해도 된다는 선택지 — 검증 수단이 없는 에이전트는 자기 작업의 성패를 알 수 없습니다.
- 무한 루프 방지책으로 프롬프트에 '빨리 끝내라'고 쓰는 오답 — 프롬프트 지시는 상한이 아니며, 코드 수준의 반복·예산 상한이 필요합니다.
실습 시나리오
실제 시험과 같은 형식의 시나리오 문제입니다.
고객 문의를 처리하는 에이전트가 같은 검색 도구를 같은 인자로 20회 연속 호출하며 멈추지 않습니다. 가장 먼저 적용해야 할 조치는 무엇입니까?
빌드 연습 · 최소 에이전틱 루프 구현하기
약 45분1.도구 두 개 정의
read_file과 run_tests 두 도구의 JSON 스키마를 작성합니다.
기대 결과 · 각 도구에 name, description, input_schema가 있고, run_tests 설명에 '수정 후 반드시 호출'이 명시되어 있습니다.
2.기본 루프 작성
stop_reason이 tool_use인 동안 도구를 실행하고 결과를 대화에 추가하는 while 루프를 구현합니다.
기대 결과 · 도구 결과가 user 역할의 tool_result 블록으로 추가되고, end_turn에서 루프가 종료됩니다.
3.반복 상한 추가
최대 15턴 상한을 두고, 초과 시 진행 상황 요약과 함께 예외를 발생시킵니다.
기대 결과 · 상한 도달 시 조용히 멈추지 않고 중단 사유가 보고됩니다.
4.종료 조건 테스트
일부러 실패하는 테스트 파일을 만들어 에이전트가 수정-검증-재수정 사이클을 도는지 관찰합니다.
기대 결과 · 에이전트가 run_tests 결과를 보고 수정 방향을 바꾸는 턴이 기록에 남습니다.
5.비용 로깅
턴마다 usage의 input/output 토큰을 누적 기록합니다.
기대 결과 · 실행 종료 시 총 토큰 사용량이 출력됩니다.