스크랩

Claude Code 백그라운드 에이전트의 숨은 비용: 30초마다 실행되는 '상태 요약'이 비용의 16%를 쓴다

Claude Code 백그라운드 에이전트의 숨은 비용: 30초마다 실행되는 '상태 요약'이 비용의 16%를 쓴다

한눈에 보는 핵심 요약

Claude Code에서 백그라운드 에이전트를 쓰면, 에이전트 패널에 짧은 상태 문구가 표시됩니다. 이 3~5단어짜리 문구를 만들기 위해 Claude Code는 약 30초마다 에이전트의 전체 대화 기록을 해당 에이전트의 모델(대개 Opus)에 다시 보냅니다. 패널에만 표시되는 이 문구가 생각보다 큰 비용을 차지한다는 분석이 나왔습니다.

  • Flamerix AI의 측정에서 agent_summary 호출은 추정 비용의 16.4%, 전체 API 요청의 19.7%(약 5건 중 1건)를 차지했습니다.
  • 이 요약은 에이전트 패널에만 표시되고, 대화나 모델의 문맥에는 전혀 들어가지 않습니다.
  • 요약 기능만 끄는 공식 설정은 없습니다.
  • 바이너리를 수정하지 않고 요약 타이머만 차단하는 Bun 사전 로드 스크립트 no-agent-summary.js가 공개됐습니다.

상태 요약은 어떻게 동작하나

백그라운드 에이전트가 실행되는 동안 Claude Code는 주기적으로 다음 작업을 합니다.

  1. 에이전트의 전체 대화 기록을 에이전트 모델에 보냅니다.
  2. 「가장 최근 행동을 3~5단어로 설명하라」고 요청합니다.
  3. 응답으로 에이전트 패널의 상태 줄을 갱신합니다.

이 기능은 2.1.232(2026년 8월 13일) 부터 대화형 백그라운드 에이전트에서 기본으로 켜졌습니다.

숫자로 보는 비용

DRIM이 OpenTelemetry의 claude_code.api_request 이벤트를 query_source별로 묶어 분석하다가 이 현상을 발견했습니다.

| 항목 | 수치 | |---|---| | 추정 비용 비중 (훅 적용 전 1주일) | 16.4% (주 대화는 15.7%) | | 전체 API 요청 비중 | 19.7% | | 이 기능 비용 중 Opus에서 발생한 비율 | 96% | | 출력 토큰 1개당 입력 토큰 | 약 9,000개 | | 훅 적용 후 이틀간 비중 | 0% |

주 대화보다 상태 요약이 비용을 더 많이 썼다는 점이 눈에 띕니다. 출력은 몇 단어인데 입력은 전체 기록이다 보니 입력과 출력의 비율이 극단적으로 치우쳐 있습니다.

참고: 여기서 말하는 비용은 실제 청구액이 아니라 Claude Code의 cost_usd 추정치입니다. 구독 이용자라면 돈 대신 사용량 제한 여유가 줄어듭니다.

프롬프트 캐시가 있어도 비용은 남는다

  • 호출의 약 98%는 캐시에서 기록을 다시 읽습니다.
  • 하지만 캐시가 적용되지 않는 약 2%의 호출이 이 기능 비용의 3분의 1 이상을 차지합니다.

캐시는 비용을 줄여 주지만 없애 주지는 못합니다.


왜 공식 설정으로는 끌 수 없나

가장 먼저 떠오르는 방법은 설정으로 끄는 것이겠지만, 하나씩 확인해 보면 모두 막혀 있습니다.

  • SDK의 agentProgressSummaries: 기본값은 false지만 번들 내부에서는 OR 조건의 한 항목일 뿐이고, 이 값을 쓰는 코드 경로는 모두 true로 설정합니다. false로는 끌 수 없습니다.
  • CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES: 문서화되지 않은 환경 변수로, 켜는 것만 가능합니다.
  • 요약 간격: 모델 계열별 서버 플래그로 정해지며 30~240초로 제한됩니다. 기본값이 이미 최솟값이고, 타이머를 끄는 값은 없습니다.
  • DISABLE_NON_ESSENTIAL_MODEL_CALLS: 바이너리에 존재하지 않습니다.
  • CLAUDE_CODE_FORK_SUBAGENT=0 / CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1: 요약은 멈추지만 각각 포크 서브에이전트, 백그라운드 에이전트 기능까지 함께 꺼집니다.

