· 6분 · 에이전트

에이전트 루프의 과도한 API 예산 소모 방지하기

에이전트 루프의 과도한 API 예산 소모 방지하기

구체적인 엔지니어링 기법, 비교 표, 실용적인 예시를 포함해 글을 바로 작성하겠습니다. 실제 루프 동작과 비용 관리에 기반해 내용을 구성하고, 마지막에는 실행 가능한 핵심 제안을 담겠습니다.## API 예산을 가장 빠르게 소진하는 방법

한번은 “간단한” 코딩 에이전트가 400토큰짜리 사용자 요청을 두 분도 채 안 되어 청구서에 찍힌 28,000토큰짜리 항목으로 바꾸는 것을 본 적이 있습니다.

그 패턴은 지긋지긋할 만큼 익숙했습니다. 에이전트는 검색 도구를 호출하고, 결과를 요약하고, 자기 요약을 다시 읽고, 표현만 조금 바꿔 같은 도구에 다시 요청했습니다. 그러고는 매 턴마다 대화 전체와 저장소의 절반, 실패한 계획의 흔적까지 담길 때까지 컨텍스트를 늘렸습니다. 모델에는 문제가 없었습니다. 잘못된 것은 루프 설계였습니다.

이것이 에이전트 시스템의 핵심 문제입니다. 비용은 사용자 프롬프트에 비례하는 것이 아니라 다음에 비례합니다: 루프 동작. 잘못된 루프 하나가 다음 요소를 몇 배로 늘릴 수 있습니다:

  • 계속 커지는 컨텍스트로 인한 입력 토큰,
  • 장황한 “사고”로 인한 출력 토큰,
  • 반복 호출로 인한 도구 비용,
  • 그리고 순차적인 재시도로 인한 지연 시간.

프로덕션 환경에서 에이전트를 개발한다면, 진짜 과제는 “모델을 더 똑똑하게 만드는 것”이 아닙니다. “루프의 비용을 낮추고, 길이를 줄이며, 폭주하기 어렵게 만드는 것”입니다.

에이전트 루프가 비용을 폭증시키는 이유

일반적인 채팅 턴에는 입력 하나와 출력 하나가 있습니다. 에이전트 루프는 다음과 같은 순서로 진행됩니다:

  1. 사용자가 질문하고,
  2. 모델이 계획을 세우고,
  3. 모델이 도구를 호출하고,
  4. 도구가 데이터를 반환하고,
  5. 모델이 데이터를 읽고,
  6. 모델이 다음 단계를 결정하고,
  7. 이를 반복합니다.

반복할 때마다 다음 반복의 프롬프트에 새 토큰이 추가됩니다. 도구 출력 전체와 이전 사고 과정을 계속 덧붙이면, 프롬프트는 대체로 계단식으로 커집니다.

실제로는 이로 인해 비용을 배가시키는 세 가지 요인이 생깁니다:

  • 컨텍스트 누적: 각 단계가 이전 단계의 내용을 그대로 이어받습니다.
  • 잦은 도구 호출: 인수만 조금씩 바꿔 같은 도구를 호출합니다.
  • 끝없는 재시도: 이미 충분히 수행했는데도 모델이 계속 “한 가지만 더 시도”합니다.

흔히 놓치는 점은 실행별 추적 데이터를 살펴보기 전까지 토큰 낭비가 대개 드러나지 않는다는 것입니다. 최종 답변은 간결할 수 있지만, 숨겨진 처리 과정은 사용자에게 보이는 출력보다 10x 더 클 수 있습니다.

간단한 비용 모델

루프가 실행하는 N 단계 각각에 다음이 포함된다면:

  • C 이월되는 컨텍스트의 토큰,
  • I 다시 주입되는 도구 출력의 토큰,
  • O 모델 출력의 토큰,

대략적인 총량은 다음과 같습니다:

total_tokens ≈ N * (C + I + O)

