Claude Code agent_summary: 토큰 사용량 측정과 비용 절감
Claude Code 백그라운드 에이전트의 진행 표시를 생성하는 요청 비용을 측정합니다. 한 팀의 추정 비용 비중 16.4%를 Claude Code 팀이 공개한 전체 2%, 터미널 6%와 비교합니다. 로컬 OpenTelemetry 수신기로 직접 비중을 구한 뒤 2.1.311+의 캐시 수정, 백그라운드 제어 설정, 커뮤니티 패치의 영향을 판단합니다.
목차

에이전트 기능을 끄기 전에 Claude Code를 2.1.311 이상으로 업데이트하고 OpenTelemetry로 agent_summary 비용을 측정합니다. 백그라운드 에이전트가 작업하는 동안 진행 요약 요청이 추가 비용을 만들 수 있습니다. 널리 알려진 16.4%는 한 팀의 추정 비용 비중이며, 모든 세션에 일률적으로 더해지는 토큰 소비 비율은 아닙니다.
agent_summary가 만드는 요청
agent_summary는 별도의 모델 요청으로 에이전트의 진행 표시 문구를 만듭니다. dblumenau의 Claude Code 2.1.312 보고에서는 작업 중인 백그라운드 에이전트의 대화 기록 전체를 해당 에이전트의 모델에 보냈습니다. 모델에 요구한 결과는 단어 3~5개로 이루어진 짧은 문구였습니다. 이 재현에서는 Haiku를 사용했으므로 Opus에만 해당하는 동작은 아닙니다.
작업 중인 백그라운드 에이전트는 약 30초마다 이런 요청을 보낼 수 있습니다. 공식 변경 기록에 따르면 대화 기록이 바뀌지 않았을 때는 해당 주기의 호출을 건너뛸 수 있습니다. 경과한 분에 2를 곱해 추정하지 말고 실제 요청 수를 셉니다.
따라서 화면에 표시되는 문구의 길이보다 컨텍스트 크기가 비용에 더 큰 영향을 줄 수 있습니다. 비용을 조사할 때는 기록된 모델과 캐시 사용량을 함께 확인합니다.
이 동작은 사용자가 설정하는 터미널 상태 표시줄과 별개입니다. 사용자 지정 상태 표시줄 문서는 API 토큰을 사용하지 않는 로컬 스크립트를 설명합니다. 이 스크립트를 제거해도 요약을 위한 별도 모델 요청은 중단되지 않습니다.
16.4%, 2%, 6%가 측정한 대상
세 비용 비율은 서로 다른 집단을 측정한 결과입니다. 2026-11-01 기준으로 공개된 관련 보고는 다음과 같습니다.
| 보고 | 측정 대상과 기간 | 보고된 요약 비용 비중 | 비용 계산 기준 |
|---|---|---|---|
| Flamerix AI | 한 팀의 1주일 측정 | 16.4% | Claude Code의 요청별 cost_usd 추정치 |
| Claude Code 팀의 edwinarbus | 텔레메트리를 보내는 사용자, 10월 29일 답변 직전 주 | 약 2% | API 정가로 계산한 비용 |
| 같은 팀 답변의 터미널 사용 집단 | 같은 텔레메트리 표본 중 터미널 사용 | 약 6% | API 정가로 계산한 비용 |
Flamerix는 요약 요청이 API 요청 수의 19.7%를 차지했다고 별도로 보고했습니다. 요청 비중과 비용 비중은 분모가 다릅니다. 어느 수치도 요약이 사용자의 구독 한도 중 얼마를 소비했는지 보여주지는 않습니다.
이 보고를 계기로 실제 작업의 비용을 측정합니다. 긴 작업을 많이 위임하는 팀은 주로 기본 대화에서 작업하는 개발자와 결과가 크게 다를 수 있습니다.
업데이트한 뒤 절감 효과를 확인합니다
Claude Code 팀의 답변에 따르면 2.1.311+에는 프롬프트 캐시를 활용하지 못하는 고비용 요약 요청에 대한 수정이 포함됩니다. 이 설명은 캐시 수정에 관한 것이며 요약 요청 자체가 없어진다는 뜻은 아닙니다. 모든 세션에서 일정 비율을 절감한다는 근거도 아닙니다.
네이티브 설치 방식으로 설치했다면 Bash에서 다음 명령을 실행합니다.
claude --version
claude update
claude --version
업데이트 후 Claude Code를 다시 시작합니다. 패키지 관리자로 설치했다면 공식 설치 안내에 있는 해당 패키지 관리자의 업데이트 명령을 사용합니다. 업데이트 채널이나 조직 정책에 따라 설치 가능한 버전이 제한될 수 있으므로 결과를 확인합니다.
같은 답변은 짧은 메모와 더 작은 모델을 사용하는 저비용 모드를 개발 중이라고 설명합니다. 출시 날짜는 제시하지 않았습니다. 2026-11-01 기준으로 이 모드를 켜는 공개 설정은 문서화되어 있지 않습니다. 아직 이 모드의 절감 효과를 예산에 반영하지 않습니다.
OpenTelemetry로 세션 비용 측정하기
claude_code.api_request 로그 이벤트를 수집하고 query_source별로 묶습니다. 이 값은 요청을 보낸 하위 시스템을 나타냅니다. 모니터링 참고 문서는 이 속성, 기록된 model, 추정 비용 cost_usd를 설명합니다.
조직이 이미 이 이벤트를 수집한다면 기존 수집 시스템을 사용합니다. 세션 하나나 정해진 시간 구간으로 범위를 좁힌 뒤, 요청 출처별로 API 요청 수와 cost_usd 합계를 구합니다. 모델을 비교할 때는 model을 두 번째 그룹 기준으로 추가합니다.
아래 로컬 예제에는 Bash, Python 3, 인증이 완료된 Claude Code 설치가 필요합니다. 텔레메트리 전송 대상을 직접 설정할 수 있는 컴퓨터에서 사용합니다. 예제는 집계값을 메모리에 보관하고 메타데이터를 출력하므로 원본 프롬프트나 응답을 수집할 필요가 없습니다.
로컬 수신기 시작하기
첫 번째 터미널에서 다음 코드를 실행합니다. 같은 컴퓨터 안에서 통신하는 로컬 루프백 주소를 사용합니다. OTLP HTTP JSON을 받아 요청 출처별 누적 합계를 출력합니다.
python3 -u - <<'PY'
import gzip, json, math
from collections import defaultdict
from http.server import BaseHTTPRequestHandler, HTTPServer
rows = defaultdict(lambda: [0, 0.0, 0])
models = defaultdict(set)
seen = set()
def scalar(value):
return next((value[k] for k in ("stringValue", "doubleValue", "intValue", "boolValue")
if k in value), None)
def report():
count = sum(row[0] for row in rows.values())
cost = sum(row[1] for row in rows.values())
missing = sum(row[2] for row in rows.values())
print("\nquery_source\trequests\trequest_%\tknown_USD\tcost_%\tmissing_cost\tmodels")
for source, (n, usd, absent) in sorted(rows.items()):
share = f"{100 * usd / cost:.2f}" if cost > 0 and not missing else "n/a"
names = ",".join(sorted(models[source]))
print(f"{source}\t{n}\t{100 * n / count:.2f}\t{usd:.6f}\t{share}\t{absent}\t{names}")
print(f"Total API records: {count}; missing costs: {missing}", flush=True)
class Receiver(BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def do_POST(self):
if self.path != "/v1/logs":
self.send_error(404)
return
raw = self.rfile.read(int(self.headers.get("Content-Length", "0")))
try:
if self.headers.get("Content-Encoding") == "gzip":
raw = gzip.decompress(raw)
data = json.loads(raw)
except (ValueError, OSError):
self.send_error(400)
return
for resource in data.get("resourceLogs", []):
for scope in resource.get("scopeLogs", []):
for record in scope.get("logRecords", []):
a = {item["key"]: scalar(item["value"])
for item in record.get("attributes", [])}
event = a.get("event.name") or scalar(record.get("body", {}))
if event in ("user_prompt", "claude_code.user_prompt"):
print("Received user_prompt event.", flush=True)
if event not in ("api_request", "claude_code.api_request"):
continue
identity = a.get("request_id") or json.dumps(record, sort_keys=True)
if identity in seen:
continue
seen.add(identity)
source = str(a.get("query_source") or "(missing)")
models[source].add(str(a.get("model") or "(missing)"))
row = rows[source]
row[0] += 1
try:
usd = (float(a["cost_usd"]) if a.get("cost_usd") is not None
else float(a["cost_usd_micros"]) / 1_000_000)
if not math.isfinite(usd) or usd < 0:
raise ValueError()
row[1] += usd
except (KeyError, ValueError, TypeError):
row[2] += 1
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(b"{}")
report()
server = HTTPServer(("127.0.0.1", 4318), Receiver)
print("Listening on 127.0.0.1:4318. Stop after Claude exits with Ctrl+C.", flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
finally:
server.server_close()
report()
PY
측정할 세션 실행하기
두 번째 터미널에서 다음 명령을 실행합니다. 괄호로 만든 서브셸은 환경 변수 변경을 이번 측정 프로세스에만 적용합니다.
(
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=none
export OTEL_TRACES_EXPORTER=none
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/json
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://127.0.0.1:4318/v1/logs
export OTEL_LOGS_EXPORT_INTERVAL=1000
claude
)
백그라운드 작업이 필요한 평소의 과제를 선택합니다. 예를 들어 파일을 수정하지 않고 인증 테스트를 검토하도록 백그라운드 에이전트에 요청합니다. 기본 대화에서 작업을 계속하면서 백그라운드 에이전트가 실제로 시작했는지 확인합니다.
Claude의 출력을 파이프로 넘기지 않고 대화형 세션을 유지합니다. claude -p로 바꾸면 실행 모드가 달라져 요약 동작도 달라질 수 있습니다.
프롬프트를 제출하면 첫 번째 터미널에 Received user_prompt event.가 먼저 표시되어야 합니다. 이어서 API 요청 행이 나타나야 합니다. 작업을 마치고 Claude를 정상 종료한 다음 마지막 텔레메트리 전송이 도착할 때까지 기다립니다. 그 후 Ctrl+C로 수신기를 중지하고 터미널 출력 기록에서 최종 표를 복사합니다.
결과 해석과 누락 데이터 확인하기
측정 구간의 agent_summary 행을 확인합니다. request_%의 분모는 수집한 모든 API 요청 이벤트 수입니다. cost_%의 분모는 수집한 모든 API 요청의 추정 비용 합계입니다. 요약 요청의 추정 비용을 이 합계로 나눈 뒤 100을 곱해 비용 비중을 구합니다.
summary cost share = 100 × summary estimated USD / all captured estimated USD
models 열에는 출처별로 보고된 모델이 나열됩니다. 기존 수집 시스템에서 캐시 동작을 조사한다면 input_tokens, cache_read_tokens, cache_creation_tokens, output_tokens도 확인합니다.
수신기는 출처가 없는 요청도 (missing)으로 분모에 포함합니다. 수집한 요청 중 하나라도 유효한 비용이 없으면 비용 비율에 n/a를 표시합니다. 결과를 전체 비용의 완전한 내역으로 해석하기 전에 누락된 필드를 해결합니다.
agent_summary 행이 없다는 것은 해당 시간 구간에서 관찰되지 않았다는 뜻입니다. 기능이 꺼졌다는 증거는 아닙니다. 먼저 프롬프트 이벤트와 API 요청 이벤트가 도착했고, 실제 백그라운드 작업도 있었는지 확인합니다.
아무 데이터도 오지 않으면 수신기가 실행 중인지, 프로토콜이 http/json인지, 엔드포인트가 /v1/logs로 끝나는지 확인합니다. 조직이 관리하는 전송 대상이 로컬 설정보다 우선할 수 있습니다. 추가 진단에는 같은 서브셸 안에서 claude --debug-file ./otel-debug.log를 실행합니다. 모니터링 안내에 따라 [3P telemetry]가 붙은 내보내기 오류를 살펴봅니다.
대화 기록, /usage, 청구 내역이 다른 이유
대화 기록에서 요청이 빠지면 비용의 출처를 알기 어려워도 세션 전체 합계는 정확할 수 있습니다. 이슈 #104358에서 rabitlord1324는 백그라운드 요청 기록이 빠진다고 보고하면서 Claude Code의 내부 계산은 정확하다고 설명합니다.
현재 사용량 문서는 세션 합계와 모델별 사용량을 설명합니다. 여기서 필요한 query_source별 내역은 제공하지 않습니다. 대화 기록 분석기가 요약 요청을 놓쳤다는 이유만으로 모든 요약이 /usage에서 제외된다고 판단하지 않습니다.
cost_usd는 여전히 추정치입니다. Claude Code는 보통 정가를 사용하지만, 관리 설정의 modelPricing 표에 따라 보고용 단가가 달라질 수 있습니다. 구독 한도 소비량과 API 청구 금액은 서로 다른 수치입니다. 종량제 비용은 제공업체의 실제 사용 내역과 대조합니다.
BetterToken 사용자의 경우 문서에 명시된 Claude Code 기본 URL은 https://bettertoken.ai입니다. 2026-11-01 기준으로 카탈로그에는 claude-opus-5-5가 등록되어 있습니다. 측정 구간을 기준으로 콘솔의 모델, 토큰 사용량, 청구 내역과 비교합니다.
유용한 작업을 유지하면서 비용 줄이기
측정한 부가 비용이 중요하다면 작업 범위를 좁히거나 백그라운드 에이전트 수를 줄이는 것부터 시작합니다. 2026-11-01 기준으로 환경 변수 및 서브에이전트 참고 문서에는 요약만 끄는 설정이 문서화되어 있지 않습니다.
| 방법 | 바뀌는 동작 | 고려할 점 |
|---|---|---|
| 백그라운드 작업 수나 작업 시간 줄이기 | 대화 기록을 반복 요약하는 기회를 줄입니다 | 유용한 병렬 처리가 줄어들 수 있으므로 완료 시간을 비교합니다 |
| 범위가 제한된 서브에이전트에 더 작은 모델 사용 | 위임받은 작업을 수행하는 모델이 바뀝니다 | 결과 품질과 텔레메트리에 실제로 기록된 모델을 확인합니다 |
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 claude | 해당 도구를 통한 백그라운드 실행을 폭넓게 비활성화합니다 | Claude는 서브에이전트가 끝날 때까지 기다린 후 계속합니다 |
CLAUDE_CODE_FORK_SUBAGENT=0 claude | Claude가 자율적으로 대화 분기를 만드는 동작을 끕니다 | 백그라운드 서브에이전트는 계속 실행할 수 있으며 요약 중단은 보장되지 않습니다 |
| 커뮤니티의 Bun preload 패치 | 요약 타이머를 가로채려고 시도합니다 | Claude Code 안에서 외부 코드를 실행하며 업데이트 후 작동하지 않을 수 있습니다 |
백그라운드 작업 제어 변수는 run_in_background를 제거하고 해당 도구의 자동 백그라운드 전환을 막습니다. MCP 작업, Monitor 도구, 별도의 백그라운드 세션에는 예외나 별도 제어 설정이 있습니다. 모든 백그라운드 활동을 끄는 설정은 아닙니다.
대화 분기를 제어하는 fork 모드 설정은 문서화되어 있습니다. 그러나 이 설정의 부수 효과로 요약까지 중단된다는 보장은 없습니다. 2.1.312에서 chrisvaillancourt는 macOS에서 요약이 중단되었다고 보고했습니다. 반면 dblumenau는 Linux에서 요청이 계속 발생하는 것을 관찰했습니다. 두 보고는 설정과 실행 모드도 다릅니다.
제어 변수는 새 세션을 시작하기 전에 설정합니다. ~/.claude/settings.json과 프로젝트 설정에서 충돌하는 env 항목을 확인합니다. 이 값이 셸에서 상속받은 값을 덮어쓸 수 있습니다.
Flamerix preload는 커뮤니티의 실험적 우회 방법으로 취급합니다. 사용을 고려하기 전에 특정 리비전으로 고정한 코드를 검토합니다. 클라이언트를 업데이트할 때마다 요청 수를 확인합니다. 표시 문구가 멈췄다는 사실만으로 요청까지 중단됐다고 볼 수는 없습니다.
범위가 제한된 조사에는 읽기 전용 서브에이전트의 모델 검증 안내를 참고합니다. 큰 도구 정의가 컨텍스트를 늘린다면 MCP 컨텍스트 비용 비교로 도구 수를 줄이는 효과를 확인합니다.
한 번에 변수 하나만 바꿉니다. 같은 작업, 모델, 컨텍스트 조건에서 요청 출처별 비용과 함께 결과의 수용 가능 여부, 경과 시간을 비교합니다. 유용한 동시 실행을 잃는 비용이 측정한 절감액보다 크다면 백그라운드 작업을 다시 사용합니다.