비용 도구에서도 잘 보이지 않는다

  • 이 요청은 디스크의 세션 로그에 기록되지 않습니다(이슈 #104358).
  • 그래서 대화 기록을 읽는 비용 분석 도구로는 확인할 수 없고, OpenTelemetry 내보내기에서만 보입니다.
  • 백그라운드 토큰 사용량 문서에도 빠져 있습니다. 문서에 있는 Agent View의 행 요약은 몇 분마다 갱신되는 Haiku급 모델 기능으로 설명돼 있어 실제 동작과 다릅니다.

참고로 2.1.310에는 전체 기록 대신 약 2,000토큰짜리 메모를 쓰는 더 저렴한 short 모드가 추가됐습니다. 다만 기본은 꺼져 있고, 서버 플래그로만 바꿀 수 있습니다.


해결책: 요약 타이머만 골라서 막는 no-agent-summary.js

공개된 스크립트는 약 80줄 분량의 Bun 사전 로드(preload) 스크립트입니다.

동작 원리

  • setTimeout과 setInterval을 감쌉니다.
  • 콜백 소스에 [AgentSummary] Timer fired 같은 요약기 식별 문자열이 있으면, 타이머를 등록하지 않고 비활성 핸들을 돌려줍니다.
  • 그 밖의 타이머는 그대로 통과시킵니다.

왜 이런 방식을 썼나

  • 사전 컴파일된 Bun 바이트코드 때문에 바이너리 안의 JavaScript 텍스트를 고쳐도 효과가 없습니다.
  • API 클라이언트가 globalThis.fetch를 거치지 않아서 네트워크 단에서 가로챌 수도 없습니다.
  • 반면 Function.prototype.toString은 바이트코드로 실행되는 콜백도 소스 문자열을 돌려주므로, 타이머를 식별하는 데 쓸 수 있습니다.

흥미로운 디테일이 하나 있습니다. 비활성 타이머의 지연값은 정확히 2³¹−1ms를 씁니다. Node와 Bun은 이보다 큰 지연을 1ms로 처리하기 때문에, 무한대에 가까운 값을 넣으면 오히려 즉시 실행될 수 있습니다.


적용 방법과 주의점

설치

  • 스크립트를 고정된 위치에 두고, 절대 경로로 BUN_OPTIONS=--preload ...에 지정합니다.
  • 모든 Claude Code 프로세스에 전달되도록 다음 중 하나를 씁니다.
    • 셸 초기화 파일
    • systemd 사용자 환경 (보통 다음 로그인부터 적용)
    • PATH에서 실제 실행 파일보다 앞에 오는 claude 래퍼 (예제 래퍼는 자식 프로세스에 사전 로드 옵션이 중복으로 붙지 않도록 처리합니다)

꼭 재시작해야 할 것들

  • 설치 후 claude daemon stop --any로 데몬을 멈추고, 실행 중인 세션도 모두 재시작해야 합니다.
  • 데몬, pty 호스트, 미리 준비된 bg-spare 세션은 데몬을 시작한 클라이언트의 환경을 물려받습니다. 기존 데몬이 살아 있으면 훅이 없는 세션이 계속 만들어질 수 있습니다.
  • Linux에서는 /proc/<pid>/environ으로 --preload가 전달됐는지 확인할 수 있습니다.
  • claude -p, cron, CI, 에디터 연동 같은 헤드리스 실행에도 같은 환경 설정이 필요합니다.

제어용 환경 변수

  • CC_AGENT_SUMMARY_MODE
    • off(기본값): 요약 타이머를 막습니다.
    • pass: 훅을 로드하고 로그는 남기지만 모든 타이머를 그대로 실행합니다.
    • 그 밖의 값: 표준 오류로 경고하고 off로 처리합니다.
  • CC_AGENT_SUMMARY_LOG: 지정한 파일에 로드 상태, 처음 20개의 일치 타이머, 종료 시 집계를 덧붙입니다.
  • CC_AGENT_SUMMARY_MARKER: 지정한 파일을 로드 상태 한 줄로 덮어씁니다.

플랫폼 지원

  • Linux에서만 검증됐습니다. macOS와 Windows는 테스트하지 않았습니다.
  • Windows에서는 /$bunfs/ 조건 때문에 훅이 설치되지 않을 가능성이 높습니다.

효과는 확실했을까? 버전별 검증 결과

Flamerix AI가 2.1.301의 모든 Claude Code 프로세스에 훅을 적용하자 agent_summary 요청이 0건이 됐고, 다른 부수 요청이 그만큼 늘어나지도 않았습니다.

  • 서브에이전트, 백그라운드 에이전트, 포크된 스킬, 백그라운드 세션은 정상적으로 실행되고, 요약용 포크만 빠집니다.
  • 대신 에이전트 패널의 상태 갱신이 멈추고, SDK의 task_progress에서 summary가 사라집니다.
  • 성능 영향은 작습니다. 타이머 콜백마다 toString을 한 번만 호출하고 결과를 WeakMap에 캐시합니다. 한 차례 측정에서 claude --version 실행 시간이 약 10ms에서 17ms로 늘었습니다.

요약 포크 횟수 (훅 없음 / pass / off)

  • 2.1.312, 2.1.310, 2.1.301: 약 2.5분 실행에서 5 / 5 / 0
  • 2.1.308: 5 / 6 / 0 (pass 실행에서 에이전트가 8단계 대신 9단계를 수행)
  • 2.1.280, 2.1.257: 대화형 1 / 1 / 0, 타이머 실행 11 / 11 / 0
  • 2.1.312 별도 대화형 검증: 훅 없이 6회, off에서 0회

pass에서는 요약이 다시 나타나고 off에서만 사라졌습니다. 스크립트가 단순히 로드만 된 게 아니라 차단 동작 자체가 효과를 냈다는 뜻입니다.


업데이트할 때마다 확인해야 할 체크리스트

이 훅은 콜백 안의 소스 문자열에 의존합니다. 그래서 Claude Code가 업데이트되면 경고 없이 무력화될 수 있습니다.

✅ 확인할 항목

  1. 로그에 preload active mode=off …가 있는지 확인합니다. 없다면 BUN_OPTIONS가 전달되지 않았거나 경로가 절대 경로가 아닐 수 있습니다.
  2. 백그라운드 서브에이전트를 1~2분 돌렸을 때 summary timer #1 suppressed (setTimeout, delay=30000) 같은 로그가 찍히고 패널 갱신이 멈추는지 봅니다.
  3. 가장 확실한 방법은 OpenTelemetry에서 query_source = agent_summary가 계속 0인지 확인하는 것입니다.

⚠️ 훅이 깨질 수 있는 경우

  • 로그 문자열이나 요청 출처 식별자가 바뀔 때
  • 콜백이 fn.bind(...)나 다른 래퍼를 거쳐 원래 소스를 얻지 못할 때
  • 식별 문자열이 콜백 밖의 공용 상수로 옮겨질 때
  • 타이머가 timers/promises나 스케줄러 루프처럼 감싸지 않은 API로 옮겨질 때

기능 도입 타임라인 (2026년)

  • 2.1.32 (2월 5일): 코드가 처음 등장했지만 공개 빌드에서는 실행되지 않음
  • 2.1.72 (3월 9일): SDK에서 선택적으로 켤 수 있게 됨
  • 2.1.117 (4월 21일): 포크 활성화 조건이나 서버 배포를 통해 대화형 CLI에서도 실행 가능
  • 2.1.220 이하 (7월 24일): 포크된 스킬은 항상 요약되는 상태
  • 2.1.232 (8월 13일): 대화형 백그라운드 에이전트에서 기본 활성화
  • 2.1.300 (10월 14일): 서버가 간격을 30~240초로 조절 가능
  • 2.1.310 (10월 23일): 더 저렴한 short 모드 추가 (비활성)

Flamerix AI 환경에서 이 기능이 주간 추정 비용에서 차지한 비중은 7월 3.5~7%에서 10월 중순 약 16% 로 늘었습니다. 2.1.233을 도입한 주에 7.3%, 이어서 11.8%로 올랐습니다.


사용 전 꼭 알아둘 점

  • 스크립트는 MIT 라이선스, 보증 없이 제공되며 사용에 따른 위험은 이용자가 집니다.
  • 클라이언트의 런타임 동작을 바꾸는 것은 이용 중인 서비스 약관과 충돌할 수 있으니 직접 확인해야 합니다.
  • 제작자는 Anthropic과 제휴 관계가 없으며, 후원이나 승인을 받은 도구가 아닙니다.

에디터의 한마디

이번 분석에서 가장 중요한 점은 보이지 않는 비용을 측정했다는 것입니다. 세션 로그에도, 비용 도구에도 잡히지 않는 요청이 전체의 5분의 1을 차지할 수 있다는 사실은 OpenTelemetry 같은 관측 도구가 왜 필요한지 잘 보여 줍니다.

백그라운드 에이전트를 많이 쓰는 팀이라면 스크립트 적용 여부와 상관없이 query_source별 사용량을 한 번 확인해 보시길 권합니다. 그리고 장기적으로는 요약 기능만 끄는 공식 옵션이나, 기본으로 켜진 short 모드처럼 더 저렴한 방식이 제공되기를 기대해 봅니다.

원문: GeekNews


원문: https://news.hada.io/topic?id=36213