이는 낙관적인 추정입니다. 그 이유는 C 일반적으로 루프가 계속될수록 커지기 때문입니다. 실제 에이전트에서는 후반부 턴의 비용이 가장 높은 경우가 많습니다.

실제로 중요한 제어 수단

제어할 수 있는 지점은 몇 군데 있습니다. 핵심은 이들을 조합하는 것입니다.

제어 수단줄이는 대상가장 적합한 사용 사례절충점
단계 수 제한폭주하는 루프도구를 사용하는 모든 에이전트어려운 작업에서는 일찍 중단될 수 있음
컨텍스트 정리입력 토큰장기 작업유용한 이력이 사라질 위험
비용이 더 낮은 하위 에이전트일상적인 작업에 드는 비용검색, 추출, 분류조율에 따른 오버헤드
도구 결과 캐싱반복적인 도구 사용 비용변화가 적은 소스, 반복 쿼리데이터 노후화와 캐시 무효화
비용 관측 가능성예상치 못한 청구액프로덕션 시스템계측 작업 필요
루프 중단 조건무한 또는 거의 무한한 루프신뢰성이 낮은 에이전트“충분히 좋은” 상태를 정의해야 함

가장 효과적인 프로덕션 구성은 이 모든 수단을 사용합니다.

1) 단계 수를 엄격하게 제한하기

이것은 가장 간단하면서도 가장 과소평가되는 해결책입니다. 엄격한 최댓값을 설정하지 않으면, 모델이 스스로 멈출 것이라고 믿는 셈입니다.

저는 보통 다음과 같이 시작합니다:

  • 3–5 단계: 범위가 좁은 작업,
  • 6–8 단계: 중간 수준의 작업,
  • 그 이상은 확실한 이유가 있을 때만 허용합니다.

적절한 제한은 단순한 안전장치가 아닙니다. 작업을 더 잘 분해하도록 유도합니다. 에이전트가 네 번만 행동할 수 있다면, 대체로 다음과 같이 동작합니다:

  • 더 일찍 적절한 도구를 요청하고,
  • 탐색 과정에서의 우회를 피하고,
  • 쓸 만한 답에 더 빠르게 도달합니다.

루프 제어기 예시

MAX_STEPS = 5

for step in range(MAX_STEPS):
    action = model.next_action(state)

    if action.type == "final":
        return action.final_answer

    observation = run_tool(action.tool_name, action.arguments)
    state = update_state(state, action, observation)

raise RuntimeError("Agent stopped after step cap")

흔히 놓치는 점은 단계 수 제한을 다음에 적용해야 한다는 것입니다: 전체 계획. 각 서브루틴에 독립적으로 적용해서는 안 됩니다. 그렇지 않으면 비용 폭증이 최상위 에이전트에서 중첩된 보조 에이전트로 옮겨갈 뿐입니다.

2) 매 턴마다 컨텍스트 정리하기

대부분의 에이전트는 너무 많은 이력을 유지합니다. 이전의 모든 도구 응답이 필요한 것은 아닙니다. 특히 응답이 길고 그중 한 가지 세부 정보만 중요했다면 더욱 그렇습니다.

실제로는 계층형 메모리 정책을 사용했을 때 가장 좋은 결과를 얻었습니다:

  • 원문 그대로 유지: 사용자 요청, 현재 목표, 최종 결정, 아직 해결되지 않은 제약 조건.
  • 요약: 이전 추론, 분기별 시도, 긴 도구 출력.
  • 삭제: 막다른 가설, 중복된 도구 결과, 오래된 중간 초안.

유지할 정보

원본 대화 기록을 그대로 다시 입력하는 것보다 간결한 상태 객체를 사용하는 편이 낫습니다:

{
  "goal": "Find the failing endpoint and propose a fix",
  "constraints": [
    "No schema changes",
    "Must preserve backward compatibility"
  ],
  "known_facts": [
    "500s started after deploy 1842",
    "Only /v1/search is affected"
  ],
  "open_questions": [
    "Is the new cache layer returning stale payloads?"
  ],
  "step": 3
}

