콘텐츠로 이동

02-구조

02. 구조 — Claude Code 루프의 종료 조건들

섹션 제목: “02. 구조 — Claude Code 루프의 종료 조건들”

Claude Code의 메인 루프는 대략 다음 의사코드와 같다.

while True:
resp = claude.messages.create(...)
handle_text(resp)
if resp.stop_reason == "end_turn":
break # ← 1차 종료 (모델 결정)
if resp.stop_reason == "tool_use":
for tool_use in resp.tool_uses:
result = dispatch(tool_use)
append_tool_result(result)
if step_count > MAX_STEPS: # ← 2차 종료 (안전 상한)
warn_user(); break
continue
if resp.stop_reason in ("max_tokens", "stop_sequence", "refusal"):
break # ← 3차 종료 (비정상)

정상 종료. 모델이 “이번 턴에 할 일이 끝났다”고 선언. 사용자에게 입력 차례를 돌려준다. 본질적으로 모델의 자기 판단이며, 시스템 프롬프트의 응답 정책(간결함, “묻기 전에 끝내라” 등)에 영향을 받는다.

무한 루프 방지. 도구 호출 횟수, 누적 토큰, 벽시계 시간 중 하나라도 초과하면 강제 종료. 사용자에게 “계속할까요?” 류 안내가 뜬다. 이 상한이 너무 낮으면 큰 작업이 중간에 잘리고, 너무 높으면 오류 시 토큰만 태운다.

  • max_tokens — 단일 응답 토큰 한도 도달. 보통 더 길게 출력하라는 신호 또는 출력 형식 오용.
  • refusal — 안전 정책에 의한 거절.
  • 도구 에러 폭주 — 같은 도구가 같은 인자로 N번 실패하면 하네스가 개입할 수 있다(구현 의존).

무한 루프와 조기 종료의 진짜 원인

섹션 제목: “무한 루프와 조기 종료의 진짜 원인”
증상흔한 진짜 원인
무한 루프도구 결과가 모델에게 “성공/실패”를 모호하게 전달 → 모델이 같은 도구를 계속 재시도 (SWE-agent의 “empty output → success message” 발견과 같은 맥락)
같은 파일을 N번 읽음컨텍스트가 차서 모델이 이전 Read 결과를 잊음 → /compact 또는 새 세션
너무 일찍 end_turn시스템 프롬프트가 “간결함”을 과도하게 강조 / 사용자 요청이 모호 → 명시적 “끝까지 해” 지시 또는 TodoWrite 활용
max_tokens 빈발응답에 거대 코드 블록을 통째로 출력하려 함 → Edit 도구로 우회

Letta 팀은 이를 일반화해 “loop design = behavior design”이라고 부른다. 모델이 아니라 루프를 디자인하는 것이 에이전트 엔지니어링이다.