TL;DR
에이전트가 동일한 툴 호출을 반복해 무한루프에 빠지는 근본 원인은 호출 기록 부재, 툴 반환의 판독 실패, 목표 달성 검증 부재의 세 가지로 분류되며 단순히 턴 제한이나 recursion_limit을 늘리는 방식은 실패를 지연시킬 뿐이다. 제안된 해결책은 툴 이름과 정렬된 JSON 인자를 결합해 SHA-256 해시로 호출 지문을 생성하고 한 실행 내 해시 집합을 유지해 이미 실행된 호출을 차단하는 방식으로, sort_keys=True와 변동 필드 제거가 구현상 필수적이다. 이 방식은 많은 루프를 잡아내지만 툴 반환이 동일한 실패를 반복하면 호출 측 지문화만으로는 부족해 응답 지문화와 명시적 종료 조건을 병행해야 완전한 해결이 된다. 운영적으로는 cap 증설보다 지문화와 종료 조건 도입이 비용과 지연을 줄이는 데 더 효과적이라는 점이 경험적으로 확인되었다.
커뮤니티 반응
커뮤니티 반응은 실무적이고 경험 기반인 사례 공유와 함께 대체로 수용적이었다. 많은 참가자가 해시 기반 중복 검출이 실제로 많은 루프를 잡아냈다는 경험을 나누었고 일부는 응답 내용에 대한 지문화나 상태 검증을 추가로 도입해야 했다고 보고했다. 반면 변동 필드 처리와 툴에서 반환하는 자유문(prose)을 기계적으로 판독하는 문제는 여전히 논쟁거리로 남아 있었다.
주요 논점
호출 지문화는 동일 입력에 대한 중복 실행을 빠르게 차단해 불필요한 반복을 줄일 수 있다는 점에서 실용적이며 구현이 간단하고 비용 대비 효과가 크다.
호출 지문화는 상당 부분 문제를 줄이지만 툴 반환의 반복적 실패나 외부 상태 변화에는 한계가 있으므로 추가적인 응답 지문화나 목표 검증 로직과 결합해야 완전한 해결책이 된다.
일부 환경에서는 변동 필드와 비결정적 툴 출력 때문에 지문화 유지비용과 전처리 복잡도가 높아져 도입 비용이 실익을 넘지 않을 수 있다.
합의점 vs 논쟁점
합의점
- 동일한 호출을 반복하는 문제는 단순히 턴 제한을 늘리는 것으로 해결되지 않으며 호출 기록을 남기거나 중복 검사를 해야 근본적으로 완화된다.
- 정렬된 직렬화(sort_keys=True)와 변동 필드 제거는 해시 기반 중복 탐지의 필수 전처리이며 이를 적용하지 않으면 같은 호출이 다른 해시로 처리되어 탐지가 실패한다.
- 툴 반환값의 명확한 성공/실패 표기와 목표 달성 검증 로직은 호출 지문화와 함께 있어야 실제 작업 완료 여부를 신뢰할 수 있다.
논쟁점
- 툴 응답 자체를 지문화해서 결과 기반으로 루프를 차단해야 하는지 여부가 커뮤니티에서 분열되었다.
- 변동 필드 전처리의 범위와 어떤 필드를 제거할지에 대한 합의가 없어서 구현마다 결과가 달라진다는 점이 논쟁거리로 남아 있다.
실용적 조언
- 각 툴 호출의 키를 생성할 때 툴 이름과 인자를 결합하되 JSON 직렬화에서 sort_keys=True를 사용해 인자 순서 변동을 무력화해야 한다. 변동 필드인 타임스탬프나 요청 ID는 해시에 포함시키지 않도록 전처리 단계에서 제거해야 동일 호출을 일관되게 인식할 수 있다. 중복이 탐지되면 모델에게 반복 중단 신호를 반환하거나 해당 경로를 성공/실패로 명시해 다음 정책을 결정하도록 해야 한다.
- 툴 반환이 자유텍스트인 경우에는 반환 텍스트를 정규화하거나 중요 필드를 추출해 결과 지문을 별도로 생성하는 방식으로 응답 차원의 반복을 감시해야 한다. 결과 지문화는 툴 출력이 동일한 실패 패턴을 재생산할 때 유용하며 이 경우엔 호출 지문화와 병행해 적용해야 효과가 있다. 또한 로그에 중복 히트와 차단 사유를 남겨 디버깅과 정책 조정에 활용해야 한다.
- 운영 중에는 cap이나 max_turns를 단순히 올리기보다 지문화와 종료 조건을 먼저 도입해 루프의 근본 원인을 제거해야 비용과 지연을 줄일 수 있다. 테스트 환경에서 다양한 쿼리 패턴으로 해시 충돌 및 변동 필드 처리 결과를 검증해 운영 규칙을 표준화하면 예기치 못한 반복을 예방할 수 있다. 마지막으로 복잡한 비결정적 툴과 통신할 때는 응답 표준화(contract)를 설계해 에이전트가 성공 여부를 기계적으로 판정할 수 있게 해야 한다.
섹션별 상세
import hashlib, json
seen = set()
def action_key(tool_name, args):
blob = tool_name + json.dumps(args, sort_keys=True)
return hashlib.sha256(blob.encode()).hexdigest()
def guard(tool_name, args):
key = action_key(tool_name, args)
if key in seen:
return "REPEAT_BLOCKED: this exact call already ran"
seen.add(key)
return None # allowed이 코드는 에이전트의 툴 호출을 툴 이름과 정렬된 JSON 인자로 결합해 SHA-256 해시를 생성하고 한 실행 내에 이미 실행된 호출을 차단하는 예시이다.
용어 해설
- 호출 지문화(Fingerprinting)
- — 툴 이름과 인자 조합을 정렬하고 해시하여 각 호출을 고유 식별자로 만드는 기법으로, 동일한 입력이 재시도될 때 중복 실행을 탐지하고 차단하는 데 사용된다. 입력을 정규화하기 위해 JSON 정렬(sort_keys=True)과 불변 필드 제거가 필요하며, 이를 통해 에이전트의 반복 호출을 빠르게 판별할 수 있다. 에이전트 루프를 근본적으로 멈추는 간단한 실행 시 검증 수단으로 널리 활용된다.
- 재귀 제한(recursion_limit)(recursion_limit)
- — 에이전트 또는 프레임워크가 한 실행에서 허용하는 최대 재귀 호출 또는 턴 수를 가리키는 설정으로, 한계에 도달하면 오류를 발생시켜 실행을 종료한다. 본문에서는 LangGraph의 recursion_limit 기본값이 10007로 설정되어 있어 단순히 값만 높여서는 루프 원인을 해결하지 못한다고 지적되었다. 이 값을 경보처럼만 사용하면 실패의 원인 파악과 작업 완료 여부 판단이 이루어지지 않는다.
- 종료 조건(Stop Condition)
- — 에이전트가 목표 달성 여부를 판단해 실행을 중단할 명시적 기준으로, 목표 성취 검증이나 툴 반환값의 성공/실패 판정 로직이 포함된다. 적절한 종료 조건이 없으면 실행은 단지 캡(cap)이나 턴 제한에 의해 중단되어 작업 완료 여부를 알 수 없게 된다. 결과적으로 종료 조건 설계는 루프 해결과 비용 제어에 핵심적인 역할을 한다.
- 정렬된 JSON 직렬화(sort_keys)(sort_keys)
- — JSON 직렬화 시 키를 정렬해 동일한 키-값 쌍이 항상 같은 문자열로 출력되도록 하는 옵션으로, 인자 순서가 바뀌어도 동일 호출로 인식되게 하는 데 중요하다. 정렬을 사용하지 않으면 동일한 의미의 인자가 다른 순서로 주어질 때 다른 해시가 생성되어 중복 탐지가 무력화된다. Python 예시에서는 sort_keys=True가 필수적이라는 점이 구현상 핵심으로 제시되었다.
- 변동 필드(Volatile Field)
- — 타임스탬프나 요청 ID처럼 매 호출마다 값이 달라져 해시 결과를 매번 변경시키는 필드로, 해시 기반 중복 탐지에서는 해당 필드를 제거하거나 무시해야 동일 호출로 판단할 수 있다. 변동 필드를 그대로 포함하면 모든 호출이 유일한 것으로 처리되어 루프 검출이 실패한다. 따라서 전처리 단계에서 변동 필드 필터링이 필요하다.
언급된 도구
에이전트 프레임워크로서 재귀 제한과 오류 메시지 예시가 본문에서 사례로 사용되었다
에이전트 실행에서 기본 max_turns 정책을 제공하는 SDK로서 max_turns 기본값과 예외(MaxTurnsExceeded)가 사례로 언급되었다
AI 요약 · 북마크 · 개인 피드 설정 — 무료
출처 · 인용 안내
인용 시 "요약 출처: AI Trends (aitrends.kr)"를 표기하고, 사실 확인은 원문 보기 기준으로 진행해 주세요. 자세한 기준은 운영 정책을 참고해 주세요.