이는 모델에 “생각을 말로 풀어내는” 대화 20턴을 입력하는 것보다 훨씬 저렴합니다.

실용적인 컨텍스트 축소 패턴

버퍼 세 개를 사용하세요:

  • working_set: 현재 턴, 도구 출력, 현재 실행 중인 계획.
  • summary_memory: 압축된 이력.
  • archive: 감사에 필요할 수 있지만 프롬프트 컨텍스트에는 필요하지 않은 모든 정보.

에이전트가 볼 수 있는 것은 다음뿐입니다 working_set + summary_memory. 다음이 working_set 너무 커지면, 요약하여 다음에 저장합니다 summary_memory.

여기서 주의할 점은 요약 과정에서 의미가 변질되는 것입니다. 요약기가 정보를 누락하면 제약 조건이 사라져 에이전트가 같은 실수를 반복할 수 있습니다. 저는 모델이 서술문에서 사실을 추론할 필요가 없도록 구조화된 사실을 자유 형식 요약과 별도로 보존하는 방식을 선호합니다.

3) 단순한 작업에는 더 저렴한 하위 에이전트 사용하기

모든 단계에 가장 뛰어난 모델이 필요한 것은 아닙니다.

작업이 다음과 같다면:

  • JSON에서 필드 추출,
  • 티켓 분류,
  • 파일 변경 여부 확인,
  • 또는 짧은 검색 쿼리 생성,

더 작고 저렴한 모델로도 충분한 경우가 많습니다. 고가 모델은 계획 수립, 종합, 모호한 문제 처리에 사용하세요.

일반적인 역할 분담

  • 주 에이전트: 작업을 추론하고 언제 중단할지 결정합니다.
  • 하위 에이전트: 검색, 추출, 정규화처럼 범위가 좁은 작업을 수행합니다.
  • 검증기: 출력의 형식, 완전성, 명백한 오류를 확인합니다.

실용적인 아키텍처:

User
  -> Orchestrator (strong model)
      -> Search agent (cheap model)
      -> Extract agent (cheap model)
      -> Final synthesis (strong model)

이 방식이 효과적인 이유

주 모델은 모든 작업을 직접 수행하는 대신 제어 흐름에 집중합니다. 하위 에이전트는 프롬프트로 작업 범위를 제한하고 토큰 수에도 엄격한 상한을 설정할 수 있습니다.

검색 하위 에이전트용 프롬프트 예시:

Return only:
1. the best search query,
2. 3 likely source categories,
3. no explanation.

이처럼 범위가 좁은 작업 규약은 출력을 짧고 예측 가능하게 유지합니다.

플랫폼에서 여러 모델을 합리적인 비용으로 사용할 수 있다면, 바로 이런 상황에서 그 가치가 드러납니다. 오케스트레이션에는 강력한 모델을, 반복적인 하위 작업에는 더 저렴한 모델을 사용하면 일상적인 파싱에 고가 모델의 토큰을 낭비할 이유가 없습니다.

4) 도구 결과를 철저히 캐싱하기

에이전트 루프는 같은 정보에 여러 번 비용을 지불하는 경우가 많습니다:

  • 반복적인 파일 읽기,
  • 반복적인 데이터베이스 조회,
  • 반복적인 웹 검색,
  • 반복적인 임베딩 또는 검색 호출.

도구 출력 캐싱은 투자 대비 효과가 가장 큰 개선 사항 중 하나입니다.

원시 텍스트가 아닌 의미 기반 키로 캐싱하기

원본 프롬프트 기반 캐시는 표현이 조금만 바뀌어도 적중하지 않습니다. 더 나은 키는 정규화된 의도를 기반으로 합니다:

def cache_key(tool_name, args):
    normalized = normalize_args(args)
    return f"{tool_name}:{hash(normalized)}"

캐시 항목 예시:

{
  "tool": "repo_search",
  "query": "auth middleware rate limit bug",
  "result": {
    "files": ["src/middleware/auth.py", "src/api/routes.py"],
    "ts": 1710001123
  }
}

캐싱 효과가 가장 큰 경우

  • 저장소 검사 에이전트,
  • 문서 검색,
  • 변경이 적은 지식 베이스,
  • 결정론적 변환,
  • 여러 턴으로 이루어진 워크플로에서의 반복 검색.

캐싱이 해가 될 수 있는 경우

  • 빠르게 변하는 데이터,
  • 사용자별 결과,
  • 최신성에 대한 시간 제약이 있는 모든 정보.

흔한 함정은 도구 출력을 캐싱하면서 에이전트에 정보의 최신성을 알리지 않는 것입니다. 모델이 캐시된 데이터를 받았는데 실시간 데이터를 받았다고 생각하면 잘못된 결정을 내릴 수 있습니다. 도구 메타데이터에 타임스탬프나 유효 기간을 포함하세요.

5) 대시보드에 지출 표시하기

지연 시간과 성공률만 추적하면 실제 실패 양상인 ‘비용이 많이 드는 성공’을 놓치게 됩니다.

실행별로 다음 항목을 관측할 수 있어야 합니다:

  • 입력 토큰,
  • 출력 토큰,
  • 도구 호출,
  • 도구 지연 시간,
  • 총지출,
  • 단계 수,
  • 중단 사유.

최소한의 이벤트 스키마

{
  "run_id": "r_8f2c",
  "step": 4,
  "model": "gpt-5.5",
  "input_tokens": 1842,
  "output_tokens": 221,
  "tool_name": "repo_search",
  "tool_latency_ms": 312,
  "estimated_cost_usd": 0.087,
  "stop_reason": "step_cap"
}

실무에서 살펴볼 지표

  • 성공한 작업당 토큰 수: 가장 명확한 비용 지표입니다.
  • 단계당 토큰 수: 프롬프트 비대화를 포착합니다.
  • 반복되는 도구 호출: 대개 계획 수립상의 버그입니다.
  • 중단 사유 분포: 많은 실행이 상한에 도달해 종료된다면 에이전트의 계획 수립이 부족한 것입니다.

가장 유용한 그래프 중 하나는 작업 유형별 비용입니다. 일부 범주는 본래 비용이 많이 들 수 있습니다. 그것은 괜찮습니다. 포착해야 할 것은 비용이 적게 들어야 하는데 그렇지 않은 작업입니다.

6) 폭주하는 루프를 조기에 중단하기

루프의 중단 사유가 “단계 상한에 도달했다”뿐이어서는 안 됩니다.

유용한 중단 조건은 다음과 같습니다:

  • 모델이 최종 답변을 생성한 경우,
  • 새로운 정보 없이 같은 도구 호출이 반복되는 경우,
  • 신뢰도 또는 유용성의 변화량이 임계값보다 작은 경우,
  • 검증기가 작업이 해결되었다고 판단한 경우,
  • 계획이 더 이상 진전되지 않는 경우.

루프 차단기 예시

seen_calls = set()

for step in range(MAX_STEPS):
    action = model.next_action(state)
    signature = (action.tool_name, canonicalize(action.arguments))

    if signature in seen_calls:
        return fail("repeated tool call with no new information")
    seen_calls.add(signature)

    observation = run_tool(action.tool_name, action.arguments)
    state = update_state(state, action, observation)

    if verifier_says_done(state):
        return finalize(state)

이는 매우 흔한 실패 양상을 포착합니다. 모델이 “도움을 주고 싶어서” 같은 쿼리의 표현만 바꾸는 경우입니다.

또 다른 유용한 보호 장치: 유용성 기준으로 진행 여부 결정하기

어떤 단계가 상태를 충분히 바꾸지 못한다면 계속 진행하지 마세요. 다음처럼 단순한 기준으로도 판단할 수 있습니다:

  • 새로 발견된 파일이 없음,
  • 새로 추출된 개체가 없음,
  • 답변 신뢰도가 개선되지 않음,
  • 새로운 근거가 추가되지 않음.

정확한 지표는 작업에 따라 다르지만, 추가로 얻는 이득을 판단할 기준은 반드시 필요합니다.

이를 잘 조정하면 실제로 어떤 일이 일어날까요

루프를 더 엄격하게 관리하면 에이전트는 대개 처음에는 알아차리기 어려운 방식으로 개선됩니다:

  • 불필요한 말이 줄고 더 단호하게 판단합니다.
  • 적절한 도구를 더 일찍 요청합니다.
  • “아직 조사 중입니다”라는 응답이 반복되는 횟수가 줄어듭니다.
  • 비용이 줄어듭니다 및 턴 수가 줄면 컨텍스트와 지연 시간도 줄어들기 때문에 속도도 빨라집니다.

중요한 절충점은 엄격한 상한과 과감한 컨텍스트 축소가 실제로 어려운 작업에서 시스템을 취약하게 만들 수 있다는 것입니다. 따라서 에이전트가 실제로는 시간 초과로 종료되는 문제를 “해결하고” 있는 것처럼 표현하지 마세요. 로그와 사용자에게 표시되는 메시지에 그 사실을 솔직하게 드러내세요.

제가 따르는 경험칙은 범위가 정해진 역량 을 끝없는 탐색보다 우선하도록 최적화하는 것입니다.

실용적인 프로덕션 구성

내일 에이전트를 출시한다면 다음 구성부터 시작하겠습니다.

  • MAX_STEPS = 5
  • 전체 대화 기록을 재전송하는 대신 구조화된 상태 사용
  • 매 턴이 끝난 뒤 요약 메모리 생성
  • 검색과 추출에 저렴한 하위 에이전트 사용
  • 최신성 메타데이터를 포함한 도구 결과 캐시
  • 실행별 토큰 사용량과 비용 기록
  • 반복 호출 감지기
  • 명시적인 중단 사유

화려한 구성은 아니지만, 비용이 제품보다 빠르게 늘어나는 것을 막아 주는 구성입니다.

실무 핵심 요점

  • 먼저 단계 수에 상한을 두세요. 제어되지 않는 루프를 막는 가장 저렴한 보호 장치입니다.
  • 컨텍스트를 과감하게 줄이고 구조화된 상태를 요약과 분리해 유지하세요.
  • 모든 작업에 고가의 토큰을 쓰는 대신, 범위가 좁은 작업에는 더 저렴한 하위 에이전트를 사용하세요.
  • 반복해서 사용할 수 있는 도구 출력을 캐시하되, 모델이 오래된 데이터를 맹신하지 않도록 최신성을 표시하세요.
  • 실행별, 단계별, 도구 호출별 지출을 기록해 비용 증가 문제를 즉시 파악할 수 있게 하세요.
  • 자원이 소진될 때뿐 아니라 반복, 진전 없음, 완료 확인 시에도 루프를 중단하세요.
  • 범위가 정해진 역량을 목표로 설계하세요. 충분한 품질, 빠른 속도, 예측 가능한 동작이 영리하지만 비용이 많이 드는 방식보다 언제나 낫습니다.
A

이 블로그는 AI Prime Tech가 발행합니다. 기사는 AI의 도움을 받아 초안을 작성합니다. 모델 ID, 가격, 한도 등 버전별 세부 정보는 활용하기 전에 공식 문서와 대조해 확인하세요.

더 저렴하게 Claude API 이용하기

API 키 하나로 Claude Opus 5.5, Sonnet 5, Haiku 4.5, Fable 5.1 및 GPT-6개 모델을 이용하세요. 구독 없이 사용한 만큼만 결제합니다.

API 키 받기 →
AI Prime Tech는 독립적인 타사 API 게이트웨이입니다. Claude™ 및 Anthropic®는 Anthropic, PBC의 상표입니다. 제휴 또는 보증 관계를 의미하지 않습니다.